diff --git a/frontend/public/locales/en-GB/translation.json b/frontend/public/locales/en-GB/translation.json index 2b00d7108..0ca900e90 100644 --- a/frontend/public/locales/en-GB/translation.json +++ b/frontend/public/locales/en-GB/translation.json @@ -85,6 +85,7 @@ "warning": { "tooltipTitle": "Warning" }, + "edit": "Edit", "delete": "Delete", "username": "Username", "password": "Password", @@ -538,10 +539,6 @@ "title": "Edit Table of Contents", "desc": "Add or edit bookmarks and table of contents in PDF documents" }, - "automate": { - "title": "Automate", - "desc": "Build multi-step workflows by chaining together PDF actions. Ideal for recurring tasks." - }, "manageCertificates": { "title": "Manage Certificates", "desc": "Import, export, or delete digital certificate files used for signing PDFs." @@ -601,6 +598,10 @@ "changePermissions": { "title": "Change Permissions", "desc": "Change document restrictions and permissions" + }, + "automate": { + "title": "Automate", + "desc": "Build multi-step workflows by chaining together PDF actions. Ideal for recurring tasks." } }, "viewPdf": { @@ -2184,5 +2185,68 @@ "results": { "title": "Decrypted PDFs" } - } + }, + "automate": { + "title": "Automate", + "desc": "Build multi-step workflows by chaining together PDF actions. Ideal for recurring tasks.", + "invalidStep": "Invalid step", + "files": { + "placeholder": "Select files to process with this automation" + }, + "selection": { + "title": "Automation Selection", + "saved": { + "title": "Saved" + }, + "createNew": { + "title": "Create New Automation" + }, + "suggested": { + "title": "Suggested" + } + }, + "creation": { + "createTitle": "Create Automation", + "editTitle": "Edit Automation", + "description": "Automations run tools sequentially. To get started, add tools in the order you want them to run.", + "name": { + "placeholder": "Automation name" + }, + "tools": { + "selectTool": "Select a tool...", + "selected": "Selected Tools", + "remove": "Remove tool", + "configure": "Configure tool", + "notConfigured": "! Not Configured", + "addTool": "Add Tool", + "add": "Add a tool..." + }, + "save": "Save Automation", + "unsavedChanges": { + "title": "Unsaved Changes", + "message": "You have unsaved changes. Are you sure you want to go back? All changes will be lost.", + "cancel": "Cancel", + "confirm": "Go Back" + } + }, + "run": { + "title": "Run Automation" + }, + "sequence": { + "unnamed": "Unnamed Automation", + "steps": "{{count}} steps", + "running": "Running Automation...", + "run": "Run Automation", + "finish": "Finish" + }, + "reviewTitle": "Automation Results", + "config": { + "loading": "Loading tool configuration...", + "noSettings": "This tool does not have configurable settings.", + "title": "Configure {{toolName}}", + "description": "Configure the settings for this tool. These settings will be applied when the automation runs.", + "cancel": "Cancel", + "save": "Save Configuration" + } + } } diff --git a/frontend/src/components/tools/addWatermark/WatermarkFormatting.tsx b/frontend/src/components/tools/addWatermark/WatermarkFormatting.tsx index 9a267f638..b6af3365c 100644 --- a/frontend/src/components/tools/addWatermark/WatermarkFormatting.tsx +++ b/frontend/src/components/tools/addWatermark/WatermarkFormatting.tsx @@ -6,7 +6,7 @@ import NumberInputWithUnit from "../shared/NumberInputWithUnit"; interface WatermarkFormattingProps { parameters: AddWatermarkParameters; - onParameterChange: (key: keyof AddWatermarkParameters, value: any) => void; + onParameterChange: (key: K, value: AddWatermarkParameters[K]) => void; disabled?: boolean; } diff --git a/frontend/src/components/tools/addWatermark/WatermarkImageFile.tsx b/frontend/src/components/tools/addWatermark/WatermarkImageFile.tsx index 6f38ae206..85e723ccb 100644 --- a/frontend/src/components/tools/addWatermark/WatermarkImageFile.tsx +++ b/frontend/src/components/tools/addWatermark/WatermarkImageFile.tsx @@ -6,7 +6,7 @@ import FileUploadButton from "../../shared/FileUploadButton"; interface WatermarkImageFileProps { parameters: AddWatermarkParameters; - onParameterChange: (key: keyof AddWatermarkParameters, value: any) => void; + onParameterChange: (key: K, value: AddWatermarkParameters[K]) => void; disabled?: boolean; } @@ -17,7 +17,7 @@ const WatermarkImageFile = ({ parameters, onParameterChange, disabled = false }: onParameterChange('watermarkImage', file)} + onChange={(file) => onParameterChange('watermarkImage', file || undefined)} accept="image/*" disabled={disabled} placeholder={t('watermark.settings.image.choose', 'Choose Image')} diff --git a/frontend/src/components/tools/addWatermark/WatermarkStyleSettings.tsx b/frontend/src/components/tools/addWatermark/WatermarkStyleSettings.tsx index 2de9335b0..f3c6751cf 100644 --- a/frontend/src/components/tools/addWatermark/WatermarkStyleSettings.tsx +++ b/frontend/src/components/tools/addWatermark/WatermarkStyleSettings.tsx @@ -5,7 +5,7 @@ import { AddWatermarkParameters } from "../../../hooks/tools/addWatermark/useAdd interface WatermarkStyleSettingsProps { parameters: AddWatermarkParameters; - onParameterChange: (key: keyof AddWatermarkParameters, value: any) => void; + onParameterChange: (key: K, value: AddWatermarkParameters[K]) => void; disabled?: boolean; } @@ -19,7 +19,7 @@ const WatermarkStyleSettings = ({ parameters, onParameterChange, disabled = fals {t('watermark.settings.rotation', 'Rotation (degrees)')} onParameterChange('rotation', value || 0)} + onChange={(value) => onParameterChange('rotation', typeof value === 'number' ? value : (parseInt(value as string, 10) || 0))} min={-360} max={360} disabled={disabled} @@ -28,7 +28,7 @@ const WatermarkStyleSettings = ({ parameters, onParameterChange, disabled = fals {t('watermark.settings.opacity', 'Opacity (%)')} onParameterChange('opacity', value || 50)} + onChange={(value) => onParameterChange('opacity', typeof value === 'number' ? value : (parseInt(value as string, 10) || 50))} min={0} max={100} disabled={disabled} @@ -40,7 +40,7 @@ const WatermarkStyleSettings = ({ parameters, onParameterChange, disabled = fals {t('watermark.settings.spacing.width', 'Width Spacing')} onParameterChange('widthSpacer', value || 50)} + onChange={(value) => onParameterChange('widthSpacer', typeof value === 'number' ? value : (parseInt(value as string, 10) || 50))} min={0} max={200} disabled={disabled} @@ -49,7 +49,7 @@ const WatermarkStyleSettings = ({ parameters, onParameterChange, disabled = fals {t('watermark.settings.spacing.height', 'Height Spacing')} onParameterChange('heightSpacer', value || 50)} + onChange={(value) => onParameterChange('heightSpacer', typeof value === 'number' ? value : (parseInt(value as string, 10) || 50))} min={0} max={200} disabled={disabled} diff --git a/frontend/src/components/tools/addWatermark/WatermarkTextStyle.tsx b/frontend/src/components/tools/addWatermark/WatermarkTextStyle.tsx index 00fd21d09..91217f76b 100644 --- a/frontend/src/components/tools/addWatermark/WatermarkTextStyle.tsx +++ b/frontend/src/components/tools/addWatermark/WatermarkTextStyle.tsx @@ -6,7 +6,7 @@ import { alphabetOptions } from "../../../constants/addWatermarkConstants"; interface WatermarkTextStyleProps { parameters: AddWatermarkParameters; - onParameterChange: (key: keyof AddWatermarkParameters, value: any) => void; + onParameterChange: (key: K, value: AddWatermarkParameters[K]) => void; disabled?: boolean; } diff --git a/frontend/src/components/tools/addWatermark/WatermarkWording.tsx b/frontend/src/components/tools/addWatermark/WatermarkWording.tsx index 621a0f399..5278ca332 100644 --- a/frontend/src/components/tools/addWatermark/WatermarkWording.tsx +++ b/frontend/src/components/tools/addWatermark/WatermarkWording.tsx @@ -6,7 +6,7 @@ import { removeEmojis } from "../../../utils/textUtils"; interface WatermarkWordingProps { parameters: AddWatermarkParameters; - onParameterChange: (key: keyof AddWatermarkParameters, value: any) => void; + onParameterChange: (key: K, value: AddWatermarkParameters[K]) => void; disabled?: boolean; } diff --git a/frontend/src/components/tools/automate/AutomationEntry.tsx b/frontend/src/components/tools/automate/AutomationEntry.tsx index 906b834f5..3314831be 100644 --- a/frontend/src/components/tools/automate/AutomationEntry.tsx +++ b/frontend/src/components/tools/automate/AutomationEntry.tsx @@ -106,7 +106,7 @@ export default function AutomationEntry({
{renderContent()}
- + {showMenu && ( - {t('common.edit', 'Edit')} + {t('edit', 'Edit')} )} {onDelete && ( @@ -151,7 +151,7 @@ export default function AutomationEntry({ onDelete(); }} > - {t('common.delete', 'Delete')} + {t('delete', 'Delete')} )} diff --git a/frontend/src/components/tools/automate/AutomationExecutor.tsx b/frontend/src/components/tools/automate/AutomationExecutor.tsx index 1f3c7d601..c259515e5 100644 --- a/frontend/src/components/tools/automate/AutomationExecutor.tsx +++ b/frontend/src/components/tools/automate/AutomationExecutor.tsx @@ -39,7 +39,7 @@ export const AutomationExecutor: React.FC = ({ const tool = toolRegistry[op.operation]; if (tool?.component) { const toolComponent = tool.component as ToolComponent; - if (toolComponent.tool) { + if ('tool' in toolComponent) { // We still can't call the hook here dynamically // This approach also won't work } @@ -79,7 +79,7 @@ export const AutomationExecutor: React.FC = ({ } const toolComponent = tool.component as ToolComponent; - if (!toolComponent.tool) { + if (!('tool' in toolComponent)) { throw new Error(`Tool ${operation.operation} does not support automation`); } diff --git a/frontend/src/components/tools/automate/AutomationRun.tsx b/frontend/src/components/tools/automate/AutomationRun.tsx index 29e875700..0034e0001 100644 --- a/frontend/src/components/tools/automate/AutomationRun.tsx +++ b/frontend/src/components/tools/automate/AutomationRun.tsx @@ -103,15 +103,15 @@ export default function AutomationRun({ automation, onBack, onComplete, automate setCurrentStepIndex(-1); setIsExecuting(false); setCurrentFiles(finalResults); - + // Properly integrate results with FileContext if (finalResults.length > 0) { console.log(`🎨 Integrating ${finalResults.length} result files with FileContext`); - + // Use FileContext's consumeFiles to properly add results // This replaces input files with output files (like other tools do) await consumeFiles(activeFiles, finalResults); - + console.log(`✅ Successfully integrated automation results with FileContext`); } diff --git a/frontend/src/components/tools/automate/AutomationSelection.tsx b/frontend/src/components/tools/automate/AutomationSelection.tsx index 96fe4d75e..3a0efc8d4 100644 --- a/frontend/src/components/tools/automate/AutomationSelection.tsx +++ b/frontend/src/components/tools/automate/AutomationSelection.tsx @@ -44,7 +44,7 @@ export default function AutomationSelection({ key={automation.id} title={automation.name} badgeIcon={SettingsIcon} - operations={automation.operations.map(op => typeof op === 'string' ? op : op.operation)} + operations={automation.operations.map((op: any) => typeof op === 'string' ? op : op.operation)} onClick={() => onRun(automation)} showMenu={true} onEdit={() => onEdit(automation)} diff --git a/frontend/src/contexts/ToolWorkflowContext.tsx b/frontend/src/contexts/ToolWorkflowContext.tsx index 7fce31aae..895408fa6 100644 --- a/frontend/src/contexts/ToolWorkflowContext.tsx +++ b/frontend/src/contexts/ToolWorkflowContext.tsx @@ -229,14 +229,12 @@ export function useToolWorkflow(): ToolWorkflowContextValue { // Return minimal safe fallback to prevent crashes return { - state: { - sidebarsVisible: true, - leftPanelView: 'toolPicker', - readerMode: false, - previewFile: null, - pageEditorFunctions: null, - searchQuery: '' - }, + sidebarsVisible: true, + leftPanelView: 'toolPicker', + readerMode: false, + previewFile: null, + pageEditorFunctions: null, + searchQuery: '', selectedToolKey: null, selectedTool: null, toolRegistry: {}, diff --git a/frontend/src/hooks/tools/addWatermark/useAddWatermarkParameters.ts b/frontend/src/hooks/tools/addWatermark/useAddWatermarkParameters.ts index a7cb23430..5b9a25da6 100644 --- a/frontend/src/hooks/tools/addWatermark/useAddWatermarkParameters.ts +++ b/frontend/src/hooks/tools/addWatermark/useAddWatermarkParameters.ts @@ -46,3 +46,4 @@ export const useAddWatermarkParameters = (): AddWatermarkParametersHook => { }, }); }; + diff --git a/frontend/src/hooks/tools/automate/useAutomationExecution.ts b/frontend/src/hooks/tools/automate/useAutomationExecution.ts index 9f1a85ae5..5b7e0f903 100644 --- a/frontend/src/hooks/tools/automate/useAutomationExecution.ts +++ b/frontend/src/hooks/tools/automate/useAutomationExecution.ts @@ -49,7 +49,7 @@ export const useAutomationExecution = () => { // Initialize operation hooks for all tools in the automation const hooks: Record = {}; - steps.forEach((step) => { + steps.forEach((step: ExecutionStep) => { const tool = toolRegistry[step.operation]; if (tool?.component) { const toolComponent = tool.component as ToolComponent; diff --git a/frontend/src/hooks/tools/automate/useSuggestedAutomations.ts b/frontend/src/hooks/tools/automate/useSuggestedAutomations.ts index af81e65e5..aa4a4cef8 100644 --- a/frontend/src/hooks/tools/automate/useSuggestedAutomations.ts +++ b/frontend/src/hooks/tools/automate/useSuggestedAutomations.ts @@ -4,8 +4,6 @@ import StarIcon from '@mui/icons-material/Star'; export interface SuggestedAutomation { id: string; - name: string; - description: string; operations: string[]; icon: React.ComponentType; } @@ -16,26 +14,20 @@ export function useSuggestedAutomations(): SuggestedAutomation[] { const suggestedAutomations = useMemo(() => [ { id: "compress-and-merge", - name: t("automate.suggested.compressAndMerge.name", "Compress & Merge"), - description: t("automate.suggested.compressAndMerge.description", "Compress multiple PDFs then merge them into one"), operations: ["compress", "merge"], icon: StarIcon, }, { id: "ocr-and-convert", - name: t("automate.suggested.ocrAndConvert.name", "OCR & Convert"), - description: t("automate.suggested.ocrAndConvert.description", "Apply OCR to PDFs then convert to different format"), operations: ["ocr", "convert"], icon: StarIcon, }, { id: "secure-workflow", - name: t("automate.suggested.secureWorkflow.name", "Secure Workflow"), - description: t("automate.suggested.secureWorkflow.description", "Sanitize, add password, and set permissions"), operations: ["sanitize", "addPassword", "changePermissions"], icon: StarIcon, }, ], [t]); return suggestedAutomations; -} \ No newline at end of file +} diff --git a/frontend/src/tools/Automate.tsx b/frontend/src/tools/Automate.tsx index e1f621815..957dc560c 100644 --- a/frontend/src/tools/Automate.tsx +++ b/frontend/src/tools/Automate.tsx @@ -4,7 +4,7 @@ import { useFileContext } from "../contexts/FileContext"; import { useToolFileSelection } from "../contexts/FileSelectionContext"; import { createToolFlow } from "../components/tools/shared/createToolFlow"; -import { createFilesToolStep } from "../components/tools/shared/filesToolStep"; +import { createFilesToolStep } from "../components/tools/shared/FilesToolStep"; import AutomationSelection from "../components/tools/automate/AutomationSelection"; import AutomationCreation, { AutomationMode } from "../components/tools/automate/AutomationCreation"; import AutomationRun from "../components/tools/automate/AutomationRun"; @@ -133,7 +133,7 @@ const Automate = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => { }, steps: automationSteps, review: { - isVisible: true, + isVisible: hasResults, operation: automateOperation, title: t('automate.reviewTitle', 'Automation Results') } diff --git a/frontend/src/tools/Compress.tsx b/frontend/src/tools/Compress.tsx index 47c76c4b6..d4106a48b 100644 --- a/frontend/src/tools/Compress.tsx +++ b/frontend/src/tools/Compress.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from "react"; +import React, { use, useEffect } from "react"; import { useTranslation } from "react-i18next"; import { useEndpointEnabled } from "../hooks/useEndpointConfig"; import { useFileContext } from "../contexts/FileContext"; @@ -8,7 +8,7 @@ import { createToolFlow } from "../components/tools/shared/createToolFlow"; import CompressSettings from "../components/tools/compress/CompressSettings"; -import { useCompressParameters, initialParameters } from "../hooks/tools/compress/useCompressParameters"; +import { useCompressParameters } from "../hooks/tools/compress/useCompressParameters"; import { useCompressOperation } from "../hooks/tools/compress/useCompressOperation"; import { BaseToolProps, ToolComponent } from "../types/tool"; import { useCompressTips } from "../components/tooltips/useCompressTips"; @@ -99,8 +99,6 @@ const Compress = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => { Compress.tool = () => useCompressOperation; // Static method to get default parameters for automation -Compress.getDefaultParameters = () => { - return initialParameters; -}; +Compress.getDefaultParameters = () => useCompressParameters(); export default Compress as ToolComponent;