diff --git a/frontend/src/components/tools/automate/AutomationEntry.tsx b/frontend/src/components/tools/automate/AutomationEntry.tsx new file mode 100644 index 000000000..7afd09866 --- /dev/null +++ b/frontend/src/components/tools/automate/AutomationEntry.tsx @@ -0,0 +1,91 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { Button, Group, Text, Badge } from '@mantine/core'; + +interface AutomationEntryProps { + /** Optional title for the automation (usually for custom ones) */ + title?: string; + /** MUI Icon component for the badge */ + badgeIcon?: React.ComponentType; + /** Array of tool operation names in the workflow */ + operations: string[]; + /** Click handler */ + onClick: () => void; + /** Whether to keep the icon at normal color (for special cases like "Add New") */ + keepIconColor?: boolean; +} + +export default function AutomationEntry({ + title, + badgeIcon: BadgeIcon, + operations, + onClick, + keepIconColor = false +}: AutomationEntryProps) { + const { t } = useTranslation(); + + const renderContent = () => { + if (title) { + // Custom automation with title + return ( + + {BadgeIcon && ( + + )} + + {title} + + + ); + } else { + // Suggested automation showing tool chain + return ( + + {BadgeIcon && ( + + )} + + {operations.map((op, index) => ( + + + {String(t(`${op}.title`, op))} + + {index < operations.length - 1 && ( + + → + + )} + + ))} + + + ); + } + }; + + return ( + + ); +} \ No newline at end of file diff --git a/frontend/src/components/tools/automate/AutomationSelection.tsx b/frontend/src/components/tools/automate/AutomationSelection.tsx index 19ae677c3..9311b424a 100644 --- a/frontend/src/components/tools/automate/AutomationSelection.tsx +++ b/frontend/src/components/tools/automate/AutomationSelection.tsx @@ -1,9 +1,11 @@ -import React, { useState, useEffect } from "react"; +import React from "react"; import { useTranslation } from "react-i18next"; -import { Button, Card, Text, Title, Stack, Group, Badge, Divider } from "@mantine/core"; -import AddIcon from "@mui/icons-material/Add"; +import { Title, Stack, Divider } from "@mantine/core"; +import AddCircleOutline from "@mui/icons-material/AddCircleOutline"; import SettingsIcon from "@mui/icons-material/Settings"; -import StarIcon from "@mui/icons-material/Star"; +import AutomationEntry from "./AutomationEntry"; +import { useSuggestedAutomations } from "../../../hooks/tools/automate/useSuggestedAutomations"; +import { useSavedAutomations } from "../../../hooks/tools/automate/useSavedAutomations"; interface AutomationSelectionProps { onSelectCustom: () => void; @@ -11,91 +13,34 @@ interface AutomationSelectionProps { onCreateNew: () => void; } -interface SavedAutomation { - id: string; - name: string; - description?: string; - operations: any[]; - createdAt: string; -} - export default function AutomationSelection({ onSelectCustom, onSelectSuggested, onCreateNew }: AutomationSelectionProps) { const { t } = useTranslation(); - const [savedAutomations, setSavedAutomations] = useState([]); - - // Load saved automations from IndexedDB - useEffect(() => { - loadSavedAutomations(); - }, []); - - const loadSavedAutomations = async () => { - try { - const { automationStorage } = await import("../../../services/automationStorage"); - const automations = await automationStorage.getAllAutomations(); - setSavedAutomations(automations); - } catch (error) { - console.error("Error loading saved automations:", error); - setSavedAutomations([]); - } - }; - - // Suggested automations - these are pre-defined common workflows - const suggestedAutomations = [ - { - 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: , - }, - { - 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: , - }, - { - 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: , - }, - ]; + const { savedAutomations } = useSavedAutomations(); + const suggestedAutomations = useSuggestedAutomations(); return ( - - {/* Create New Automation */} +
{t("automate.selection.saved.title", "Saved")} - + + + {/* Saved Automations */} {savedAutomations.map((automation) => ( - + typeof op === 'string' ? op : op.operation)} + onClick={() => onSelectCustom()} + /> ))} @@ -104,31 +49,18 @@ export default function AutomationSelection({ onSelectCustom, onSelectSuggested, {t("automate.selection.suggested.title", "Suggested")} - + {suggestedAutomations.map((automation) => ( - + /> ))}
+ ); } diff --git a/frontend/src/hooks/tools/automate/useSavedAutomations.ts b/frontend/src/hooks/tools/automate/useSavedAutomations.ts new file mode 100644 index 000000000..c52e4c784 --- /dev/null +++ b/frontend/src/hooks/tools/automate/useSavedAutomations.ts @@ -0,0 +1,55 @@ +import { useState, useEffect, useCallback } from 'react'; +import { AutomationConfig } from '../../../services/automationStorage'; + +export interface SavedAutomation extends AutomationConfig {} + +export function useSavedAutomations() { + const [savedAutomations, setSavedAutomations] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const loadSavedAutomations = useCallback(async () => { + try { + setLoading(true); + setError(null); + const { automationStorage } = await import('../../../services/automationStorage'); + const automations = await automationStorage.getAllAutomations(); + setSavedAutomations(automations); + } catch (err) { + console.error('Error loading saved automations:', err); + setError(err as Error); + setSavedAutomations([]); + } finally { + setLoading(false); + } + }, []); + + const refreshAutomations = useCallback(() => { + loadSavedAutomations(); + }, [loadSavedAutomations]); + + const deleteAutomation = useCallback(async (id: string) => { + try { + const { automationStorage } = await import('../../../services/automationStorage'); + await automationStorage.deleteAutomation(id); + // Refresh the list after deletion + refreshAutomations(); + } catch (err) { + console.error('Error deleting automation:', err); + throw err; + } + }, [refreshAutomations]); + + // Load automations on mount + useEffect(() => { + loadSavedAutomations(); + }, [loadSavedAutomations]); + + return { + savedAutomations, + loading, + error, + refreshAutomations, + deleteAutomation + }; +} \ No newline at end of file diff --git a/frontend/src/hooks/tools/automate/useSuggestedAutomations.ts b/frontend/src/hooks/tools/automate/useSuggestedAutomations.ts new file mode 100644 index 000000000..af81e65e5 --- /dev/null +++ b/frontend/src/hooks/tools/automate/useSuggestedAutomations.ts @@ -0,0 +1,41 @@ +import { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import StarIcon from '@mui/icons-material/Star'; + +export interface SuggestedAutomation { + id: string; + name: string; + description: string; + operations: string[]; + icon: React.ComponentType; +} + +export function useSuggestedAutomations(): SuggestedAutomation[] { + const { t } = useTranslation(); + + 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