manage submission - using api fetched listing data
This commit is contained in:
parent
dcc05fc762
commit
eefb85bbde
@ -4,180 +4,9 @@ import AdminHeader from '../../components/admin/AdminHeader';
|
||||
import Table from '../../components/common/Table';
|
||||
import SearchIcon from '../../assets/images/material-symbols_search-rounded.svg';
|
||||
import DownloadIcon from '../../assets/images/DownloadSimple.svg';
|
||||
import ActionIcon from '../../assets/images/qlementine-icons_preview-16.svg';
|
||||
import CaretUp from '../../assets/images/caret-up.svg';
|
||||
import { TextField, SelectField } from '../../components/common/FormControls';
|
||||
|
||||
const submissions = [
|
||||
{
|
||||
id: 1,
|
||||
establishment: 'CASCADE MARINE FOODS LLC',
|
||||
emirate: 'Sharjah',
|
||||
year: '2025',
|
||||
quarter: 'Q4',
|
||||
submittedAt: '18/10/2025, 10:40 AM',
|
||||
products: 10,
|
||||
submittedBy: 'Ahmed (Owner)',
|
||||
status: 'Pending',
|
||||
reviewer: 'Omar',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
establishment: 'Khazan meat factory',
|
||||
emirate: 'Sharjah',
|
||||
year: '2025',
|
||||
quarter: 'Q3',
|
||||
submittedAt: '18/10/2025, 10:40 AM',
|
||||
products: 6,
|
||||
submittedBy: 'Sara (Admin)',
|
||||
status: 'Approved',
|
||||
reviewer: 'Abdul',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
establishment: 'SPINNEYS FRESH FOOD INDUSTRIES L.L.C',
|
||||
emirate: 'Dubai',
|
||||
year: '2025',
|
||||
quarter: 'Q2',
|
||||
submittedAt: '18/10/2025, 10:40 AM',
|
||||
products: 8,
|
||||
submittedBy: 'Shaik (Admin)',
|
||||
status: 'Approved',
|
||||
reviewer: 'Abdul',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
establishment: 'FRESHLY FROZEN FOODS FACTORY L.L.C',
|
||||
emirate: 'Dubai',
|
||||
year: '2025',
|
||||
quarter: 'Q1',
|
||||
submittedAt: '18/10/2025, 10:40 AM',
|
||||
products: 4,
|
||||
submittedBy: 'Abdul (Manager)',
|
||||
status: 'Rejected',
|
||||
reviewer: 'Abdul',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
establishment: 'SAHAR FOOD INDUSTRY (L.L.C)',
|
||||
emirate: 'Dubai',
|
||||
year: '2025',
|
||||
quarter: 'Q4',
|
||||
submittedAt: '18/10/2025, 10:40 AM',
|
||||
products: 8,
|
||||
submittedBy: 'Ahmed (Owner)',
|
||||
status: 'Approved',
|
||||
reviewer: 'Abdul',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
establishment: 'CONTINENTAL FOOD PROCESSING LLC',
|
||||
emirate: 'Ajman',
|
||||
year: '2025',
|
||||
quarter: 'Q3',
|
||||
submittedAt: '18/10/2025, 10:40 AM',
|
||||
products: 5,
|
||||
submittedBy: 'Sara (Admin)',
|
||||
status: 'Rejected',
|
||||
reviewer: 'Abdul',
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
establishment: 'AL KHAZNA POULTRY FARM',
|
||||
emirate: 'Abu Dhabi',
|
||||
year: '2025',
|
||||
quarter: 'Q2',
|
||||
submittedAt: '18/10/2025, 10:40 AM',
|
||||
products: 7,
|
||||
submittedBy: 'Sara (Admin)',
|
||||
status: 'Approved',
|
||||
reviewer: 'Abdul',
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
establishment: 'DIAMOND MEAT PROCESSING L.L.C',
|
||||
emirate: 'Abu Dhabi',
|
||||
year: '2025',
|
||||
quarter: 'Q2',
|
||||
submittedAt: '18/10/2025, 10:40 AM',
|
||||
products: 7,
|
||||
submittedBy: 'Ahmed (Owner)',
|
||||
status: 'Approved',
|
||||
reviewer: 'Abdul',
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
establishment: 'ALLIANCE FOODS CO L.L.C',
|
||||
emirate: 'Abu Dhabi',
|
||||
year: '2025',
|
||||
quarter: 'Q2',
|
||||
submittedAt: '18/10/2025, 10:40 AM',
|
||||
products: 7,
|
||||
submittedBy: 'Ahmed (Owner)',
|
||||
status: 'Approved',
|
||||
reviewer: 'Abdul',
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
establishment: 'KRUSTASIA FOODS L.L.C',
|
||||
emirate: 'Dubai',
|
||||
year: '2025',
|
||||
quarter: 'Q1',
|
||||
submittedAt: '18/10/2025, 10:40 AM',
|
||||
products: 8,
|
||||
submittedBy: 'Sara (Admin)',
|
||||
status: 'Pending',
|
||||
reviewer: 'Fathima',
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
establishment: 'AL TAMEER FOOD INDUSTRIES',
|
||||
emirate: 'Abu Dhabi',
|
||||
year: '2025',
|
||||
quarter: 'Q3',
|
||||
submittedAt: '19/10/2025, 8:15 AM',
|
||||
products: 11,
|
||||
submittedBy: 'Ahmed (Owner)',
|
||||
status: 'Pending',
|
||||
reviewer: 'Omar',
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
establishment: 'AL RAWABI DAIRY COMPANY',
|
||||
emirate: 'Dubai',
|
||||
year: '2024',
|
||||
quarter: 'Q4',
|
||||
submittedAt: '12/09/2024, 4:10 PM',
|
||||
products: 9,
|
||||
submittedBy: 'Mariam (Admin)',
|
||||
status: 'Approved',
|
||||
reviewer: 'Abdul',
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
establishment: 'EMIRATES FRESH POULTRY',
|
||||
emirate: 'Ajman',
|
||||
year: '2024',
|
||||
quarter: 'Q4',
|
||||
submittedAt: '05/09/2024, 11:05 AM',
|
||||
products: 6,
|
||||
submittedBy: 'Haroon (Manager)',
|
||||
status: 'Rejected',
|
||||
reviewer: 'Omar',
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
establishment: 'GULF FISHERIES LLC',
|
||||
emirate: 'Ras Al Khaimah',
|
||||
year: '2024',
|
||||
quarter: 'Q3',
|
||||
submittedAt: '21/08/2024, 9:15 AM',
|
||||
products: 12,
|
||||
submittedBy: 'Owais (Owner)',
|
||||
status: 'Approved',
|
||||
reviewer: 'Fathima',
|
||||
},
|
||||
];
|
||||
import { SelectField } from '../../components/common/FormControls';
|
||||
import { getSubmissions } from '../../services/admin/submission.js';
|
||||
|
||||
const STATUS_VARIANTS = {
|
||||
Approved: { background: '#F3FAF4', text: '#2F663C', border: '#F3FAF4' },
|
||||
@ -206,6 +35,10 @@ const Badge = ({ children, status }) => {
|
||||
|
||||
const ManageSubmissions = () => {
|
||||
const navigate = useNavigate();
|
||||
const [submissions, setSubmissions] = React.useState([]);
|
||||
const [loading, setLoading] = React.useState(true);
|
||||
const [error, setError] = React.useState(null);
|
||||
|
||||
const [search, setSearch] = React.useState('');
|
||||
const [year, setYear] = React.useState('');
|
||||
const [quarter, setQuarter] = React.useState('');
|
||||
@ -214,37 +47,68 @@ const ManageSubmissions = () => {
|
||||
const [currentPage, setCurrentPage] = React.useState(1);
|
||||
const pageSize = 10;
|
||||
|
||||
const yearOptions = React.useMemo(() => ['All', ...new Set(submissions.map((item) => item.year))], []);
|
||||
const quarterOptions = React.useMemo(() => ['All', ...new Set(submissions.map((item) => item.quarter))], []);
|
||||
const emirateOptions = React.useMemo(() => ['All', ...new Set(submissions.map((item) => item.emirate))], []);
|
||||
const statusOptions = React.useMemo(() => ['All', ...new Set(submissions.map((item) => item.status))], []);
|
||||
// 🔹 Fetch data from backend
|
||||
React.useEffect(() => {
|
||||
const fetchSubmissions = async () => {
|
||||
try {
|
||||
const result = await getSubmissions();
|
||||
|
||||
// Handle success or direct data array
|
||||
const data = result?.status === 'success' ? result.data : result;
|
||||
|
||||
// Map API fields to table fields
|
||||
const mapped = (data || []).map((item) => ({
|
||||
id: item.id,
|
||||
establishment: '', // blank
|
||||
emirate: '', // blank
|
||||
year: String(item.year ?? ''),
|
||||
quarter: item.quarter ?? '',
|
||||
products: '', // blank
|
||||
submittedBy: item.created_by ?? '',
|
||||
submittedAt: item.created_at
|
||||
? new Date(item.created_at).toLocaleString()
|
||||
: '',
|
||||
status: item.status ?? '',
|
||||
reviewer: '', // blank
|
||||
}));
|
||||
|
||||
setSubmissions(mapped);
|
||||
} catch (err) {
|
||||
console.error('Failed to load submissions', err);
|
||||
setError('Unable to load submissions. Please try again later.');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchSubmissions();
|
||||
}, []);
|
||||
|
||||
const yearOptions = React.useMemo(() => ['All', ...new Set(submissions.map((i) => i.year))], [submissions]);
|
||||
const quarterOptions = React.useMemo(() => ['All', ...new Set(submissions.map((i) => i.quarter))], [submissions]);
|
||||
const emirateOptions = React.useMemo(() => ['All', ...new Set(submissions.map((i) => i.emirate))], [submissions]);
|
||||
const statusOptions = React.useMemo(() => ['All', ...new Set(submissions.map((i) => i.status))], [submissions]);
|
||||
|
||||
const filtered = React.useMemo(() => {
|
||||
return submissions.filter((item) => {
|
||||
const matchesSearch = search
|
||||
? item.establishment.toLowerCase().includes(search.toLowerCase()) ||
|
||||
item.submittedBy.toLowerCase().includes(search.toLowerCase())
|
||||
const matchSearch = search
|
||||
? item.establishment?.toLowerCase().includes(search.toLowerCase()) ||
|
||||
item.submittedBy?.toLowerCase().includes(search.toLowerCase())
|
||||
: true;
|
||||
const matchesYear = !year || year === 'All' || item.year === year;
|
||||
const matchesQuarter = !quarter || quarter === 'All' || item.quarter === quarter;
|
||||
const matchesEmirate = !emirate || emirate === 'All' || item.emirate === emirate;
|
||||
const matchesStatus = !status || status === 'All' || item.status === status;
|
||||
return matchesSearch && matchesYear && matchesQuarter && matchesEmirate && matchesStatus;
|
||||
const matchYear = !year || year === 'All' || item.year === year;
|
||||
const matchQuarter = !quarter || quarter === 'All' || item.quarter === quarter;
|
||||
const matchEmirate = !emirate || emirate === 'All' || item.emirate === emirate;
|
||||
const matchStatus = !status || status === 'All' || item.status === status;
|
||||
return matchSearch && matchYear && matchQuarter && matchEmirate && matchStatus;
|
||||
});
|
||||
}, [search, year, quarter, emirate, status]);
|
||||
}, [submissions, search, year, quarter, emirate, status]);
|
||||
|
||||
React.useEffect(() => {
|
||||
setCurrentPage(1);
|
||||
}, [search, year, quarter, emirate, status]);
|
||||
|
||||
const summaryCounts = React.useMemo(() => {
|
||||
return {
|
||||
total: submissions.length,
|
||||
approved: submissions.filter((item) => item.status === 'Approved').length,
|
||||
pending: submissions.filter((item) => item.status === 'Pending').length,
|
||||
rejected: submissions.filter((item) => item.status === 'Rejected').length,
|
||||
};
|
||||
}, []);
|
||||
const summaryCounts = React.useMemo(() => ({
|
||||
total: submissions.length,
|
||||
approved: submissions.filter((i) => i.status === 'Approved').length,
|
||||
pending: submissions.filter((i) => i.status === 'Pending').length,
|
||||
rejected: submissions.filter((i) => i.status === 'Rejected').length,
|
||||
}), [submissions]);
|
||||
|
||||
const headers = [
|
||||
'Establishment',
|
||||
@ -259,116 +123,61 @@ const ManageSubmissions = () => {
|
||||
'Actions',
|
||||
];
|
||||
|
||||
const tableRows = React.useMemo(
|
||||
() =>
|
||||
filtered.map((item) => [
|
||||
item.establishment,
|
||||
item.emirate,
|
||||
item.year,
|
||||
item.quarter,
|
||||
item.products,
|
||||
item.submittedBy,
|
||||
item.submittedAt,
|
||||
item.status,
|
||||
item.reviewer,
|
||||
item.id,
|
||||
]),
|
||||
[filtered]
|
||||
);
|
||||
const tableRows = filtered.map((item) => [
|
||||
item.establishment,
|
||||
item.emirate,
|
||||
item.year,
|
||||
item.quarter,
|
||||
item.products,
|
||||
item.submittedBy,
|
||||
item.submittedAt,
|
||||
item.status,
|
||||
item.reviewer,
|
||||
item.id,
|
||||
]);
|
||||
|
||||
const columnWidths = React.useMemo(
|
||||
() => [
|
||||
302,
|
||||
105,
|
||||
83,
|
||||
91,
|
||||
100,
|
||||
152,
|
||||
208,
|
||||
120,
|
||||
104,
|
||||
119,
|
||||
],
|
||||
[]
|
||||
);
|
||||
const columnWidths = [302, 105, 83, 91, 100, 152, 208, 120, 104, 119];
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-screen text-[#92722A]">
|
||||
<svg className="animate-spin h-6 w-6 mr-3 text-[#92722A]" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v8z"></path>
|
||||
</svg>
|
||||
Loading submissions...
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-screen text-red-600">
|
||||
{error}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const toolbar = (
|
||||
<div className="px-6 pt-6 pb-5 space-y-4">
|
||||
<div className="flex flex-col gap-2 md:flex-row md:items-center md:justify-between">
|
||||
<h2 className="text-[18px] leading-[28px] font-medium text-[#232528]">
|
||||
<h2 className="text-[18px] font-medium text-[#232528]">
|
||||
Manage Submissions <span className="text-[#7A7F87]">({filtered.length})</span>
|
||||
</h2>
|
||||
<div className="w-full md:w-auto">
|
||||
<div className="flex flex-wrap gap-3 md:flex-nowrap">
|
||||
<div className="relative" style={{ width: '320px', height: '40px' }}>
|
||||
<img src={SearchIcon} alt="Search" className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4" />
|
||||
<input
|
||||
type="text"
|
||||
value={search}
|
||||
onChange={(event) => setSearch(event.target.value)}
|
||||
placeholder="Search Establishment"
|
||||
className="h-full w-full rounded-md border border-[#C3C6CB] pl-9 pr-3 text-sm focus:outline-none focus:ring-1 focus:ring-[#92722A]"
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full md:w-[134px]" style={{ height: '40px' }}>
|
||||
<SelectField
|
||||
label={null}
|
||||
value={quarter}
|
||||
onChange={(event) => setQuarter(event.target.value)}
|
||||
options={quarterOptions.map((value) => ({ label: value === 'All' ? 'All' : value, value }))}
|
||||
placeholder="Quarter"
|
||||
width="100%"
|
||||
variant="toolbar"
|
||||
allowClear
|
||||
onClear={() => setQuarter('All')}
|
||||
clearValue="All"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex w-full flex-wrap gap-3 md:w-auto md:flex-nowrap">
|
||||
<div className="w-full md:w-[134px]" style={{ height: '40px' }}>
|
||||
<SelectField
|
||||
label={null}
|
||||
value={year}
|
||||
onChange={(event) => setYear(event.target.value)}
|
||||
options={yearOptions.map((value) => ({ label: value === 'All' ? 'All' : value, value }))}
|
||||
placeholder="Year"
|
||||
width="100%"
|
||||
variant="toolbar"
|
||||
allowClear
|
||||
onClear={() => setYear('All')}
|
||||
clearValue="All"
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full md:w-[134px]" style={{ height: '40px' }}>
|
||||
<SelectField
|
||||
label={null}
|
||||
value={status}
|
||||
onChange={(event) => setStatus(event.target.value)}
|
||||
options={statusOptions.map((value) => ({ label: value === 'All' ? 'All' : value, value }))}
|
||||
placeholder="Status"
|
||||
width="100%"
|
||||
variant="toolbar"
|
||||
allowClear
|
||||
onClear={() => setStatus('All')}
|
||||
clearValue="All"
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full md:w-[134px]" style={{ height: '40px' }}>
|
||||
<SelectField
|
||||
label={null}
|
||||
value={emirate}
|
||||
onChange={(event) => setEmirate(event.target.value)}
|
||||
options={emirateOptions.map((value) => ({ label: value === 'All' ? 'All' : value, value }))}
|
||||
placeholder="Emirates"
|
||||
width="100%"
|
||||
variant="toolbar"
|
||||
allowClear
|
||||
onClear={() => setEmirate('All')}
|
||||
clearValue="All"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<div className="relative w-[320px]">
|
||||
<img src={SearchIcon} alt="Search" className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4" />
|
||||
<input
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
placeholder="Search Establishment"
|
||||
className="h-10 w-full rounded-md border border-[#C3C6CB] pl-9 pr-3 text-sm focus:outline-none focus:ring-1 focus:ring-[#92722A]"
|
||||
/>
|
||||
</div>
|
||||
<SelectField value={quarter} onChange={(e) => setQuarter(e.target.value)} options={quarterOptions.map((v) => ({ label: v, value: v }))} placeholder="Quarter" width="134px" variant="toolbar" />
|
||||
<SelectField value={year} onChange={(e) => setYear(e.target.value)} options={yearOptions.map((v) => ({ label: v, value: v }))} placeholder="Year" width="134px" variant="toolbar" />
|
||||
<SelectField value={status} onChange={(e) => setStatus(e.target.value)} options={statusOptions.map((v) => ({ label: v, value: v }))} placeholder="Status" width="134px" variant="toolbar" />
|
||||
<SelectField value={emirate} onChange={(e) => setEmirate(e.target.value)} options={emirateOptions.map((v) => ({ label: v, value: v }))} placeholder="Emirates" width="134px" variant="toolbar" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -380,53 +189,37 @@ const ManageSubmissions = () => {
|
||||
<div className="max-w-[1280px] mx-auto px-4 py-6 flex flex-col gap-6">
|
||||
<nav className="flex items-center gap-2 text-sm text-[#8F9299]">
|
||||
<Link to="/admin/dashboard" className="flex items-center gap-2 text-[#232528] hover:underline">
|
||||
Dashboard
|
||||
<img src={CaretUp} alt="Dashboard" className="h-3 w-3" />
|
||||
Dashboard <img src={CaretUp} alt="Dashboard" className="h-3 w-3" />
|
||||
</Link>
|
||||
<span className="flex items-center gap-2 text-[#92722A] font-medium">
|
||||
Manage Submissions
|
||||
{/* <img src={CaretUp} alt="Manage Submissions" className="h-3 w-3" /> */}
|
||||
</span>
|
||||
<span className="flex items-center gap-2 text-[#92722A] font-medium">Manage Submissions</span>
|
||||
</nav>
|
||||
|
||||
<div className="flex flex-col gap-3 rounded-[12px] bg-white px-5 py-3 shadow-[0_12px_24px_rgba(15,23,42,0.06)] md:flex-row md:items-center md:justify-between">
|
||||
<div className="flex flex-wrap gap-[6px] ">
|
||||
{(
|
||||
[
|
||||
{ key: 'total', label: 'Total', value: summaryCounts.total },
|
||||
{ key: 'approved', label: 'Approved', value: summaryCounts.approved },
|
||||
{ key: 'pending', label: 'Pending', value: summaryCounts.pending },
|
||||
{ key: 'rejected', label: 'Rejected', value: summaryCounts.rejected },
|
||||
]
|
||||
).map((item) => {
|
||||
const variant = SUMMARY_VARIANTS[item.key];
|
||||
{/* Summary bar */}
|
||||
<div className="flex flex-wrap gap-[6px] rounded-[12px] bg-white px-5 py-3 shadow">
|
||||
{Object.entries(summaryCounts).map(([key, val]) => {
|
||||
const v = SUMMARY_VARIANTS[key];
|
||||
return (
|
||||
<div
|
||||
key={item.key}
|
||||
className="inline-flex h-[28px] items-center justify-start gap-[6px] rounded-[8px] px-2 py-[2px] text-[13px] ml-5"
|
||||
style={{ background: variant.background }}
|
||||
>
|
||||
<span className="font-medium" style={{ color: variant.label }}>
|
||||
{item.label}:
|
||||
<div key={key} className="inline-flex items-center gap-[6px] rounded-[8px] px-2 py-[2px]" style={{ background: v.background }}>
|
||||
<span className="font-medium text-[13px]" style={{ color: v.label }}>
|
||||
{key.charAt(0).toUpperCase() + key.slice(1)}:
|
||||
</span>
|
||||
<span className="font-semibold" style={{ color: variant.value }}>
|
||||
{item.value}
|
||||
<span className="font-semibold text-[13px]" style={{ color: v.value }}>
|
||||
{val}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex h-10 w-full items-center justify-center gap-3 rounded-[6px] border border-[#C3C6CB] bg-white px-5 text-sm font-medium text-[#5F646D] transition-colors hover:bg-[#F7F7F7] md:w-[143px]"
|
||||
style={{ paddingTop: '10px', paddingBottom: '10px' }}
|
||||
className="ml-auto inline-flex h-10 items-center justify-center gap-3 rounded-[6px] border border-[#C3C6CB] bg-white px-5 text-sm font-medium text-[#5F646D] hover:bg-[#F7F7F7]"
|
||||
>
|
||||
<img src={DownloadIcon} alt="Export" className="h-4 w-4" />
|
||||
<span>Export CSV</span>
|
||||
Export CSV
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="rounded-2xl border border-[#E5E7EB] bg-white shadow-[0_16px_32px_rgba(15,23,42,0.06)] overflow-hidden">
|
||||
{/* Table */}
|
||||
<div className="rounded-2xl border border-[#E5E7EB] bg-white shadow overflow-hidden">
|
||||
<Table
|
||||
headers={headers}
|
||||
rows={tableRows}
|
||||
@ -440,36 +233,17 @@ const ManageSubmissions = () => {
|
||||
totalItems: filtered.length,
|
||||
}}
|
||||
renderCell={(value, rowIndex, columnIndex) => {
|
||||
if (columnIndex === 7) {
|
||||
if (columnIndex === 7)
|
||||
return <div className="flex justify-start"><Badge status={String(value)}>{value}</Badge></div>;
|
||||
if (columnIndex === 9)
|
||||
return (
|
||||
<div className="flex justify-start">
|
||||
<Badge status={String(value)}>{value}</Badge>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => navigate(`/admin/validations/${value}`)}
|
||||
className="text-sm font-medium text-[#92722A] hover:underline"
|
||||
>
|
||||
View Details
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
if (columnIndex === 9) {
|
||||
return (
|
||||
<div className="flex h-12 w-full items-center justify-start">
|
||||
<button
|
||||
type="button"
|
||||
className="whitespace-nowrap text-sm font-medium text-[#92722A]"
|
||||
onClick={() => navigate(`/admin/validations/${value}`)}
|
||||
>
|
||||
View Details
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (columnIndex === 5) {
|
||||
return <span className="text-sm text-[#232528]">{value}</span>;
|
||||
}
|
||||
|
||||
if (columnIndex === 8) {
|
||||
return <span className="text-sm text-[#232528]">{value}</span>;
|
||||
}
|
||||
|
||||
return value;
|
||||
}}
|
||||
/>
|
||||
|
||||
18
ipi-survey-platform/src/services/admin/submission.js
Normal file
18
ipi-survey-platform/src/services/admin/submission.js
Normal file
@ -0,0 +1,18 @@
|
||||
import apiClient from '../api/apiClient.js';
|
||||
|
||||
/** Fetch all submissions for the listing page */
|
||||
export const getSubmissions = async () => {
|
||||
const response = await apiClient.get('/submissions');
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/** 🔹 Fetch a single submission by ID */
|
||||
export const getSubmissionById = async (id) => {
|
||||
const response = await apiClient.get(`/submissions/${id}`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export default {
|
||||
getSubmissions,
|
||||
getSubmissionById,
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user