diff --git a/ipi-survey-platform/public/assets/images/TawasolLogo.png b/ipi-survey-platform/public/assets/images/TawasolLogo.png
new file mode 100644
index 0000000..717a9f2
Binary files /dev/null and b/ipi-survey-platform/public/assets/images/TawasolLogo.png differ
diff --git a/ipi-survey-platform/src/components/admin/SubmissionTable.jsx b/ipi-survey-platform/src/components/admin/SubmissionTable.jsx
index eccc512..5e44fd7 100644
--- a/ipi-survey-platform/src/components/admin/SubmissionTable.jsx
+++ b/ipi-survey-platform/src/components/admin/SubmissionTable.jsx
@@ -2,7 +2,7 @@ import React, { useState, useEffect, useMemo } from 'react';
import Table from '@/components/common/Table';
import apiClient from '../../services/api/apiClient.js';
import { CheckCircle2, XCircle, Eye } from 'lucide-react';
-
+import Footer from '../common/Footer.jsx';
const SubmissionTable = ({ selectedQuarter, selectedYear }) => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
@@ -144,6 +144,7 @@ const SubmissionTable = ({ selectedQuarter, selectedYear }) => {
const columnWidths = [240, 120, 100, 100, 130, 150, 180, 150, 150, 180, 120];
return (
+ <>
{
+
+ >
);
};
diff --git a/ipi-survey-platform/src/components/common/Footer.jsx b/ipi-survey-platform/src/components/common/Footer.jsx
new file mode 100644
index 0000000..85d0598
--- /dev/null
+++ b/ipi-survey-platform/src/components/common/Footer.jsx
@@ -0,0 +1,71 @@
+import React from 'react';
+import { Phone, Smartphone, Headphones, Instagram, Facebook, Linkedin, Youtube } from 'lucide-react';
+
+const Footer = () => {
+ return (
+
+ );
+};
+
+export default Footer;
\ No newline at end of file
diff --git a/ipi-survey-platform/src/components/dashboard/SubmissionHistory.jsx b/ipi-survey-platform/src/components/dashboard/SubmissionHistory.jsx
index f759eb0..9592e4a 100644
--- a/ipi-survey-platform/src/components/dashboard/SubmissionHistory.jsx
+++ b/ipi-survey-platform/src/components/dashboard/SubmissionHistory.jsx
@@ -1,53 +1,50 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import Table from '@/components/common/Table';
+import Footer from '../common/Footer';
const searchIconSrc = '/assets/images/material-symbols_search-rounded.svg';
-const caretDownSrc = '/assets/images/CaretDown.svg';
+
+const formatDateTime = (value) => {
+ if (!value) return '—';
+ const date = new Date(value);
+ if (Number.isNaN(date.getTime())) return '—';
+ const formatted = date.toLocaleString('en-GB', {
+ day: '2-digit',
+ month: '2-digit',
+ year: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit',
+ hour12: true,
+ });
+ return formatted.replace(/\s?(am|pm)$/i, (match) => match.toUpperCase());
+};
const getStatusClass = (status) => {
const normalized = String(status || '').toLowerCase();
if (normalized === 'approved') {
return 'inline-flex items-center rounded-md bg-green-100 px-2 py-1 text-xs font-medium text-green-800';
}
- if (normalized === 'pending' || normalized === 'under review' || normalized === 'in-progress') {
+ if (['pending', 'under review', 'in-progress'].includes(normalized)) {
return 'inline-flex items-center rounded-md bg-yellow-100 px-2 py-1 text-xs font-medium text-yellow-800';
}
- if (normalized === 'rejected' || normalized === 'returned') {
+ if (['rejected', 'returned'].includes(normalized)) {
return 'inline-flex items-center rounded-md bg-red-100 px-2 py-1 text-xs font-medium text-red-800';
}
return 'inline-flex items-center rounded-md bg-gray-100 px-2 py-1 text-xs font-medium text-gray-800';
};
-const formatQuarter = (quarter, year) => {
- if (!quarter && !year) return '—';
- return [quarter || '', year || ''].filter(Boolean).join(' ');
-};
-
-const formatDate = (value) => {
- if (!value) return '—';
- const date = new Date(value);
- if (Number.isNaN(date.getTime())) return '—';
- return date.toLocaleDateString('en-GB');
-};
-
-const statusOptions = [
- { label: 'All Status', value: 'all' },
- { label: 'Approved', value: 'approved' },
- { label: 'Under Review', value: 'under review' },
- { label: 'Pending', value: 'pending' },
- { label: 'Returned', value: 'returned' },
- { label: 'Rejected', value: 'rejected' },
-];
-
-const normalizeStatus = (status) => String(status || '').toLowerCase();
-
const downloadCsv = (rows) => {
if (!rows.length) return;
- const headers = ['Quarter', 'Status', 'Submission Date', 'Products'];
+ const headers = ['Survey Name', 'Year', 'Quarter', 'Status', 'Submission On', 'Products'];
const csvRows = [headers.join(',')];
rows.forEach((row) => {
- csvRows.push(row.slice(0, 4).map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(','));
+ csvRows.push(
+ row
+ .slice(0, 6)
+ .map((cell) => `"${String(cell).replace(/"/g, '""')}"`)
+ .join(',')
+ );
});
const blob = new Blob([csvRows.join('\n')], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
@@ -62,57 +59,72 @@ const downloadCsv = (rows) => {
export const SubmissionHistory = ({ history = [], loading = false, error = '' }) => {
const [searchTerm, setSearchTerm] = React.useState('');
- const [statusFilter, setStatusFilter] = React.useState('all');
const navigate = useNavigate();
const normalizedHistory = React.useMemo(() => {
if (!Array.isArray(history)) return [];
return history.map((entry, index) => ({
id: entry?.id ?? index,
- quarter: formatQuarter(entry?.quarter, entry?.year),
+ survey_name: '—',
+ year: entry?.year || '—',
+ quarter: entry?.quarter || '—',
status: entry?.status || 'Pending',
- date: formatDate(entry?.created_at || entry?.submitted_on),
- products: entry?.product_count,
+ submission_on: formatDateTime(entry?.created_at || entry?.submitted_on),
+ products: entry?.product_count ?? 0,
}));
}, [history]);
const filteredRows = normalizedHistory.filter((entry) => {
const term = searchTerm.trim().toLowerCase();
- const matchesSearch =
+ return (
!term ||
+ entry.year.toString().includes(term) ||
entry.quarter.toLowerCase().includes(term) ||
- entry.status.toLowerCase().includes(term) ||
- entry.date.toLowerCase().includes(term);
- const matchesStatus =
- statusFilter === 'all' || normalizeStatus(entry.status) === statusFilter;
- return matchesSearch && matchesStatus;
- });
- const rowsData = filteredRows.map((s) => {
- const count = Number.isFinite(Number(s.products)) ? Number(s.products) : null;
- const display = count === null ? '—' : `${count} ${count === 1 ? 'product' : 'products'}`;
- return [s.quarter, s.status, s.date, display, 'View'];
+ entry.status.toLowerCase().includes(term)
+ );
});
- const handleViewSubmission = React.useCallback(() => {
- navigate('/history');
- }, [navigate]);
+ const navigateToView = (id) => navigate(`/history/${id}`);
+
+ const rowsData = filteredRows.map((item) => {
+ const productCount = Number(item.products) || 0;
+ const productLabel = `${productCount} ${productCount === 1 ? 'product' : 'products'}`;
+
+ return [
+ item.survey_name,
+ item.year,
+ item.quarter,
+ {item.status},
+ {item.submission_on},
+ productLabel,
+ ,
+ ];
+ });
const toolbar = (
-
Submission History
+
+ Submission History
+
{loading && Loading…}
{error && {error}}
+
-
-
-

-
+
@@ -161,32 +165,28 @@ export const SubmissionHistory = ({ history = [], loading = false, error = '' })
);
return (
-
- {
- if (ci === 1) {
- return {value};
- }
- if (ci === 4) {
- const disabled = !normalizedHistory[ri];
- return (
-
- );
- }
- return value;
- }}
- />
-
+ <>
+
+
+
+ >
);
};
+
+export default SubmissionHistory;
\ No newline at end of file
diff --git a/ipi-survey-platform/src/components/dashboard/SurveyCarousel.jsx b/ipi-survey-platform/src/components/dashboard/SurveyCarousel.jsx
new file mode 100644
index 0000000..9d4742e
--- /dev/null
+++ b/ipi-survey-platform/src/components/dashboard/SurveyCarousel.jsx
@@ -0,0 +1,94 @@
+import React, { useState, useEffect } from 'react';
+import { ChevronLeft, ChevronRight, Pause } from 'lucide-react';
+
+const SurveyCarousel = ({
+ surveys = [],
+ autoRotate = true,
+ rotateInterval = 5000,
+ onStartSurvey,
+}) => {
+ const [currentIndex, setCurrentIndex] = useState(0);
+ const [isPaused, setIsPaused] = useState(false);
+
+ useEffect(() => {
+ if (!autoRotate || isPaused) return;
+ const timer = setInterval(() => {
+ setCurrentIndex((prev) => (prev + 1) % surveys.length);
+ }, rotateInterval);
+ return () => clearInterval(timer);
+ }, [autoRotate, isPaused, rotateInterval, surveys.length]);
+
+ if (!surveys.length) return null;
+ const current = surveys[currentIndex];
+
+ const handlePrev = () =>
+ setCurrentIndex((prev) => (prev === 0 ? surveys.length - 1 : prev - 1));
+
+ const handleNext = () =>
+ setCurrentIndex((prev) => (prev === surveys.length - 1 ? 0 : prev + 1));
+
+ return (
+
+
+
+
+ {current?.title || '—'}
+
+
+ {current?.subtitle || ''}
+
+
+ Due Date: {current?.dueDate || '—'}
+ Est. time: {current?.estTime || '—'}
+ Status: {current?.status || '—'}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {surveys.map((_, idx) => (
+ setCurrentIndex(idx)}
+ className={`h-[8px] w-[8px] rounded-full cursor-pointer transition ${
+ idx === currentIndex ? 'bg-[#92722A]' : 'bg-gray-300'
+ }`}
+ />
+ ))}
+
+
+
+
+ );
+};
+
+export default SurveyCarousel;
\ No newline at end of file
diff --git a/ipi-survey-platform/src/components/dashboard/SurveyStatus.jsx b/ipi-survey-platform/src/components/dashboard/SurveyStatus.jsx
index 2b79a09..93b62ba 100644
--- a/ipi-survey-platform/src/components/dashboard/SurveyStatus.jsx
+++ b/ipi-survey-platform/src/components/dashboard/SurveyStatus.jsx
@@ -1,5 +1,6 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
+import SurveyCarousel from './SurveyCarousel';
const formatQuarterYear = (quarter, year) => {
if (!quarter || !year) return '—';
@@ -25,18 +26,37 @@ const formatRelativeDays = (value) => {
if (diffDays === 0) return 'Due today';
return `${diffDays} day${diffDays === 1 ? '' : 's'}`;
};
-
+const surveyList = [
+ {
+ title: 'Q4 2024 Survey Ready',
+ subtitle: 'Complete your quarterly industrial production data submission',
+ dueDate: '31.01.2025',
+ estTime: '15–20 minutes',
+ status: 'Pending',
+ },
+ {
+ title: 'Q1 2025 Survey Ready',
+ subtitle: 'Begin your new quarter production data collection',
+ dueDate: '30.04.2025',
+ estTime: '20 minutes',
+ status: 'Pending',
+ },
+ {
+ title: 'Q2 2025 Survey Ready',
+ subtitle: 'Review and confirm your industrial production figures',
+ dueDate: '31.07.2025',
+ estTime: '25 minutes',
+ status: 'Pending',
+ },
+];
export const SurveyStatus = ({ data = null, loading = false, error = '' }) => {
+
const navigate = useNavigate();
- const handleStartSurvey = () => {
- navigate('/survey', {
- state: {
- currentQuarter: data?.current_quarter,
- currentYear: data?.current_quarter_year
- }
- });
+ const handleStartSurvey = (survey) => {
+ console.log('Starting survey:', survey.title);
};
+
const submissionStatus = data?.submission_status || '—';
const currentQuarter = formatQuarterYear(data?.current_quarter, data?.current_quarter_year);
const nextDeadlineDate = formatDate(data?.next_deadline);
@@ -60,67 +80,15 @@ export const SurveyStatus = ({ data = null, loading = false, error = '' }) => {
return (
-
-
-
-
-
{heroTitle}
-
{heroSubtitle}
-
- Due: {nextDeadlineDate}
- {nextDeadlineRelative}
-
-
-
-
-
-
+
+
+
-
-
-
-
Submission Status
-
{loading ? 'Loading…' : submissionStatus}
-
{currentQuarter !== '—' ? `${currentQuarter} submission required` : 'Awaiting reporting period data.'}
-
-
-
-
Next Deadline
-
{loading ? 'Loading…' : nextDeadlineRelative}
-
{`Due on ${nextDeadlineDate}`}
-
-
-
-
Last Submission
-
{loading ? 'Loading…' : lastSubmissionQuarter}
-
{`Submitted ${lastSubmissionDate}`}
-
-
-
-
-
-
Submission Status
-
{loading ? 'Loading…' : submissionStatus}
-
{currentQuarter !== '—' ? `${currentQuarter} submission required` : 'Awaiting reporting period data.'}
-
-
-
Next Deadline
-
{loading ? 'Loading…' : nextDeadlineRelative}
-
{`Due on ${nextDeadlineDate}`}
-
-
-
Last Submission
-
{loading ? 'Loading…' : lastSubmissionQuarter}
-
{`Submitted ${lastSubmissionDate}`}
-
-
-
);
-};
+};
\ No newline at end of file
diff --git a/ipi-survey-platform/src/components/dashboard/WelcomeSection.jsx b/ipi-survey-platform/src/components/dashboard/WelcomeSection.jsx
index fc246d9..37fe7ed 100644
--- a/ipi-survey-platform/src/components/dashboard/WelcomeSection.jsx
+++ b/ipi-survey-platform/src/components/dashboard/WelcomeSection.jsx
@@ -29,7 +29,18 @@ export const WelcomeSection = ({ data = null, loading = false, error = '' }) =>
-
Welcome Back
+
+ Welcome{' '}
+ {(() => {
+ try {
+ const userProfile = JSON.parse(sessionStorage.getItem('user_profile'));
+ return userProfile?.name ? userProfile.name : '';
+ } catch (error) {
+ console.error('Error reading user_profile from sessionStorage:', error);
+ return '';
+ }
+ })()} !
+
{description}
diff --git a/ipi-survey-platform/src/components/layout/HeaderBar.jsx b/ipi-survey-platform/src/components/layout/HeaderBar.jsx
index 94bc91d..9c3ed40 100644
--- a/ipi-survey-platform/src/components/layout/HeaderBar.jsx
+++ b/ipi-survey-platform/src/components/layout/HeaderBar.jsx
@@ -1,6 +1,6 @@
import React from 'react';
import { NavLink, useNavigate } from 'react-router-dom';
-
+import { FileSearch } from 'lucide-react';
const logoSrc = '/assets/images/FCSCLogo.svg';
const surveyIconActiveSrc = '/assets/images/Vector.svg';
const surveyIconInactiveSrc = '/assets/images/Vectorblack.svg';
@@ -88,6 +88,29 @@ const HeaderBar = () => {
>
)}
+
+
+ `inline-flex items-center gap-2 pb-1 border-b-2 ${
+ isActive
+ ? 'text-[#92722A] font-medium border-[#92722A]'
+ : 'text-[#232528] hover:text-gray-900 border-transparent'
+ }`
+ }
+>
+ {({ isActive }) => (
+ <>
+
+ Overview
+ >
+ )}
+
+
`inline-flex items-center gap-2 pb-1 border-b-2 ${isActive ? 'text-[#92722A] font-medium border-[#92722A]' : 'text-[#232528] hover:text-gray-900 border-transparent'}`}