Custom Icons

This commit is contained in:
Connor Yoh 2025-08-26 12:21:48 +01:00
parent d8b03995c4
commit 5c5c2d452c
7 changed files with 271 additions and 24 deletions

View File

@ -13,6 +13,7 @@ import CheckIcon from '@mui/icons-material/Check';
import { ToolRegistryEntry } from '../../../data/toolsTaxonomy';
import ToolConfigurationModal from './ToolConfigurationModal';
import ToolList from './ToolList';
import IconSelector from './IconSelector';
import { AutomationConfig, AutomationMode, AutomationTool } from '../../../types/automation';
import { useAutomationForm } from '../../../hooks/tools/automate/useAutomationForm';
@ -31,6 +32,8 @@ export default function AutomationCreation({ mode, existingAutomation, onBack, o
const {
automationName,
setAutomationName,
automationIcon,
setAutomationIcon,
selectedTools,
addTool,
removeTool,
@ -101,6 +104,7 @@ export default function AutomationCreation({ mode, existingAutomation, onBack, o
const automationData = {
name: automationName.trim(),
description: '',
icon: automationIcon,
operations: selectedTools.map(tool => ({
operation: tool.operation,
parameters: tool.parameters || {}
@ -149,8 +153,9 @@ export default function AutomationCreation({ mode, existingAutomation, onBack, o
<Divider mb="md" />
<Stack gap="md">
{/* Automation Name */}
<Stack gap="xs">
{/* Automation Name and Icon */}
<Group gap="xs" align="flex-start">
<Stack gap="xs" style={{ flex: 1 }}>
<Text size="sm" fw={500} mb="xs" style={{ color: "var(--mantine-color-text)" }}>
{t('automate.creation.name.label', 'Automation Name')} *
</Text>
@ -162,6 +167,13 @@ export default function AutomationCreation({ mode, existingAutomation, onBack, o
/>
</Stack>
<IconSelector
value={automationIcon || 'SettingsIcon'}
onChange={setAutomationIcon}
size="sm"
/>
</Group>
{/* Selected Tools List */}
{selectedTools.length > 0 && (

View File

@ -6,6 +6,7 @@ import SettingsIcon from "@mui/icons-material/Settings";
import AutomationEntry from "./AutomationEntry";
import { useSuggestedAutomations } from "../../../hooks/tools/automate/useSuggestedAutomations";
import { AutomationConfig, SuggestedAutomation } from "../../../types/automation";
import { iconMap } from './iconMap';
interface AutomationSelectionProps {
savedAutomations: AutomationConfig[];
@ -42,18 +43,21 @@ export default function AutomationSelection({
keepIconColor={true}
/>
{/* Saved Automations */}
{savedAutomations.map((automation) => (
{savedAutomations.map((automation) => {
const IconComponent = automation.icon ? iconMap[automation.icon as keyof typeof iconMap] : SettingsIcon;
return (
<AutomationEntry
key={automation.id}
title={automation.name}
badgeIcon={SettingsIcon}
badgeIcon={IconComponent || SettingsIcon}
operations={automation.operations.map(op => typeof op === 'string' ? op : op.operation)}
onClick={() => onRun(automation)}
showMenu={true}
onEdit={() => onEdit(automation)}
onDelete={() => onDelete(automation)}
/>
))}
);
})}
<Divider pb='sm' />
{/* Suggested Automations */}

View 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>
);
}

View 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;

View File

@ -14,6 +14,7 @@ export function useAutomationForm({ mode, existingAutomation, toolRegistry }: Us
const { t } = useTranslation();
const [automationName, setAutomationName] = useState('');
const [automationIcon, setAutomationIcon] = useState<string>('');
const [selectedTools, setSelectedTools] = useState<AutomationTool[]>([]);
const getToolName = useCallback((operation: string) => {
@ -33,6 +34,7 @@ export function useAutomationForm({ mode, existingAutomation, toolRegistry }: Us
useEffect(() => {
if ((mode === AutomationMode.SUGGESTED || mode === AutomationMode.EDIT) && existingAutomation) {
setAutomationName(existingAutomation.name || '');
setAutomationIcon(existingAutomation.icon || '');
const operations = existingAutomation.operations || [];
const tools = operations.map((op, index) => {
@ -101,6 +103,8 @@ export function useAutomationForm({ mode, existingAutomation, toolRegistry }: Us
return {
automationName,
setAutomationName,
automationIcon,
setAutomationIcon,
selectedTools,
setSelectedTools,
addTool,

View File

@ -45,10 +45,27 @@ export function useSavedAutomations() {
try {
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
const savedAutomation = {
name: suggestedAutomation.name,
description: suggestedAutomation.description,
icon: getIconKey(suggestedAutomation.icon),
operations: suggestedAutomation.operations
};

View File

@ -11,6 +11,7 @@ export interface AutomationConfig {
id: string;
name: string;
description?: string;
icon?: string;
operations: AutomationOperation[];
createdAt: string;
updatedAt: string;