All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 13m1s
162 lines
5.5 KiB
TypeScript
162 lines
5.5 KiB
TypeScript
// src/features/flyer/FlyerUploader.tsx
|
|
import React, { useEffect, useCallback } from 'react';
|
|
import { useNavigate, Link } from 'react-router-dom';
|
|
import { logger } from '../../services/logger.client';
|
|
import { ProcessingStatus } from './ProcessingStatus';
|
|
import { useDragAndDrop } from '../../hooks/useDragAndDrop';
|
|
import { useFlyerUploader } from '../../hooks/useFlyerUploader';
|
|
|
|
interface FlyerUploaderProps {
|
|
onProcessingComplete: () => void;
|
|
}
|
|
|
|
export const FlyerUploader: React.FC<FlyerUploaderProps> = ({ onProcessingComplete }) => {
|
|
const navigate = useNavigate();
|
|
|
|
const {
|
|
processingState,
|
|
statusMessage,
|
|
errorMessage,
|
|
duplicateFlyerId,
|
|
processingStages,
|
|
estimatedTime,
|
|
currentFile,
|
|
flyerId,
|
|
upload,
|
|
resetUploaderState,
|
|
} = useFlyerUploader();
|
|
|
|
useEffect(() => {
|
|
if (statusMessage) logger.info(`FlyerUploader Status: ${statusMessage}`);
|
|
}, [statusMessage]);
|
|
|
|
useEffect(() => {
|
|
if (errorMessage) {
|
|
logger.error(`[FlyerUploader] Error encountered: ${errorMessage}`, { duplicateFlyerId });
|
|
}
|
|
}, [errorMessage, duplicateFlyerId]);
|
|
|
|
// Handle completion and navigation
|
|
useEffect(() => {
|
|
if (processingState === 'completed' && flyerId) {
|
|
onProcessingComplete();
|
|
// Small delay to show the "Complete" state before redirecting
|
|
const timer = setTimeout(() => {
|
|
navigate(`/flyers/${flyerId}`);
|
|
}, 1500);
|
|
return () => clearTimeout(timer);
|
|
}
|
|
}, [processingState, flyerId, onProcessingComplete, navigate]);
|
|
|
|
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
const file = event.target.files?.[0];
|
|
if (file) {
|
|
upload(file);
|
|
}
|
|
event.target.value = '';
|
|
};
|
|
|
|
const onFilesDropped = useCallback(
|
|
(files: FileList) => {
|
|
if (files && files.length > 0) {
|
|
upload(files[0]);
|
|
}
|
|
},
|
|
[upload],
|
|
);
|
|
|
|
const isProcessing = processingState === 'uploading' || processingState === 'polling';
|
|
const { isDragging, dropzoneProps } = useDragAndDrop<HTMLLabelElement>({
|
|
onFilesDropped,
|
|
disabled: isProcessing,
|
|
});
|
|
|
|
const borderColor = isDragging ? 'border-brand-primary' : 'border-gray-400 dark:border-gray-600';
|
|
const bgColor = isDragging
|
|
? 'bg-brand-light/50 dark:bg-brand-dark/20'
|
|
: 'bg-gray-50/50 dark:bg-gray-800/20';
|
|
|
|
if (isProcessing || processingState === 'completed' || processingState === 'error') {
|
|
return (
|
|
<div className="max-w-4xl mx-auto">
|
|
<ProcessingStatus
|
|
stages={processingStages}
|
|
estimatedTime={estimatedTime}
|
|
currentFile={currentFile}
|
|
/>
|
|
<div className="mt-4 text-center">
|
|
{/* Display status message if not completed (completed has its own redirect logic) */}
|
|
{statusMessage && processingState !== 'completed' && (
|
|
<p className="text-gray-600 dark:text-gray-400 mt-2 italic animate-pulse">
|
|
{statusMessage}
|
|
</p>
|
|
)}
|
|
|
|
{processingState === 'completed' && (
|
|
<p className="text-green-600 dark:text-green-400 mt-2 font-bold">
|
|
Processing complete! Redirecting to flyer {flyerId}...
|
|
</p>
|
|
)}
|
|
|
|
{errorMessage && (
|
|
<div className="text-red-600 dark:text-red-400 font-semibold p-4 bg-red-100 dark:bg-red-900/30 rounded-md">
|
|
{duplicateFlyerId ? (
|
|
<p>
|
|
{errorMessage} You can view it here:{' '}
|
|
<Link to={`/flyers/${duplicateFlyerId}`} className="text-blue-500 underline" data-discover="true">
|
|
Flyer #{duplicateFlyerId}
|
|
</Link>
|
|
</p>
|
|
) : (
|
|
<p>{errorMessage}</p>
|
|
)}
|
|
</div>
|
|
)}
|
|
{processingState === 'polling' && (
|
|
<button
|
|
onClick={resetUploaderState}
|
|
className="mt-4 text-sm text-gray-500 hover:text-gray-800 dark:hover:text-gray-200 underline transition-colors"
|
|
title="The flyer will continue to process in the background."
|
|
>
|
|
Stop Watching Progress
|
|
</button>
|
|
)}
|
|
{(processingState === 'error' || processingState === 'completed') && (
|
|
<button
|
|
onClick={resetUploaderState}
|
|
className="mt-4 text-sm bg-brand-secondary hover:bg-brand-dark text-white font-bold py-2 px-4 rounded-lg"
|
|
>
|
|
Upload Another Flyer
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="max-w-xl mx-auto p-6 bg-white dark:bg-gray-800 rounded-lg shadow-md">
|
|
<h2 className="text-2xl font-bold mb-4 text-center">Upload New Flyer</h2>
|
|
<div className="flex flex-col items-center space-y-4">
|
|
<label
|
|
htmlFor="flyer-upload"
|
|
className={`w-full text-center px-4 py-6 border-2 border-dashed rounded-lg cursor-pointer transition-colors duration-200 ease-in-out ${bgColor} ${borderColor} hover:border-brand-primary/70 dark:hover:border-brand-primary/50`}
|
|
{...dropzoneProps}
|
|
>
|
|
<span className="text-lg font-medium">Click to select a file</span>
|
|
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">
|
|
or drag and drop a PDF or image
|
|
</p>
|
|
<input
|
|
id="flyer-upload"
|
|
type="file"
|
|
className="hidden"
|
|
onChange={handleFileChange}
|
|
accept=".pdf,image/*"
|
|
/>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|