From c8b911366cc519a9b8c04ea5d2d1d7ffb890998e Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com.> Date: Tue, 26 Aug 2025 10:17:42 +0100 Subject: [PATCH] test --- .../public/locales/en-US/translation.json | 15 +++ .../tools/automate/AutomationEntry.tsx | 17 +++- .../tools/automate/AutomationSelection.tsx | 9 +- .../tools/automate/useSavedAutomations.ts | 24 ++++- .../tools/automate/useSuggestedAutomations.ts | 97 +++++++++++++------ frontend/src/tools/Automate.tsx | 10 +- 6 files changed, 140 insertions(+), 32 deletions(-) diff --git a/frontend/public/locales/en-US/translation.json b/frontend/public/locales/en-US/translation.json index 26c2e5b15..c15def8cc 100644 --- a/frontend/public/locales/en-US/translation.json +++ b/frontend/public/locales/en-US/translation.json @@ -2106,5 +2106,20 @@ "results": { "title": "Decrypted PDFs" } + }, + "automation": { + "suggested": { + "securePdfIngestion": "Secure PDF Ingestion", + "securePdfIngestionDesc": "Sanitise → OCR/Cleanup → PDF/A → Compress", + "emailPreparation": "Email Preparation", + "emailPreparationDesc": "Compress → Split by Size 20MB → Sanitize metadata", + "secureWorkflow": "Security Workflow", + "secureWorkflowDesc": "Sanitize PDFs and add password protection", + "optimizationWorkflow": "Optimization Workflow", + "optimizationWorkflowDesc": "Repair and compress PDFs for better performance" + } + }, + "automate": { + "copyToSaved": "Copy to Saved" } } diff --git a/frontend/src/components/tools/automate/AutomationEntry.tsx b/frontend/src/components/tools/automate/AutomationEntry.tsx index 3314831be..54295c0c6 100644 --- a/frontend/src/components/tools/automate/AutomationEntry.tsx +++ b/frontend/src/components/tools/automate/AutomationEntry.tsx @@ -4,6 +4,7 @@ import { Button, Group, Text, ActionIcon, Menu, Box } from '@mantine/core'; import MoreVertIcon from '@mui/icons-material/MoreVert'; import EditIcon from '@mui/icons-material/Edit'; import DeleteIcon from '@mui/icons-material/Delete'; +import ContentCopyIcon from '@mui/icons-material/ContentCopy'; interface AutomationEntryProps { /** Optional title for the automation (usually for custom ones) */ @@ -22,6 +23,8 @@ interface AutomationEntryProps { onEdit?: () => void; /** Delete handler */ onDelete?: () => void; + /** Copy handler (for suggested automations) */ + onCopy?: () => void; } export default function AutomationEntry({ @@ -32,7 +35,8 @@ export default function AutomationEntry({ keepIconColor = false, showMenu = false, onEdit, - onDelete + onDelete, + onCopy }: AutomationEntryProps) { const { t } = useTranslation(); const [isHovered, setIsHovered] = useState(false); @@ -132,6 +136,17 @@ export default function AutomationEntry({ + {onCopy && ( + } + onClick={(e) => { + e.stopPropagation(); + onCopy(); + }} + > + {t('automate.copyToSaved', 'Copy to Saved')} + + )} {onEdit && ( } diff --git a/frontend/src/components/tools/automate/AutomationSelection.tsx b/frontend/src/components/tools/automate/AutomationSelection.tsx index f55cf4c5d..326ac3fe1 100644 --- a/frontend/src/components/tools/automate/AutomationSelection.tsx +++ b/frontend/src/components/tools/automate/AutomationSelection.tsx @@ -5,7 +5,7 @@ import AddCircleOutline from "@mui/icons-material/AddCircleOutline"; import SettingsIcon from "@mui/icons-material/Settings"; import AutomationEntry from "./AutomationEntry"; import { useSuggestedAutomations } from "../../../hooks/tools/automate/useSuggestedAutomations"; -import { AutomationConfig } from "../../../types/automation"; +import { AutomationConfig, SuggestedAutomation } from "../../../types/automation"; interface AutomationSelectionProps { savedAutomations: AutomationConfig[]; @@ -13,6 +13,7 @@ interface AutomationSelectionProps { onRun: (automation: AutomationConfig) => void; onEdit: (automation: AutomationConfig) => void; onDelete: (automation: AutomationConfig) => void; + onCopyFromSuggested: (automation: SuggestedAutomation) => void; } export default function AutomationSelection({ @@ -20,7 +21,8 @@ export default function AutomationSelection({ onCreateNew, onRun, onEdit, - onDelete + onDelete, + onCopyFromSuggested }: AutomationSelectionProps) { const { t } = useTranslation(); const suggestedAutomations = useSuggestedAutomations(); @@ -63,9 +65,12 @@ export default function AutomationSelection({ {suggestedAutomations.map((automation) => ( op.operation)} onClick={() => onRun(automation)} + showMenu={true} + onCopy={() => onCopyFromSuggested(automation)} /> ))} diff --git a/frontend/src/hooks/tools/automate/useSavedAutomations.ts b/frontend/src/hooks/tools/automate/useSavedAutomations.ts index c52e4c784..1f210b432 100644 --- a/frontend/src/hooks/tools/automate/useSavedAutomations.ts +++ b/frontend/src/hooks/tools/automate/useSavedAutomations.ts @@ -1,5 +1,6 @@ import { useState, useEffect, useCallback } from 'react'; import { AutomationConfig } from '../../../services/automationStorage'; +import { SuggestedAutomation } from '../../../types/automation'; export interface SavedAutomation extends AutomationConfig {} @@ -40,6 +41,26 @@ export function useSavedAutomations() { } }, [refreshAutomations]); + const copyFromSuggested = useCallback(async (suggestedAutomation: SuggestedAutomation) => { + try { + const { automationStorage } = await import('../../../services/automationStorage'); + + // Convert suggested automation to saved automation format + const savedAutomation = { + name: suggestedAutomation.name, + description: suggestedAutomation.description, + operations: suggestedAutomation.operations + }; + + await automationStorage.saveAutomation(savedAutomation); + // Refresh the list after saving + refreshAutomations(); + } catch (err) { + console.error('Error copying suggested automation:', err); + throw err; + } + }, [refreshAutomations]); + // Load automations on mount useEffect(() => { loadSavedAutomations(); @@ -50,6 +71,7 @@ export function useSavedAutomations() { loading, error, refreshAutomations, - deleteAutomation + deleteAutomation, + copyFromSuggested }; } \ No newline at end of file diff --git a/frontend/src/hooks/tools/automate/useSuggestedAutomations.ts b/frontend/src/hooks/tools/automate/useSuggestedAutomations.ts index 006c9f179..fd9b7eaec 100644 --- a/frontend/src/hooks/tools/automate/useSuggestedAutomations.ts +++ b/frontend/src/hooks/tools/automate/useSuggestedAutomations.ts @@ -17,9 +17,60 @@ export function useSuggestedAutomations(): SuggestedAutomation[] { const now = new Date().toISOString(); return [ { - id: "compress-and-split", - name: t("automation.suggested.compressAndSplit", "Compress & Split"), - description: t("automation.suggested.compressAndSplitDesc", "Compress PDFs and split them by pages"), + id: "secure-pdf-ingestion", + name: t("automation.suggested.securePdfIngestion", "Secure PDF Ingestion"), + description: t("automation.suggested.securePdfIngestionDesc", "Sanitise → OCR/Cleanup → PDF/A → Compress"), + operations: [ + { + operation: "sanitize", + parameters: { + removeJavaScript: true, + removeEmbeddedFiles: true, + removeXMPMetadata: true, + removeMetadata: true, + removeLinks: false, + removeFonts: false, + } + }, + { + operation: "ocr", + parameters: { + languages: ['eng'], + ocrType: 'skip-text', + ocrRenderType: 'hocr', + additionalOptions: ['clean', 'cleanFinal'], + } + }, + { + operation: "convert", + parameters: { + fromExtension: 'pdf', + toExtension: 'pdfa', + pdfaOptions: { + outputFormat: 'pdfa-1', + } + } + }, + { + operation: "compress", + parameters: { + compressionLevel: 5, + grayscale: false, + expectedSize: '', + compressionMethod: 'quality', + fileSizeValue: '', + fileSizeUnit: 'MB', + } + } + ], + createdAt: now, + updatedAt: now, + icon: SecurityIcon, + }, + { + id: "email-preparation", + name: t("automation.suggested.emailPreparation", "Email Preparation"), + description: t("automation.suggested.emailPreparationDesc", "Compress → Split by Size 20MB → Sanitize metadata"), operations: [ { operation: "compress", @@ -36,41 +87,33 @@ export function useSuggestedAutomations(): SuggestedAutomation[] { operation: "splitPdf", parameters: { mode: 'bySizeOrCount', - pages: '1', - hDiv: '2', - vDiv: '2', + pages: '', + hDiv: '1', + vDiv: '1', merge: false, - splitType: 'pages', - splitValue: '1', + splitType: 'size', + splitValue: '20MB', bookmarkLevel: '1', includeMetadata: false, allowDuplicates: false, } + }, + { + operation: "sanitize", + parameters: { + removeJavaScript: false, + removeEmbeddedFiles: false, + removeXMPMetadata: true, + removeMetadata: true, + removeLinks: false, + removeFonts: false, + } } ], createdAt: now, updatedAt: now, icon: CompressIcon, }, - { - id: "ocr-workflow", - name: t("automation.suggested.ocrWorkflow", "OCR Processing"), - description: t("automation.suggested.ocrWorkflowDesc", "Extract text from PDFs using OCR technology"), - operations: [ - { - operation: "ocr", - parameters: { - languages: ['eng'], - ocrType: 'skip-text', - ocrRenderType: 'hocr', - additionalOptions: [], - } - } - ], - createdAt: now, - updatedAt: now, - icon: TextFieldsIcon, - }, { id: "secure-workflow", name: t("automation.suggested.secureWorkflow", "Security Workflow"), diff --git a/frontend/src/tools/Automate.tsx b/frontend/src/tools/Automate.tsx index 444cdfce9..e31d1abe3 100644 --- a/frontend/src/tools/Automate.tsx +++ b/frontend/src/tools/Automate.tsx @@ -28,7 +28,7 @@ const Automate = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => { const automateOperation = useAutomateOperation(); const toolRegistry = useFlatToolRegistry(); const hasResults = automateOperation.files.length > 0 || automateOperation.downloadUrl !== null; - const { savedAutomations, deleteAutomation, refreshAutomations } = useSavedAutomations(); + const { savedAutomations, deleteAutomation, refreshAutomations, copyFromSuggested } = useSavedAutomations(); const handleStepChange = (data: AutomationStepData) => { // If navigating away from run step, reset automation results @@ -79,6 +79,14 @@ const Automate = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => { onError?.(`Failed to delete automation: ${automation.name}`); } }} + onCopyFromSuggested={async (suggestedAutomation) => { + try { + await copyFromSuggested(suggestedAutomation); + } catch (error) { + console.error('Failed to copy suggested automation:', error); + onError?.(`Failed to copy automation: ${suggestedAutomation.name}`); + } + }} /> );