establishment dashboard updated design completed & footer added to both dashboard
This commit is contained in:
parent
e979c7a488
commit
c9fbac1f6f
BIN
ipi-survey-platform/public/assets/images/TawasolLogo.png
Normal file
BIN
ipi-survey-platform/public/assets/images/TawasolLogo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 83 KiB |
@ -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 (
|
||||
<>
|
||||
<div
|
||||
className="border border-[#E5E7EB] bg-white shadow-[0_16px_32px_rgba(15,23,42,0.06)] rounded-[8px]"
|
||||
style={{ width: '1250px', minHeight: '780px' }}
|
||||
@ -181,6 +182,8 @@ const SubmissionTable = ({ selectedQuarter, selectedYear }) => {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
71
ipi-survey-platform/src/components/common/Footer.jsx
Normal file
71
ipi-survey-platform/src/components/common/Footer.jsx
Normal file
@ -0,0 +1,71 @@
|
||||
import React from 'react';
|
||||
import { Phone, Smartphone, Headphones, Instagram, Facebook, Linkedin, Youtube } from 'lucide-react';
|
||||
|
||||
const Footer = () => {
|
||||
return (
|
||||
<footer className="bg-white border-t border-gray-200 mt-10">
|
||||
<div className="h-[3px] bg-[#92722A]" />
|
||||
|
||||
<div className="max-w-7xl mx-auto px-6 py-10 grid grid-cols-1 md:grid-cols-4 gap-8 text-[#232528]">
|
||||
<div>
|
||||
<h3 className="text-[#92722A] font-semibold mb-2">The Ministry</h3>
|
||||
<ul className="space-y-1 text-sm">
|
||||
<li><a href="#" className="hover:underline">About us</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-[#92722A] font-semibold mb-2">Using the website</h3>
|
||||
<ul className="space-y-1 text-sm">
|
||||
<li><a href="#" className="hover:underline">Disclaimer</a></li>
|
||||
<li><a href="#" className="hover:underline">Privacy policy</a></li>
|
||||
<li><a href="#" className="hover:underline">Terms and conditions</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-[#92722A] font-semibold mb-2">Information and support</h3>
|
||||
<ul className="space-y-1 text-sm">
|
||||
<li><a href="#" className="hover:underline">Contact us</a></li>
|
||||
<li><a href="#" className="hover:underline">FAQ’s</a></li>
|
||||
<li><a href="#" className="hover:underline">Feedback and complaints</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-start md:items-end gap-2">
|
||||
<img src="/assets/images/TawasolLogo.png" alt="Tawasul" className="h-12 mb-2" />
|
||||
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<Phone size={16} className="text-green-600" />
|
||||
<span className="font-semibold">171</span>
|
||||
<Smartphone size={16} className="text-green-600 ml-3" />
|
||||
<span className="font-semibold">04-7771777</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 text-sm mt-1">
|
||||
<Headphones size={16} className="text-gray-500" />
|
||||
<span className="text-gray-700">Toll free: <span className="font-semibold">800 12</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-gray-200 my-2" />
|
||||
|
||||
<div className="max-w-7xl mx-auto px-6 py-4 flex flex-col md:flex-row justify-between items-center text-xs text-gray-600">
|
||||
<p>
|
||||
© 2023. Ministry of Human Resources & Emiratisation. All rights reserved.{' '}
|
||||
<span className="text-gray-400">Last updated on 24/04/2023 at 15:45</span>
|
||||
</p>
|
||||
<div className="flex items-center gap-3 mt-3 md:mt-0">
|
||||
<span>Follow us on:</span>
|
||||
<a href="#" className="hover:text-[#92722A]"><Facebook size={16} /></a>
|
||||
<a href="#" className="hover:text-[#92722A]"><Instagram size={16} /></a>
|
||||
<a href="#" className="hover:text-[#92722A]"><Linkedin size={16} /></a>
|
||||
<a href="#" className="hover:text-[#92722A]"><Youtube size={16} /></a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
@ -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,
|
||||
<span className={getStatusClass(item.status)}>{item.status}</span>,
|
||||
<span className="whitespace-nowrap">{item.submission_on}</span>,
|
||||
productLabel,
|
||||
<button
|
||||
onClick={() => navigateToView(item.id)}
|
||||
className="text-[#9E792B] hover:text-[#7b5f22] font-medium"
|
||||
>
|
||||
View Details
|
||||
</button>,
|
||||
];
|
||||
});
|
||||
|
||||
const toolbar = (
|
||||
<div className="px-4 pt-6 pb-2 flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
|
||||
<div className="flex flex-col gap-1">
|
||||
<h3 className="text-[18px] leading-[28px] font-medium text-[#232528]">Submission History</h3>
|
||||
<h3 className="text-[18px] leading-[28px] font-medium text-[#232528]">
|
||||
Submission History
|
||||
</h3>
|
||||
<div className="flex gap-3 text-sm text-gray-500">
|
||||
{loading && <span>Loading…</span>}
|
||||
{error && <span className="text-red-600">{error}</span>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col md:flex-row gap-3 md:items-center">
|
||||
<div className="relative w-full md:w-56">
|
||||
<input
|
||||
type="text"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
placeholder="Search submissions"
|
||||
placeholder="Search Establishment"
|
||||
className="w-full h-10 rounded-md border border-[#E5E7EB] pl-10 pr-3 text-sm focus:outline-none"
|
||||
/>
|
||||
<img
|
||||
@ -121,29 +133,16 @@ export const SubmissionHistory = ({ history = [], loading = false, error = '' })
|
||||
className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5"
|
||||
/>
|
||||
</div>
|
||||
<div className="relative w-full md:w-40">
|
||||
<select
|
||||
value={statusFilter}
|
||||
onChange={(e) => setStatusFilter(e.target.value)}
|
||||
className="h-10 w-full appearance-none rounded-md border border-[#E5E7EB] bg-white pl-3 pr-9 text-sm text-[#232528] focus:outline-none cursor-pointer"
|
||||
>
|
||||
{statusOptions.map((option) => (
|
||||
<option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<img
|
||||
src={caretDownSrc}
|
||||
alt="Open"
|
||||
className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => downloadCsv(rowsData)}
|
||||
disabled={!rowsData.length}
|
||||
className={`inline-flex h-10 items-center gap-2 rounded-md border px-4 text-sm font-medium ${rowsData.length ? 'border-[#92722A] text-[#92722A] hover:bg-[#F2ECCF]' : 'border-gray-200 text-gray-400 cursor-not-allowed'}`}
|
||||
className={`inline-flex h-10 items-center gap-2 rounded-md border px-4 text-sm font-medium ${
|
||||
rowsData.length
|
||||
? 'border-[#92722A] text-[#92722A] hover:bg-[#F2ECCF]'
|
||||
: 'border-gray-200 text-gray-400 cursor-not-allowed'
|
||||
}`}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@ -152,7 +151,12 @@ export const SubmissionHistory = ({ history = [], loading = false, error = '' })
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 16v2a2 2 0 002 2h12a2 2 0 002-2v-2M7 10l5 5m0 0l5-5m-5 5V4" />
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M4 16v2a2 2 0 002 2h12a2 2 0 002-2v-2M7 10l5 5m0 0l5-5m-5 5V4"
|
||||
/>
|
||||
</svg>
|
||||
<span>Export CSV</span>
|
||||
</button>
|
||||
@ -161,32 +165,28 @@ export const SubmissionHistory = ({ history = [], loading = false, error = '' })
|
||||
);
|
||||
|
||||
return (
|
||||
<section className="max-w-[1280px] mx-auto px-0">
|
||||
<Table
|
||||
headers={['Quarter', 'Status', 'Submission Date', 'Products', 'Actions']}
|
||||
rows={rowsData}
|
||||
beforeHeader={toolbar}
|
||||
separated
|
||||
rowGapClass="border-spacing-y-2"
|
||||
renderCell={(value, ri, ci) => {
|
||||
if (ci === 1) {
|
||||
return <span className={getStatusClass(String(value))}>{value}</span>;
|
||||
}
|
||||
if (ci === 4) {
|
||||
const disabled = !normalizedHistory[ri];
|
||||
return (
|
||||
<button
|
||||
onClick={handleViewSubmission}
|
||||
className={`text-amber-700 hover:text-amber-800 ${disabled ? 'cursor-not-allowed opacity-50 hover:text-amber-700' : ''}`}
|
||||
disabled={disabled}
|
||||
>
|
||||
View
|
||||
</button>
|
||||
);
|
||||
}
|
||||
return value;
|
||||
}}
|
||||
/>
|
||||
</section>
|
||||
<>
|
||||
<section className="max-w-[1280px] mx-auto px-0">
|
||||
<Table
|
||||
headers={[
|
||||
'Survey Name',
|
||||
'Year',
|
||||
'Quarter',
|
||||
'Status',
|
||||
'Submission On',
|
||||
'Products',
|
||||
'Actions',
|
||||
]}
|
||||
rows={rowsData}
|
||||
beforeHeader={toolbar}
|
||||
separated
|
||||
rowGapClass="border-spacing-y-2"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SubmissionHistory;
|
||||
@ -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 (
|
||||
<div className="bg-white rounded-lg shadow-sm ring-1 ring-gray-200 mb-8 overflow-hidden">
|
||||
<div className="rounded-none bg-[#F2ECCF] px-6 py-4 min-h-[148px] flex items-center justify-between transition-all duration-500">
|
||||
<div className="flex-1">
|
||||
<h3 className="text-[18px] leading-[28px] font-medium text-[#232528]">
|
||||
{current?.title || '—'}
|
||||
</h3>
|
||||
<p className="mt-1 text-[16px] leading-[24px] font-medium text-[#232528]">
|
||||
{current?.subtitle || ''}
|
||||
</p>
|
||||
<div className="mt-2 flex items-center gap-6 text-[12px] leading-[16px] font-medium text-[#232528]">
|
||||
<span>Due Date: {current?.dueDate || '—'}</span>
|
||||
<span>Est. time: {current?.estTime || '—'}</span>
|
||||
<span>Status: {current?.status || '—'}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => onStartSurvey?.(current)}
|
||||
className="inline-flex items-center justify-center w-[196px] h-[52px] px-7 py-3 text-[18px] leading-[28px] font-medium rounded-lg text-white bg-[#92722A] hover:bg-[#7b5c1f] cursor-pointer whitespace-nowrap"
|
||||
>
|
||||
Start Survey Now
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between px-6 py-3 bg-[#F2ECCF]">
|
||||
<div className="flex items-center gap-3">
|
||||
|
||||
<button
|
||||
onClick={handlePrev}
|
||||
className="p-1 rounded-full hover:bg-gray-100 transition"
|
||||
>
|
||||
<ChevronLeft size={20} className="text-gray-700" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setIsPaused(!isPaused)}
|
||||
className="w-6 h-6 flex items-center justify-center rounded-full border border-black bg-transparent hover:bg-gray-100 transition"
|
||||
>
|
||||
<Pause size={12} className="text-black" />
|
||||
</button>
|
||||
<button
|
||||
onClick={handleNext}
|
||||
className="p-1 rounded-full hover:bg-gray-100 transition"
|
||||
>
|
||||
<ChevronRight size={20} className="text-gray-700" />
|
||||
</button>
|
||||
|
||||
<div className="flex items-center gap-2 ml-2">
|
||||
{surveys.map((_, idx) => (
|
||||
<span
|
||||
key={idx}
|
||||
onClick={() => setCurrentIndex(idx)}
|
||||
className={`h-[8px] w-[8px] rounded-full cursor-pointer transition ${
|
||||
idx === currentIndex ? 'bg-[#92722A]' : 'bg-gray-300'
|
||||
}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SurveyCarousel;
|
||||
@ -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 (
|
||||
<section className="max-w-[1280px] mx-auto px-0">
|
||||
<div className="bg-white rounded-lg shadow-sm ring-1 ring-gray-200 mb-8 overflow-hidden">
|
||||
<div className="p-0">
|
||||
<div className="rounded-none bg-[#F2ECCF] px-6 py-4 min-h-[148px] flex items-center justify-between">
|
||||
<div className="flex-1">
|
||||
<h3 className={`text-[18px] leading-[28px] font-medium ${error ? 'text-red-700' : 'text-[#232528]'}`}>{heroTitle}</h3>
|
||||
<p className="mt-1 text-[16px] leading-[24px] font-medium text-[#232528]">{heroSubtitle}</p>
|
||||
<div className="mt-2 flex items-center gap-6 text-[12px] leading-[16px] font-medium text-[#232528]">
|
||||
<span>Due: {nextDeadlineDate}</span>
|
||||
<span>{nextDeadlineRelative}</span>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleStartSurvey}
|
||||
className="inline-flex items-center justify-center w-[196px] h-[52px] px-7 py-3 text-[18px] leading-[28px] font-medium rounded-lg text-white bg-[#92722A] hover:bg-[#7b5c1f] cursor-pointer whitespace-nowrap"
|
||||
disabled={loading}
|
||||
>
|
||||
Start Survey Now
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-6">
|
||||
<SurveyCarousel
|
||||
surveys={surveyList}
|
||||
autoRotate
|
||||
rotateInterval={7000}
|
||||
onStartSurvey={handleStartSurvey}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-8">
|
||||
<div className="hidden sm:flex gap-5">
|
||||
<div className="w-[413px] h-[134px] bg-white rounded-[8px] ring-1 ring-gray-200 px-6 py-5">
|
||||
<p className="text-[16px] leading-[24px] font-medium text-[#232528]">Submission Status</p>
|
||||
<p className="mt-2 text-[18px] leading-[28px] font-medium text-[#232528]">{loading ? 'Loading…' : submissionStatus}</p>
|
||||
<p className="mt-2 text-[12px] leading-[16px] font-medium text-[#5F646D]">{currentQuarter !== '—' ? `${currentQuarter} submission required` : 'Awaiting reporting period data.'}</p>
|
||||
</div>
|
||||
|
||||
<div className="w-[413px] h-[134px] bg-white rounded-[8px] ring-1 ring-gray-200 px-6 py-5">
|
||||
<p className="text-[16px] leading-[24px] font-medium text-[#232528]">Next Deadline</p>
|
||||
<p className="mt-2 text-[18px] leading-[28px] font-medium text-[#232528]">{loading ? 'Loading…' : nextDeadlineRelative}</p>
|
||||
<p className="mt-2 text-[12px] leading-[16px] font-medium text-[#5F646D]">{`Due on ${nextDeadlineDate}`}</p>
|
||||
</div>
|
||||
|
||||
<div className="w-[413px] h-[134px] bg-white rounded-[8px] ring-1 ring-gray-200 px-6 py-5">
|
||||
<p className="text-[16px] leading-[24px] font-medium text-[#232528]">Last Submission</p>
|
||||
<p className="mt-2 text-[18px] leading-[28px] font-medium text-[#232528]">{loading ? 'Loading…' : lastSubmissionQuarter}</p>
|
||||
<p className="mt-2 text-[12px] leading-[16px] font-medium text-[#5F646D]">{`Submitted ${lastSubmissionDate}`}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="sm:hidden space-y-4">
|
||||
<div className="bg-white rounded-[8px] ring-1 ring-gray-200 px-6 py-5">
|
||||
<p className="text-[16px] leading-[24px] font-medium text-[#232528]">Submission Status</p>
|
||||
<p className="mt-2 text-[18px] leading-[28px] font-medium text-[#232528]">{loading ? 'Loading…' : submissionStatus}</p>
|
||||
<p className="mt-2 text-[12px] leading-[16px] font-medium text-[#5F646D]">{currentQuarter !== '—' ? `${currentQuarter} submission required` : 'Awaiting reporting period data.'}</p>
|
||||
</div>
|
||||
<div className="bg-white rounded-[8px] ring-1 ring-gray-200 px-6 py-5">
|
||||
<p className="text-[16px] leading-[24px] font-medium text-[#232528]">Next Deadline</p>
|
||||
<p className="mt-2 text-[18px] leading-[28px] font-medium text-[#232528]">{loading ? 'Loading…' : nextDeadlineRelative}</p>
|
||||
<p className="mt-2 text-[12px] leading-[16px] font-medium text-[#5F646D]">{`Due on ${nextDeadlineDate}`}</p>
|
||||
</div>
|
||||
<div className="bg-white rounded-[8px] ring-1 ring-gray-200 px-6 py-5">
|
||||
<p className="text-[16px] leading-[24px] font-medium text-[#232528]">Last Submission</p>
|
||||
<p className="mt-2 text-[18px] leading-[28px] font-medium text-[#232528]">{loading ? 'Loading…' : lastSubmissionQuarter}</p>
|
||||
<p className="mt-2 text-[12px] leading-[16px] font-medium text-[#5F646D]">{`Submitted ${lastSubmissionDate}`}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
};
|
||||
@ -29,7 +29,18 @@ export const WelcomeSection = ({ data = null, loading = false, error = '' }) =>
|
||||
|
||||
<div className="max-w-[1280px] mx-auto px-4 pt-4 pb-2">
|
||||
<div className="space-y-1">
|
||||
<h2 className="w-[360px] text-[18px] leading-[28px] font-medium text-[#232528]">Welcome Back</h2>
|
||||
<h2 className="w-[360px] text-[18px] leading-[28px] font-medium text-[#232528]">
|
||||
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 '';
|
||||
}
|
||||
})()} !
|
||||
</h2>
|
||||
<p className={`w-[360px] text-[14px] leading-[24px] font-normal ${error ? 'text-red-600' : 'text-[#5F646D]'}`}>{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -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 = () => {
|
||||
</>
|
||||
)}
|
||||
</NavLink>
|
||||
|
||||
<NavLink
|
||||
to="/survey"
|
||||
className={({ isActive }) =>
|
||||
`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 }) => (
|
||||
<>
|
||||
<FileSearch
|
||||
className={`h-[18px] w-[18px] ${
|
||||
isActive ? 'text-[#92722A]' : 'text-[#232528]'
|
||||
}`}
|
||||
/>
|
||||
<span>Overview</span>
|
||||
</>
|
||||
)}
|
||||
</NavLink>
|
||||
|
||||
<NavLink
|
||||
to="/history"
|
||||
className={({ isActive }) => `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'}`}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user