mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-09-18 09:29:24 +00:00
Custom Icons
This commit is contained in:
parent
d8b03995c4
commit
5c5c2d452c
@ -13,6 +13,7 @@ import CheckIcon from '@mui/icons-material/Check';
|
|||||||
import { ToolRegistryEntry } from '../../../data/toolsTaxonomy';
|
import { ToolRegistryEntry } from '../../../data/toolsTaxonomy';
|
||||||
import ToolConfigurationModal from './ToolConfigurationModal';
|
import ToolConfigurationModal from './ToolConfigurationModal';
|
||||||
import ToolList from './ToolList';
|
import ToolList from './ToolList';
|
||||||
|
import IconSelector from './IconSelector';
|
||||||
import { AutomationConfig, AutomationMode, AutomationTool } from '../../../types/automation';
|
import { AutomationConfig, AutomationMode, AutomationTool } from '../../../types/automation';
|
||||||
import { useAutomationForm } from '../../../hooks/tools/automate/useAutomationForm';
|
import { useAutomationForm } from '../../../hooks/tools/automate/useAutomationForm';
|
||||||
|
|
||||||
@ -31,6 +32,8 @@ export default function AutomationCreation({ mode, existingAutomation, onBack, o
|
|||||||
const {
|
const {
|
||||||
automationName,
|
automationName,
|
||||||
setAutomationName,
|
setAutomationName,
|
||||||
|
automationIcon,
|
||||||
|
setAutomationIcon,
|
||||||
selectedTools,
|
selectedTools,
|
||||||
addTool,
|
addTool,
|
||||||
removeTool,
|
removeTool,
|
||||||
@ -101,6 +104,7 @@ export default function AutomationCreation({ mode, existingAutomation, onBack, o
|
|||||||
const automationData = {
|
const automationData = {
|
||||||
name: automationName.trim(),
|
name: automationName.trim(),
|
||||||
description: '',
|
description: '',
|
||||||
|
icon: automationIcon,
|
||||||
operations: selectedTools.map(tool => ({
|
operations: selectedTools.map(tool => ({
|
||||||
operation: tool.operation,
|
operation: tool.operation,
|
||||||
parameters: tool.parameters || {}
|
parameters: tool.parameters || {}
|
||||||
@ -149,18 +153,26 @@ export default function AutomationCreation({ mode, existingAutomation, onBack, o
|
|||||||
<Divider mb="md" />
|
<Divider mb="md" />
|
||||||
|
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
{/* Automation Name */}
|
{/* Automation Name and Icon */}
|
||||||
<Stack gap="xs">
|
<Group gap="xs" align="flex-start">
|
||||||
<Text size="sm" fw={500} mb="xs" style={{ color: "var(--mantine-color-text)" }}>
|
<Stack gap="xs" style={{ flex: 1 }}>
|
||||||
{t('automate.creation.name.label', 'Automation Name')} *
|
<Text size="sm" fw={500} mb="xs" style={{ color: "var(--mantine-color-text)" }}>
|
||||||
</Text>
|
{t('automate.creation.name.label', 'Automation Name')} *
|
||||||
<TextInput
|
</Text>
|
||||||
placeholder={t('automate.creation.name.placeholder', 'My Automation')}
|
<TextInput
|
||||||
value={automationName}
|
placeholder={t('automate.creation.name.placeholder', 'My Automation')}
|
||||||
onChange={(e) => setAutomationName(e.currentTarget.value)}
|
value={automationName}
|
||||||
size="sm"
|
onChange={(e) => setAutomationName(e.currentTarget.value)}
|
||||||
/>
|
size="sm"
|
||||||
</Stack>
|
/>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<IconSelector
|
||||||
|
value={automationIcon || 'SettingsIcon'}
|
||||||
|
onChange={setAutomationIcon}
|
||||||
|
size="sm"
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
|
||||||
{/* Selected Tools List */}
|
{/* Selected Tools List */}
|
||||||
|
@ -6,6 +6,7 @@ import SettingsIcon from "@mui/icons-material/Settings";
|
|||||||
import AutomationEntry from "./AutomationEntry";
|
import AutomationEntry from "./AutomationEntry";
|
||||||
import { useSuggestedAutomations } from "../../../hooks/tools/automate/useSuggestedAutomations";
|
import { useSuggestedAutomations } from "../../../hooks/tools/automate/useSuggestedAutomations";
|
||||||
import { AutomationConfig, SuggestedAutomation } from "../../../types/automation";
|
import { AutomationConfig, SuggestedAutomation } from "../../../types/automation";
|
||||||
|
import { iconMap } from './iconMap';
|
||||||
|
|
||||||
interface AutomationSelectionProps {
|
interface AutomationSelectionProps {
|
||||||
savedAutomations: AutomationConfig[];
|
savedAutomations: AutomationConfig[];
|
||||||
@ -42,18 +43,21 @@ export default function AutomationSelection({
|
|||||||
keepIconColor={true}
|
keepIconColor={true}
|
||||||
/>
|
/>
|
||||||
{/* Saved Automations */}
|
{/* Saved Automations */}
|
||||||
{savedAutomations.map((automation) => (
|
{savedAutomations.map((automation) => {
|
||||||
<AutomationEntry
|
const IconComponent = automation.icon ? iconMap[automation.icon as keyof typeof iconMap] : SettingsIcon;
|
||||||
key={automation.id}
|
return (
|
||||||
title={automation.name}
|
<AutomationEntry
|
||||||
badgeIcon={SettingsIcon}
|
key={automation.id}
|
||||||
operations={automation.operations.map(op => typeof op === 'string' ? op : op.operation)}
|
title={automation.name}
|
||||||
onClick={() => onRun(automation)}
|
badgeIcon={IconComponent || SettingsIcon}
|
||||||
showMenu={true}
|
operations={automation.operations.map(op => typeof op === 'string' ? op : op.operation)}
|
||||||
onEdit={() => onEdit(automation)}
|
onClick={() => onRun(automation)}
|
||||||
onDelete={() => onDelete(automation)}
|
showMenu={true}
|
||||||
/>
|
onEdit={() => onEdit(automation)}
|
||||||
))}
|
onDelete={() => onDelete(automation)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
<Divider pb='sm' />
|
<Divider pb='sm' />
|
||||||
|
|
||||||
{/* Suggested Automations */}
|
{/* Suggested Automations */}
|
||||||
|
117
frontend/src/components/tools/automate/IconSelector.tsx
Normal file
117
frontend/src/components/tools/automate/IconSelector.tsx
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Box, Text, Stack, Button, SimpleGrid, Tooltip, Popover } from "@mantine/core";
|
||||||
|
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
|
||||||
|
import { iconMap, iconOptions } from './iconMap';
|
||||||
|
|
||||||
|
interface IconSelectorProps {
|
||||||
|
value?: string;
|
||||||
|
onChange?: (iconKey: string) => void;
|
||||||
|
size?: "sm" | "md" | "lg";
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function IconSelector({ value = "SettingsIcon", onChange, size = "sm" }: IconSelectorProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||||
|
|
||||||
|
const selectedIconComponent = iconMap[value as keyof typeof iconMap] || iconMap.SettingsIcon;
|
||||||
|
|
||||||
|
const handleIconSelect = (iconKey: string) => {
|
||||||
|
onChange?.(iconKey);
|
||||||
|
setIsDropdownOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const iconSize = size === "sm" ? 16 : size === "md" ? 18 : 20;
|
||||||
|
const actionIconSize = size === "sm" ? "sm" : size === "md" ? "md" : "lg";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack gap="xs">
|
||||||
|
<Text size="sm" fw={500} mb="xs" style={{ color: "var(--mantine-color-text)" }}>
|
||||||
|
{t("automate.creation.icon.label", "Icon")}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Popover
|
||||||
|
opened={isDropdownOpen}
|
||||||
|
onClose={() => setIsDropdownOpen(false)}
|
||||||
|
onDismiss={() => setIsDropdownOpen(false)}
|
||||||
|
position="bottom-start"
|
||||||
|
withArrow
|
||||||
|
trapFocus
|
||||||
|
>
|
||||||
|
<Popover.Target>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size={size}
|
||||||
|
onClick={() => setIsDropdownOpen(!isDropdownOpen)}
|
||||||
|
style={{
|
||||||
|
width: size === "sm" ? "3.75rem" : "4.375rem",
|
||||||
|
position: "relative",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
paddingLeft: "0.5rem",
|
||||||
|
borderColor: "var(--mantine-color-gray-3)",
|
||||||
|
color: "var(--mantine-color-text)",
|
||||||
|
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{React.createElement(selectedIconComponent, { style: { fontSize: iconSize } })}
|
||||||
|
<KeyboardArrowDownIcon
|
||||||
|
style={{
|
||||||
|
fontSize: iconSize * 0.8,
|
||||||
|
position: "absolute",
|
||||||
|
right: "0.25rem",
|
||||||
|
top: "50%",
|
||||||
|
transform: "translateY(-50%)",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</Popover.Target>
|
||||||
|
|
||||||
|
<Popover.Dropdown>
|
||||||
|
<Stack gap="xs">
|
||||||
|
<SimpleGrid cols={4} spacing="xs">
|
||||||
|
{iconOptions.map((option) => {
|
||||||
|
const IconComponent = iconMap[option.value as keyof typeof iconMap];
|
||||||
|
const isSelected = value === option.value;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip key={option.value} label={option.label}>
|
||||||
|
<Box
|
||||||
|
onClick={() => handleIconSelect(option.value)}
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
padding: "0.5rem",
|
||||||
|
borderRadius: "0.25rem",
|
||||||
|
cursor: "pointer",
|
||||||
|
backgroundColor: isSelected ? "var(--mantine-color-gray-1)" : "transparent",
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => {
|
||||||
|
if (!isSelected) {
|
||||||
|
e.currentTarget.style.backgroundColor = "var(--mantine-color-gray-0)";
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
if (!isSelected) {
|
||||||
|
e.currentTarget.style.backgroundColor = "transparent";
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconComponent
|
||||||
|
style={{
|
||||||
|
fontSize: iconSize,
|
||||||
|
color: isSelected ? "var(--mantine-color-gray-9)" : "var(--mantine-color-gray-7)",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</SimpleGrid>
|
||||||
|
</Stack>
|
||||||
|
</Popover.Dropdown>
|
||||||
|
</Popover>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
92
frontend/src/components/tools/automate/iconMap.ts
Normal file
92
frontend/src/components/tools/automate/iconMap.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import SettingsIcon from '@mui/icons-material/Settings';
|
||||||
|
import CompressIcon from '@mui/icons-material/Compress';
|
||||||
|
import SwapHorizIcon from '@mui/icons-material/SwapHoriz';
|
||||||
|
import CleaningServicesIcon from '@mui/icons-material/CleaningServices';
|
||||||
|
import CropIcon from '@mui/icons-material/Crop';
|
||||||
|
import TextFieldsIcon from '@mui/icons-material/TextFields';
|
||||||
|
import PictureAsPdfIcon from '@mui/icons-material/PictureAsPdf';
|
||||||
|
import EditIcon from '@mui/icons-material/Edit';
|
||||||
|
import DeleteIcon from '@mui/icons-material/Delete';
|
||||||
|
import FolderIcon from '@mui/icons-material/Folder';
|
||||||
|
import CloudIcon from '@mui/icons-material/Cloud';
|
||||||
|
import StorageIcon from '@mui/icons-material/Storage';
|
||||||
|
import SearchIcon from '@mui/icons-material/Search';
|
||||||
|
import DownloadIcon from '@mui/icons-material/Download';
|
||||||
|
import UploadIcon from '@mui/icons-material/Upload';
|
||||||
|
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
|
||||||
|
import RotateLeftIcon from '@mui/icons-material/RotateLeft';
|
||||||
|
import RotateRightIcon from '@mui/icons-material/RotateRight';
|
||||||
|
import VisibilityIcon from '@mui/icons-material/Visibility';
|
||||||
|
import ContentCutIcon from '@mui/icons-material/ContentCut';
|
||||||
|
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
|
||||||
|
import WorkIcon from '@mui/icons-material/Work';
|
||||||
|
import BuildIcon from '@mui/icons-material/Build';
|
||||||
|
import AutoAwesomeIcon from '@mui/icons-material/AutoAwesome';
|
||||||
|
import SmartToyIcon from '@mui/icons-material/SmartToy';
|
||||||
|
import CheckIcon from '@mui/icons-material/Check';
|
||||||
|
import SecurityIcon from '@mui/icons-material/Security';
|
||||||
|
import StarIcon from '@mui/icons-material/Star';
|
||||||
|
|
||||||
|
export const iconMap = {
|
||||||
|
SettingsIcon,
|
||||||
|
CompressIcon,
|
||||||
|
SwapHorizIcon,
|
||||||
|
CleaningServicesIcon,
|
||||||
|
CropIcon,
|
||||||
|
TextFieldsIcon,
|
||||||
|
PictureAsPdfIcon,
|
||||||
|
EditIcon,
|
||||||
|
DeleteIcon,
|
||||||
|
FolderIcon,
|
||||||
|
CloudIcon,
|
||||||
|
StorageIcon,
|
||||||
|
SearchIcon,
|
||||||
|
DownloadIcon,
|
||||||
|
UploadIcon,
|
||||||
|
PlayArrowIcon,
|
||||||
|
RotateLeftIcon,
|
||||||
|
RotateRightIcon,
|
||||||
|
VisibilityIcon,
|
||||||
|
ContentCutIcon,
|
||||||
|
ContentCopyIcon,
|
||||||
|
WorkIcon,
|
||||||
|
BuildIcon,
|
||||||
|
AutoAwesomeIcon,
|
||||||
|
SmartToyIcon,
|
||||||
|
CheckIcon,
|
||||||
|
SecurityIcon,
|
||||||
|
StarIcon
|
||||||
|
};
|
||||||
|
|
||||||
|
export const iconOptions = [
|
||||||
|
{ value: 'SettingsIcon', label: 'Settings' },
|
||||||
|
{ value: 'CompressIcon', label: 'Compress' },
|
||||||
|
{ value: 'SwapHorizIcon', label: 'Convert' },
|
||||||
|
{ value: 'CleaningServicesIcon', label: 'Clean' },
|
||||||
|
{ value: 'CropIcon', label: 'Crop' },
|
||||||
|
{ value: 'TextFieldsIcon', label: 'Text' },
|
||||||
|
{ value: 'PictureAsPdfIcon', label: 'PDF' },
|
||||||
|
{ value: 'EditIcon', label: 'Edit' },
|
||||||
|
{ value: 'DeleteIcon', label: 'Delete' },
|
||||||
|
{ value: 'FolderIcon', label: 'Folder' },
|
||||||
|
{ value: 'CloudIcon', label: 'Cloud' },
|
||||||
|
{ value: 'StorageIcon', label: 'Storage' },
|
||||||
|
{ value: 'SearchIcon', label: 'Search' },
|
||||||
|
{ value: 'DownloadIcon', label: 'Download' },
|
||||||
|
{ value: 'UploadIcon', label: 'Upload' },
|
||||||
|
{ value: 'PlayArrowIcon', label: 'Play' },
|
||||||
|
{ value: 'RotateLeftIcon', label: 'Rotate Left' },
|
||||||
|
{ value: 'RotateRightIcon', label: 'Rotate Right' },
|
||||||
|
{ value: 'VisibilityIcon', label: 'View' },
|
||||||
|
{ value: 'ContentCutIcon', label: 'Cut' },
|
||||||
|
{ value: 'ContentCopyIcon', label: 'Copy' },
|
||||||
|
{ value: 'WorkIcon', label: 'Work' },
|
||||||
|
{ value: 'BuildIcon', label: 'Build' },
|
||||||
|
{ value: 'AutoAwesomeIcon', label: 'Magic' },
|
||||||
|
{ value: 'SmartToyIcon', label: 'Robot' },
|
||||||
|
{ value: 'CheckIcon', label: 'Check' },
|
||||||
|
{ value: 'SecurityIcon', label: 'Security' },
|
||||||
|
{ value: 'StarIcon', label: 'Star' }
|
||||||
|
];
|
||||||
|
|
||||||
|
export type IconKey = keyof typeof iconMap;
|
@ -14,6 +14,7 @@ export function useAutomationForm({ mode, existingAutomation, toolRegistry }: Us
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [automationName, setAutomationName] = useState('');
|
const [automationName, setAutomationName] = useState('');
|
||||||
|
const [automationIcon, setAutomationIcon] = useState<string>('');
|
||||||
const [selectedTools, setSelectedTools] = useState<AutomationTool[]>([]);
|
const [selectedTools, setSelectedTools] = useState<AutomationTool[]>([]);
|
||||||
|
|
||||||
const getToolName = useCallback((operation: string) => {
|
const getToolName = useCallback((operation: string) => {
|
||||||
@ -33,6 +34,7 @@ export function useAutomationForm({ mode, existingAutomation, toolRegistry }: Us
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if ((mode === AutomationMode.SUGGESTED || mode === AutomationMode.EDIT) && existingAutomation) {
|
if ((mode === AutomationMode.SUGGESTED || mode === AutomationMode.EDIT) && existingAutomation) {
|
||||||
setAutomationName(existingAutomation.name || '');
|
setAutomationName(existingAutomation.name || '');
|
||||||
|
setAutomationIcon(existingAutomation.icon || '');
|
||||||
|
|
||||||
const operations = existingAutomation.operations || [];
|
const operations = existingAutomation.operations || [];
|
||||||
const tools = operations.map((op, index) => {
|
const tools = operations.map((op, index) => {
|
||||||
@ -101,6 +103,8 @@ export function useAutomationForm({ mode, existingAutomation, toolRegistry }: Us
|
|||||||
return {
|
return {
|
||||||
automationName,
|
automationName,
|
||||||
setAutomationName,
|
setAutomationName,
|
||||||
|
automationIcon,
|
||||||
|
setAutomationIcon,
|
||||||
selectedTools,
|
selectedTools,
|
||||||
setSelectedTools,
|
setSelectedTools,
|
||||||
addTool,
|
addTool,
|
||||||
|
@ -45,10 +45,27 @@ export function useSavedAutomations() {
|
|||||||
try {
|
try {
|
||||||
const { automationStorage } = await import('../../../services/automationStorage');
|
const { automationStorage } = await import('../../../services/automationStorage');
|
||||||
|
|
||||||
|
// Map suggested automation icons to MUI icon keys
|
||||||
|
const getIconKey = (suggestedIcon: any): string => {
|
||||||
|
// Check the automation ID or name to determine the appropriate icon
|
||||||
|
switch (suggestedAutomation.id) {
|
||||||
|
case 'secure-pdf-ingestion':
|
||||||
|
case 'secure-workflow':
|
||||||
|
return 'SecurityIcon'; // Security icon for security workflows
|
||||||
|
case 'email-preparation':
|
||||||
|
return 'CompressIcon'; // Compression icon
|
||||||
|
case 'process-images':
|
||||||
|
return 'StarIcon'; // Star icon for process images
|
||||||
|
default:
|
||||||
|
return 'SettingsIcon'; // Default fallback
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Convert suggested automation to saved automation format
|
// Convert suggested automation to saved automation format
|
||||||
const savedAutomation = {
|
const savedAutomation = {
|
||||||
name: suggestedAutomation.name,
|
name: suggestedAutomation.name,
|
||||||
description: suggestedAutomation.description,
|
description: suggestedAutomation.description,
|
||||||
|
icon: getIconKey(suggestedAutomation.icon),
|
||||||
operations: suggestedAutomation.operations
|
operations: suggestedAutomation.operations
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ export interface AutomationConfig {
|
|||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
|
icon?: string;
|
||||||
operations: AutomationOperation[];
|
operations: AutomationOperation[];
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user