All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 21m19s
93 lines
3.5 KiB
TypeScript
93 lines
3.5 KiB
TypeScript
// src/pages/admin/FlyerReviewPage.tsx
|
|
import React, { useEffect, useState } from 'react';
|
|
import { Link } from 'react-router-dom';
|
|
import { getFlyersForReview } from '../../services/apiClient';
|
|
import { logger } from '../../services/logger.client';
|
|
import type { Flyer } from '../../types';
|
|
import { LoadingSpinner } from '../../components/LoadingSpinner';
|
|
import { format } from 'date-fns';
|
|
|
|
export const FlyerReviewPage: React.FC = () => {
|
|
const [flyers, setFlyers] = useState<Flyer[]>([]);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
const fetchFlyers = async () => {
|
|
setIsLoading(true);
|
|
setError(null);
|
|
try {
|
|
const response = await getFlyersForReview();
|
|
if (!response.ok) {
|
|
throw new Error((await response.json()).message || 'Failed to fetch flyers for review.');
|
|
}
|
|
setFlyers(await response.json());
|
|
} catch (err) {
|
|
const errorMessage =
|
|
err instanceof Error ? err.message : 'An unknown error occurred while fetching data.';
|
|
logger.error({ err }, 'Failed to fetch flyers for review');
|
|
setError(errorMessage);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
fetchFlyers();
|
|
}, []);
|
|
|
|
return (
|
|
<div className="max-w-7xl mx-auto py-8 px-4">
|
|
<div className="mb-8">
|
|
<Link to="/admin" className="text-brand-primary hover:underline">
|
|
← Back to Admin Dashboard
|
|
</Link>
|
|
<h1 className="text-3xl font-bold text-gray-800 dark:text-white mt-2">
|
|
Flyer Review Queue
|
|
</h1>
|
|
<p className="text-gray-500 dark:text-gray-400">
|
|
Review flyers that were processed with low confidence by the AI.
|
|
</p>
|
|
</div>
|
|
|
|
{isLoading && (
|
|
<div
|
|
role="status"
|
|
aria-label="Loading flyers for review"
|
|
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-hidden">
|
|
<ul className="divide-y divide-gray-200 dark:divide-gray-700">
|
|
{flyers.length === 0 ? (
|
|
<li className="p-6 text-center text-gray-500">
|
|
The review queue is empty. Great job!
|
|
</li>
|
|
) : (
|
|
flyers.map((flyer) => (
|
|
<li key={flyer.flyer_id} className="p-4 hover:bg-gray-50 dark:hover:bg-gray-700/50">
|
|
<Link to={`/flyers/${flyer.flyer_id}`} className="flex items-center space-x-4">
|
|
<img src={flyer.icon_url || undefined} alt={flyer.store?.name || 'Unknown Store'} className="w-12 h-12 rounded-md object-cover" />
|
|
<div className="flex-1">
|
|
<p className="font-semibold text-gray-800 dark:text-white">{flyer.store?.name || 'Unknown Store'}</p>
|
|
<p className="text-sm text-gray-500 dark:text-gray-400">{flyer.file_name}</p>
|
|
</div>
|
|
<div className="text-right text-sm text-gray-500 dark:text-gray-400">
|
|
<p>Uploaded: {format(new Date(flyer.created_at), 'MMM d, yyyy')}</p>
|
|
</div>
|
|
</Link>
|
|
</li>
|
|
))
|
|
)}
|
|
</ul>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}; |