All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 20s
97 lines
5.2 KiB
TypeScript
97 lines
5.2 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import { Link } from 'react-router-dom';
|
|
import { getSuggestedCorrections, getAllMasterItems } from '../services/supabaseClient';
|
|
import { logger } from '../services/logger';
|
|
import type { SuggestedCorrection, MasterGroceryItem } from '../types';
|
|
import { LoadingSpinner } from '../components/LoadingSpinner';
|
|
import { ArrowPathIcon } from '../components/icons/ArrowPathIcon';
|
|
import { CorrectionRow } from '../components/CorrectionRow';
|
|
|
|
export const CorrectionsPage: React.FC = () => {
|
|
const [corrections, setCorrections] = useState<SuggestedCorrection[]>([]);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [masterItems, setMasterItems] = useState<MasterGroceryItem[]>([]);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const fetchCorrections = async () => {
|
|
setIsLoading(true);
|
|
setError(null);
|
|
try {
|
|
// Fetch corrections and master items in parallel for efficiency
|
|
const [correctionsData, masterItemsData] = await Promise.all([
|
|
getSuggestedCorrections(),
|
|
getAllMasterItems()
|
|
]);
|
|
setCorrections(correctionsData);
|
|
setMasterItems(masterItemsData);
|
|
} catch (err: any) {
|
|
logger.error('Failed to fetch corrections', err);
|
|
setError(err.message);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchCorrections();
|
|
}, []);
|
|
|
|
const handleCorrectionProcessed = (correctionId: number) => {
|
|
setCorrections(prev => prev.filter(c => c.id !== correctionId));
|
|
};
|
|
|
|
return (
|
|
<div className="max-w-screen-xl mx-auto py-8 px-4">
|
|
<div className="mb-8">
|
|
<Link to="/admin" className="text-brand-primary hover:underline">← Back to Admin Dashboard</Link>
|
|
<div className="flex justify-between items-center mt-2">
|
|
<div>
|
|
<h1 className="text-3xl font-bold text-gray-800 dark:text-white">User-Submitted Corrections</h1>
|
|
<p className="text-gray-500 dark:text-gray-400">Review and approve or reject pending data corrections.</p>
|
|
</div>
|
|
<button
|
|
onClick={fetchCorrections}
|
|
disabled={isLoading}
|
|
className="p-2 rounded-md bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 disabled:opacity-50"
|
|
title="Refresh Corrections"
|
|
>
|
|
<ArrowPathIcon className={`w-5 h-5 ${isLoading ? 'animate-spin' : ''}`} />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{isLoading && <div className="flex justify-center items-center h-64"><LoadingSpinner /></div>}
|
|
{error && <div className="text-red-500 bg-red-100 dark:bg-red-900/20 p-4 rounded-lg">{error}</div>}
|
|
|
|
{!isLoading && !error && (
|
|
<div className="bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 overflow-x-auto">
|
|
<table className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
<thead className="bg-gray-50 dark:bg-gray-700/50">
|
|
<tr>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Item</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Correction Type</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Suggested Value</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Submitted By</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Date</th>
|
|
<th scope="col" className="relative px-6 py-3"><span className="sr-only">Actions</span></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody className="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
|
{corrections.length === 0 ? (
|
|
<tr><td colSpan={6} className="text-center py-8 text-gray-500">No pending corrections. Great job!</td></tr>
|
|
) : (
|
|
corrections.map(correction => (
|
|
<CorrectionRow
|
|
key={correction.id}
|
|
correction={correction}
|
|
masterItems={masterItems}
|
|
onProcessed={handleCorrectionProcessed} />
|
|
))
|
|
)}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}; |