mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-26 06:09:23 +00:00
Clean up style of automation selection
This commit is contained in:
parent
9a2fd952b1
commit
49659a17c1
91
frontend/src/components/tools/automate/AutomationEntry.tsx
Normal file
91
frontend/src/components/tools/automate/AutomationEntry.tsx
Normal file
@ -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<any>;
|
||||
/** 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 (
|
||||
<Group gap="md" align="center" justify="flex-start" style={{ width: '100%' }}>
|
||||
{BadgeIcon && (
|
||||
<BadgeIcon
|
||||
style={{
|
||||
color: keepIconColor ? 'inherit' : 'var(--mantine-color-dimmed)'
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Text fw={600} size="sm" style={{ flex: 1, textAlign: 'left', color: 'var(--mantine-color-dimmed)' }}>
|
||||
{title}
|
||||
</Text>
|
||||
</Group>
|
||||
);
|
||||
} else {
|
||||
// Suggested automation showing tool chain
|
||||
return (
|
||||
<Group gap="md" align="center" justify="flex-start" style={{ width: '100%' }}>
|
||||
{BadgeIcon && (
|
||||
<BadgeIcon
|
||||
style={{
|
||||
color: keepIconColor ? 'inherit' : 'var(--mantine-color-dimmed)'
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Group gap="xs" justify="flex-start" style={{ flex: 1 }}>
|
||||
{operations.map((op, index) => (
|
||||
<React.Fragment key={`${op}-${index}`}>
|
||||
<Badge size="xs" variant="outline" style={{ color: 'var(--mantine-color-dimmed)', borderColor: 'var(--mantine-color-dimmed)' }}>
|
||||
{String(t(`${op}.title`, op))}
|
||||
</Badge>
|
||||
{index < operations.length - 1 && (
|
||||
<Text size="xs" c="dimmed" style={{ color: 'var(--mantine-color-dimmed)' }}>
|
||||
→
|
||||
</Text>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</Group>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="subtle"
|
||||
fullWidth
|
||||
onClick={onClick}
|
||||
style={{
|
||||
height: 'auto',
|
||||
padding: '0.75rem 1rem',
|
||||
justifyContent: 'flex-start',
|
||||
display: 'flex'
|
||||
}}
|
||||
>
|
||||
<div style={{ width: '100%', display: 'flex', justifyContent: 'flex-start' }}>
|
||||
{renderContent()}
|
||||
</div>
|
||||
</Button>
|
||||
);
|
||||
}
|
@ -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<SavedAutomation[]>([]);
|
||||
|
||||
// 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: <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 />,
|
||||
},
|
||||
];
|
||||
const { savedAutomations } = useSavedAutomations();
|
||||
const suggestedAutomations = useSuggestedAutomations();
|
||||
|
||||
return (
|
||||
<Stack gap="xl">
|
||||
{/* Create New Automation */}
|
||||
<div>
|
||||
<Title order={3} size="h4" mb="md">
|
||||
{t("automate.selection.saved.title", "Saved")}
|
||||
</Title>
|
||||
<Button variant="subtle" onClick={onCreateNew}>
|
||||
<Group gap="md" align="center">
|
||||
<AddIcon color="primary" />
|
||||
<Text fw={600}>{t("automate.selection.createNew.title", "Create New Automation")}</Text>
|
||||
</Group>
|
||||
</Button>
|
||||
|
||||
<Stack gap="xs">
|
||||
<AutomationEntry
|
||||
title={t("automate.selection.createNew.title", "Create New Automation")}
|
||||
badgeIcon={AddCircleOutline}
|
||||
operations={[]}
|
||||
onClick={onCreateNew}
|
||||
keepIconColor={true}
|
||||
/>
|
||||
{/* Saved Automations */}
|
||||
{savedAutomations.map((automation) => (
|
||||
<Button variant="subtle" fullWidth={true} onClick={() => onSelectCustom()}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<Group gap="xs">
|
||||
{automation.operations.map((op: any, index: number) => (
|
||||
<React.Fragment key={`${op.operation || op}-${index}`}>
|
||||
<Badge size="xs" variant="outline">
|
||||
{String(t(`tools.${op.operation || op}.name`, op.operation || op))}
|
||||
</Badge>
|
||||
{index < automation.operations.length - 1 && (
|
||||
<Text size="xs" c="dimmed">
|
||||
→
|
||||
</Text>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</Group>
|
||||
</div>
|
||||
</Button>
|
||||
<AutomationEntry
|
||||
key={automation.id}
|
||||
title={automation.name}
|
||||
badgeIcon={SettingsIcon}
|
||||
operations={automation.operations.map(op => typeof op === 'string' ? op : op.operation)}
|
||||
onClick={() => onSelectCustom()}
|
||||
/>
|
||||
))}
|
||||
<Divider />
|
||||
|
||||
@ -104,31 +49,18 @@ export default function AutomationSelection({ onSelectCustom, onSelectSuggested,
|
||||
<Title order={3} size="h4" mb="md">
|
||||
{t("automate.selection.suggested.title", "Suggested")}
|
||||
</Title>
|
||||
<Stack gap="md">
|
||||
<Stack gap="xs">
|
||||
{suggestedAutomations.map((automation) => (
|
||||
<Button
|
||||
size="md"
|
||||
variant="subtle"
|
||||
fullWidth={true}
|
||||
<AutomationEntry
|
||||
key={automation.id}
|
||||
badgeIcon={automation.icon}
|
||||
operations={automation.operations}
|
||||
onClick={() => onSelectSuggested(automation)}
|
||||
style={{ paddingLeft: "0" }}
|
||||
>
|
||||
<Group gap="xs">
|
||||
{automation.operations.map((op, index) => (
|
||||
<React.Fragment key={op}>
|
||||
{t(`${op}.title`, op)}
|
||||
{index < automation.operations.length - 1 && (
|
||||
<Text size="xs" c="dimmed">
|
||||
→
|
||||
</Text>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</Group>
|
||||
</Button>
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
</div>
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
55
frontend/src/hooks/tools/automate/useSavedAutomations.ts
Normal file
55
frontend/src/hooks/tools/automate/useSavedAutomations.ts
Normal file
@ -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<SavedAutomation[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(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
|
||||
};
|
||||
}
|
41
frontend/src/hooks/tools/automate/useSuggestedAutomations.ts
Normal file
41
frontend/src/hooks/tools/automate/useSuggestedAutomations.ts
Normal file
@ -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<any>;
|
||||
}
|
||||
|
||||
export function useSuggestedAutomations(): SuggestedAutomation[] {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const suggestedAutomations = useMemo<SuggestedAutomation[]>(() => [
|
||||
{
|
||||
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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user