From 9d2ca3c8c8b61c6ff0b093e0aa56181982a4f942 Mon Sep 17 00:00:00 2001 From: Connor Yoh Date: Tue, 12 Aug 2025 16:29:17 +0100 Subject: [PATCH] Toolstep factory and generic files and results steps --- frontend/src/components/tools/ToolPanel.tsx | 2 +- .../tools/compress/CompressSettings.tsx | 13 +- .../tools/shared/OperationButton.tsx | 8 +- .../src/components/tools/shared/ToolStep.tsx | 121 +++++++++------ .../tools/shared/createFilesToolStep.tsx | 31 ++++ .../tools/shared/createResultsToolStep.tsx | 65 ++++++++ frontend/src/tools/Compress.tsx | 117 ++++----------- frontend/src/tools/Convert.tsx | 142 ++++++------------ frontend/src/tools/OCR.tsx | 130 ++++------------ frontend/src/tools/Split.tsx | 119 +++++---------- 10 files changed, 331 insertions(+), 417 deletions(-) create mode 100644 frontend/src/components/tools/shared/createFilesToolStep.tsx create mode 100644 frontend/src/components/tools/shared/createResultsToolStep.tsx diff --git a/frontend/src/components/tools/ToolPanel.tsx b/frontend/src/components/tools/ToolPanel.tsx index 2f2e64c78..67617a657 100644 --- a/frontend/src/components/tools/ToolPanel.tsx +++ b/frontend/src/components/tools/ToolPanel.tsx @@ -39,7 +39,7 @@ export default function ToolPanel() { }`} style={{ width: isPanelVisible ? '20rem' : '0', - padding: isPanelVisible ? '0.5rem' : '0' + padding: '0' }} >
+ + {/* Compression Method */} Compression Method @@ -54,6 +56,7 @@ const CompressSettings = ({ parameters, onParameterChange, disabled = false }: C {/* Quality Adjustment */} {parameters.compressionMethod === 'quality' && ( + Compression Level
setIsSliding(true)} onTouchEnd={() => setIsSliding(false)} disabled={disabled} - style={{ + style={{ width: '100%', height: '6px', borderRadius: '3px', @@ -107,6 +110,8 @@ const CompressSettings = ({ parameters, onParameterChange, disabled = false }: C )} + + {/* File Size Input */} {parameters.compressionMethod === 'filesize' && ( @@ -141,7 +146,7 @@ const CompressSettings = ({ parameters, onParameterChange, disabled = false }: C {/* Compression Options */} - ); } diff --git a/frontend/src/tools/Convert.tsx b/frontend/src/tools/Convert.tsx index 3512ca8eb..788c5aac8 100644 --- a/frontend/src/tools/Convert.tsx +++ b/frontend/src/tools/Convert.tsx @@ -1,16 +1,12 @@ -import React, { useEffect, useMemo, useRef } from "react"; -import { Button, Stack, Text } from "@mantine/core"; +import React, { useEffect, useRef } from "react"; +import { Stack } from "@mantine/core"; import { useTranslation } from "react-i18next"; -import DownloadIcon from "@mui/icons-material/Download"; import { useEndpointEnabled } from "../hooks/useEndpointConfig"; import { useFileContext } from "../contexts/FileContext"; import { useToolFileSelection } from "../contexts/FileSelectionContext"; -import ToolStep, { ToolStepContainer } from "../components/tools/shared/ToolStep"; +import { createToolSteps, ToolStepProvider } from "../components/tools/shared/ToolStep"; import OperationButton from "../components/tools/shared/OperationButton"; -import ErrorNotification from "../components/tools/shared/ErrorNotification"; -import FileStatusIndicator from "../components/tools/shared/FileStatusIndicator"; -import ResultsPreview from "../components/tools/shared/ResultsPreview"; import ConvertSettings from "../components/tools/convert/ConvertSettings"; @@ -105,101 +101,55 @@ const Convert = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => { setCurrentMode('convert'); }; - const previewResults = useMemo(() => - convertOperation.files?.map((file, index) => ({ - file, - thumbnail: convertOperation.thumbnails[index] - })) || [], - [convertOperation.files, convertOperation.thumbnails] - ); + const steps = createToolSteps(); return (
- - - - - + + + {/* Files Step */} + {steps.createFilesStep({ + selectedFiles, + isCollapsed: filesCollapsed, + placeholder: t("convert.selectFilesPlaceholder", "Select files in the main view to get started") + })} - - - - - {hasFiles && convertParams.parameters.fromExtension && convertParams.parameters.toExtension && ( - + - )} - - + + ))} + {!hasResults && ( + + )} - - - {convertOperation.status && ( - {convertOperation.status} - )} - - - - {convertOperation.downloadUrl && ( - - )} - - - - - - + {/* Results Step */} + {steps.createResultsStep({ + isVisible: hasResults, + operation: convertOperation, + title: t("convert.conversionResults", "Conversion Results"), + onFileClick: handleThumbnailClick + })} + +
); }; diff --git a/frontend/src/tools/OCR.tsx b/frontend/src/tools/OCR.tsx index 92a8da132..8e033bb79 100644 --- a/frontend/src/tools/OCR.tsx +++ b/frontend/src/tools/OCR.tsx @@ -1,16 +1,12 @@ import React, { useEffect, useMemo, useState } from "react"; -import { Button, Stack, Text, Box } from "@mantine/core"; +import { Stack, Box } from "@mantine/core"; import { useTranslation } from "react-i18next"; -import DownloadIcon from "@mui/icons-material/Download"; import { useEndpointEnabled } from "../hooks/useEndpointConfig"; import { useFileContext } from "../contexts/FileContext"; import { useToolFileSelection } from "../contexts/FileSelectionContext"; -import ToolStep, { ToolStepContainer } from "../components/tools/shared/ToolStep"; +import { createToolSteps, ToolStepProvider } from "../components/tools/shared/ToolStep"; import OperationButton from "../components/tools/shared/OperationButton"; -import ErrorNotification from "../components/tools/shared/ErrorNotification"; -import FileStatusIndicator from "../components/tools/shared/FileStatusIndicator"; -import ResultsPreview from "../components/tools/shared/ResultsPreview"; import OCRSettings from "../components/tools/ocr/OCRSettings"; import AdvancedOCRSettings from "../components/tools/ocr/AdvancedOCRSettings"; @@ -79,140 +75,80 @@ const OCR = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => { }; - // Step visibility and collapse logic - const filesVisible = true; - const settingsVisible = true; const resultsVisible = hasResults; const filesCollapsed = expandedStep !== 'files'; const settingsCollapsed = expandedStep !== 'settings'; - const previewResults = useMemo(() => - ocrOperation.files?.map((file: File, index: number) => ({ - file, - thumbnail: ocrOperation.thumbnails[index] - })) || [], - [ocrOperation.files, ocrOperation.thumbnails] - ); + + const steps = createToolSteps(); return ( - + {/* Files Step */} - - - + {steps.createFilesStep({ + selectedFiles, + isCollapsed: hasFiles && filesCollapsed, + })} {/* Settings Step */} - { + {steps.create("Settings", { + isCollapsed: !hasFiles || settingsCollapsed, + isCompleted: hasFiles && hasValidSettings, + onCollapsedClick: () => { if (!hasFiles) return; // Only allow if files are selected setExpandedStep(expandedStep === 'settings' ? null : 'settings'); - }} - completedMessage={hasFiles && hasValidSettings && settingsCollapsed ? "Basic settings configured" : undefined} - tooltip={ocrTips} - > + }, + tooltip: ocrTips + }, ( - - + ))} {/* Advanced Step */} - { + {steps.create("Advanced", { + isCollapsed: expandedStep !== 'advanced', + isCompleted: hasFiles && hasResults, + onCollapsedClick: () => { if (!hasFiles) return; // Only allow if files are selected setExpandedStep(expandedStep === 'advanced' ? null : 'advanced'); - }} - completedMessage={hasFiles && hasResults && expandedStep !== 'advanced' ? "OCR processing completed" : undefined} - > + }, + }, ( - + ))} {/* Process Button - Available after all configuration */} {hasValidSettings && !hasResults && ( - - )} {/* Results Step */} - - - {ocrOperation.status && ( - {ocrOperation.status} - )} - - - - {ocrOperation.downloadUrl && ( - - )} - - - - - + {steps.createResultsStep({ + isVisible: resultsVisible, + operation: ocrOperation, + title: t("ocr.results.title", "OCR Results"), + onFileClick: handleThumbnailClick + })} + ); } -export default OCR; \ No newline at end of file +export default OCR; diff --git a/frontend/src/tools/Split.tsx b/frontend/src/tools/Split.tsx index bc516c754..abcc77dd9 100644 --- a/frontend/src/tools/Split.tsx +++ b/frontend/src/tools/Split.tsx @@ -1,16 +1,12 @@ import React, { useEffect, useMemo } from "react"; -import { Button, Stack, Text } from "@mantine/core"; +import { Stack } from "@mantine/core"; import { useTranslation } from "react-i18next"; -import DownloadIcon from "@mui/icons-material/Download"; import { useEndpointEnabled } from "../hooks/useEndpointConfig"; import { useFileContext } from "../contexts/FileContext"; import { useToolFileSelection } from "../contexts/FileSelectionContext"; -import ToolStep, { ToolStepContainer } from "../components/tools/shared/ToolStep"; +import { createToolSteps, ToolStepProvider } from "../components/tools/shared/ToolStep"; import OperationButton from "../components/tools/shared/OperationButton"; -import ErrorNotification from "../components/tools/shared/ErrorNotification"; -import FileStatusIndicator from "../components/tools/shared/FileStatusIndicator"; -import ResultsPreview from "../components/tools/shared/ResultsPreview"; import SplitSettings from "../components/tools/split/SplitSettings"; import { useSplitParameters } from "../hooks/tools/split/useSplitParameters"; @@ -66,42 +62,26 @@ const Split = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => { const hasFiles = selectedFiles.length > 0; const hasResults = splitOperation.downloadUrl !== null; const filesCollapsed = hasFiles; - const settingsCollapsed = hasResults; + const settingsCollapsed = !hasFiles || hasResults; - const previewResults = useMemo(() => - splitOperation.files?.map((file, index) => ({ - file, - thumbnail: splitOperation.thumbnails[index] - })) || [], - [splitOperation.files, splitOperation.thumbnails] - ); + const steps = createToolSteps(); return ( - - + + {/* Files Step */} - - - + {steps.createFilesStep({ + selectedFiles, + isCollapsed: filesCollapsed, + placeholder: "Select a PDF file in the main view to get started" + })} {/* Settings Step */} - + {steps.create("Settings", { + isCollapsed: settingsCollapsed, + isCompleted: hasResults, + onCollapsedClick: hasResults ? handleSettingsReset : undefined, + }, ( { disabled={endpointLoading} /> - {splitParams.parameters.mode && ( - - )} - + ))} + + {!hasResults && ( + + )} {/* Results Step */} - - - {splitOperation.status && ( - {splitOperation.status} - )} - - - - {splitOperation.downloadUrl && ( - - )} - - - - - - + {steps.createResultsStep({ + isVisible: hasResults, + operation: splitOperation, + title: "Split Results", + onFileClick: handleThumbnailClick + })} + +
); }