import { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { Helmet } from 'react-helmet-async'; import { GitBranch } from 'lucide-react'; import AdSlot from '@/components/layout/AdSlot'; import { useTaskPolling } from '@/hooks/useTaskPolling'; import { startTask, uploadFile } from '@/services/api'; import { generateToolSchema } from '@/utils/seo'; import { useFileStore } from '@/stores/fileStore'; import type { Procedure, Flowchart, PDFPage, WizardStep } from './pdf-flowchart/types'; import StepProgress from './pdf-flowchart/StepProgress'; import FlowUpload from './pdf-flowchart/FlowUpload'; import ProcedureSelection from './pdf-flowchart/ProcedureSelection'; import DocumentViewer from './pdf-flowchart/DocumentViewer'; import ManualProcedure from './pdf-flowchart/ManualProcedure'; import FlowGeneration from './pdf-flowchart/FlowGeneration'; import FlowChart from './pdf-flowchart/FlowChart'; import FlowChat from './pdf-flowchart/FlowChat'; import { dispatchRatingPrompt } from '@/utils/ratingPrompt'; // --------------------------------------------------------------------------- // Component // --------------------------------------------------------------------------- export default function PdfFlowchart() { const { t } = useTranslation(); // Wizard state const [step, setStep] = useState(0); const [file, setFile] = useState(null); const [taskId, setTaskId] = useState(null); const [error, setError] = useState(null); const [uploading, setUploading] = useState(false); // Data const [pages, setPages] = useState([]); const [procedures, setProcedures] = useState([]); const [rejectedProcedures, setRejectedProcedures] = useState([]); const [flowcharts, setFlowcharts] = useState([]); const [selectedCount, setSelectedCount] = useState(0); // Sub-views const [viewingProcedure, setViewingProcedure] = useState(null); const [addingManual, setAddingManual] = useState(false); const [viewingFlow, setViewingFlow] = useState(null); const [chatOpen, setChatOpen] = useState(false); // Accept file from homepage smart-upload const storeFile = useFileStore((s) => s.file); const clearStoreFile = useFileStore((s) => s.clearFile); useEffect(() => { if (storeFile && storeFile.type === 'application/pdf') { setFile(storeFile); clearStoreFile(); } }, []); // eslint-disable-line react-hooks/exhaustive-deps // Task polling for extraction const { error: taskError } = useTaskPolling({ taskId, onComplete: (res) => { if (res?.procedures) { setProcedures(res.procedures); setFlowcharts((res.flowcharts || []) as unknown as Flowchart[]); if (res.pages) setPages(res.pages as unknown as PDFPage[]); setStep(1); setUploading(false); } }, onError: (err) => { setError(err || t('common.error')); setStep(0); setUploading(false); }, }); // ------ Handlers ------ const handleFileSelect = (f: File) => { if (f.type === 'application/pdf') { setFile(f); setError(null); } }; const handleUpload = async () => { if (!file) return; setUploading(true); setError(null); try { const data = await uploadFile('/flowchart/extract', file); setTaskId(data.task_id); } catch (err) { setError(err instanceof Error ? err.message : 'Upload failed.'); setUploading(false); } }; const handleTrySample = async () => { setUploading(true); setError(null); try { const data = await startTask('/flowchart/extract-sample'); setTaskId(data.task_id); } catch (err) { setError(err instanceof Error ? err.message : 'Sample failed.'); setUploading(false); } }; const handleRejectProcedure = (id: string) => { const proc = procedures.find((p) => p.id === id); if (!proc) return; setProcedures((prev) => prev.filter((p) => p.id !== id)); setRejectedProcedures((prev) => [...prev, proc]); }; const handleRestoreProcedure = (id: string) => { const proc = rejectedProcedures.find((p) => p.id === id); if (!proc) return; setRejectedProcedures((prev) => prev.filter((p) => p.id !== id)); setProcedures((prev) => [...prev, proc]); }; const handleContinueToGenerate = (selectedIds: string[]) => { setSelectedCount(selectedIds.length); // Filter flowcharts to selected procedures const ids = new Set(selectedIds); const selected = flowcharts.filter((fc) => ids.has(fc.procedureId)); setFlowcharts(selected); setStep(2); }; const handleManualProcedureCreated = (proc: Procedure) => { setProcedures((prev) => [...prev, proc]); // Generate a simple flowchart for the manual procedure const manualFlow: Flowchart = { id: `flow-${proc.id}`, procedureId: proc.id, title: proc.title, steps: [ { id: '1', type: 'start', title: `Begin: ${proc.title.slice(0, 40)}`, description: 'Start of procedure', connections: ['2'] }, { id: '2', type: 'process', title: proc.description.slice(0, 60) || 'Manual step', description: proc.description.slice(0, 150), connections: ['3'] }, { id: '3', type: 'end', title: 'Procedure Complete', description: 'End of procedure', connections: [] }, ], }; setFlowcharts((prev) => [...prev, manualFlow]); setAddingManual(false); }; const handleGenerationDone = () => { setStep(3); dispatchRatingPrompt('pdf-flowchart'); }; const handleFlowUpdate = (updated: Flowchart) => { setFlowcharts((prev) => prev.map((fc) => (fc.id === updated.id ? updated : fc))); if (viewingFlow?.id === updated.id) setViewingFlow(updated); }; const handleReset = () => { setFile(null); setStep(0); setTaskId(null); setError(null); setUploading(false); setPages([]); setProcedures([]); setRejectedProcedures([]); setFlowcharts([]); setSelectedCount(0); setViewingProcedure(null); setAddingManual(false); setViewingFlow(null); setChatOpen(false); }; // ------ SEO ------ const schema = generateToolSchema({ name: t('tools.pdfFlowchart.title'), description: t('tools.pdfFlowchart.description'), url: `${window.location.origin}/tools/pdf-flowchart`, }); // === SUB-VIEWS (full-screen overlays) === if (viewingFlow) { return ( <> {viewingFlow.title} — {t('common.appName')}
setViewingFlow(null)} onOpenChat={() => setChatOpen(true)} /> {chatOpen && ( setChatOpen(false)} onFlowUpdate={handleFlowUpdate} /> )}
); } if (viewingProcedure) { return (
setViewingProcedure(null)} />
); } if (addingManual) { return (
setAddingManual(false)} />
); } // === MAIN VIEW === return ( <> {t('tools.pdfFlowchart.title')} — {t('common.appName')}
{/* Header */}

{t('tools.pdfFlowchart.title')}

{t('tools.pdfFlowchart.description')}

{/* Step Progress */} {/* Step 0: Upload */} {step === 0 && ( setFile(null)} onUpload={handleUpload} onTrySample={handleTrySample} uploading={uploading} error={error} /> )} {/* Step 1: Select Procedures */} {step === 1 && ( setAddingManual(true)} onReject={handleRejectProcedure} onRestore={handleRestoreProcedure} onViewProcedure={setViewingProcedure} onBack={handleReset} /> )} {/* Step 2: Generation */} {step === 2 && ( )} {/* Step 3: Results */} {step === 3 && (

{t('tools.pdfFlowchart.flowReady')}

{t('tools.pdfFlowchart.flowReadyCount', { count: flowcharts.length })}

{flowcharts.map((flow) => (

{flow.title}

{t('tools.pdfFlowchart.steps', { count: flow.steps.length })}

))}
)}
); }