AutomateFixes (#4281)

can edit automations
drop down styles
drop down bug fixes

---------

Co-authored-by: Connor Yoh <connor@stirlingpdf.com>
This commit is contained in:
ConnorYoh 2025-08-26 09:44:30 +01:00 committed by GitHub
parent 42d7664e25
commit fe9d2367d5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 264 additions and 174 deletions

View File

@ -31,6 +31,8 @@ export interface TextInputProps {
readOnly?: boolean;
/** Accessibility label */
'aria-label'?: string;
/** Focus event handler */
onFocus?: () => void;
}
export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(({
@ -46,6 +48,7 @@ export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(({
disabled = false,
readOnly = false,
'aria-label': ariaLabel,
onFocus,
...props
}, ref) => {
const { colorScheme } = useMantineColorScheme();
@ -63,7 +66,7 @@ export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(({
return (
<div className={`${styles.container} ${className}`} style={style}>
{icon && (
<span
<span
className={styles.icon}
style={{ color: colorScheme === 'dark' ? '#FFFFFF' : '#6B7382' }}
>
@ -81,6 +84,7 @@ export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(({
disabled={disabled}
readOnly={readOnly}
aria-label={ariaLabel}
onFocus={onFocus}
style={{
backgroundColor: colorScheme === 'dark' ? '#4B525A' : '#FFFFFF',
color: colorScheme === 'dark' ? '#FFFFFF' : '#6B7382',

View File

@ -98,7 +98,7 @@ export default function AutomationCreation({ mode, existingAutomation, onBack, o
const saveAutomation = async () => {
if (!canSaveAutomation()) return;
const automation = {
const automationData = {
name: automationName.trim(),
description: '',
operations: selectedTools.map(tool => ({
@ -109,7 +109,30 @@ export default function AutomationCreation({ mode, existingAutomation, onBack, o
try {
const { automationStorage } = await import('../../../services/automationStorage');
const savedAutomation = await automationStorage.saveAutomation(automation);
let savedAutomation;
if (mode === AutomationMode.EDIT && existingAutomation) {
// For edit mode, check if name has changed
const nameChanged = automationName.trim() !== existingAutomation.name;
if (nameChanged) {
// Name changed - create new automation
savedAutomation = await automationStorage.saveAutomation(automationData);
} else {
// Name unchanged - update existing automation
const updatedAutomation = {
...existingAutomation,
...automationData,
id: existingAutomation.id,
createdAt: existingAutomation.createdAt
};
savedAutomation = await automationStorage.updateAutomation(updatedAutomation);
}
} else {
// Create mode - always create new automation
savedAutomation = await automationStorage.saveAutomation(automationData);
}
onComplete(savedAutomation);
} catch (error) {
console.error('Error saving automation:', error);

View File

@ -1,14 +1,13 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Text, Stack, Group, ActionIcon } from '@mantine/core';
import DeleteIcon from '@mui/icons-material/Delete';
import SettingsIcon from '@mui/icons-material/Settings';
import CloseIcon from '@mui/icons-material/Close';
import AddCircleOutline from '@mui/icons-material/AddCircleOutline';
import { AutomationTool } from '../../../types/automation';
import { ToolRegistryEntry } from '../../../data/toolsTaxonomy';
import ToolSelector from './ToolSelector';
import AutomationEntry from './AutomationEntry';
import React from "react";
import { useTranslation } from "react-i18next";
import { Text, Stack, Group, ActionIcon } from "@mantine/core";
import SettingsIcon from "@mui/icons-material/Settings";
import CloseIcon from "@mui/icons-material/Close";
import AddCircleOutline from "@mui/icons-material/AddCircleOutline";
import { AutomationTool } from "../../../types/automation";
import { ToolRegistryEntry } from "../../../data/toolsTaxonomy";
import ToolSelector from "./ToolSelector";
import AutomationEntry from "./AutomationEntry";
interface ToolListProps {
tools: AutomationTool[];
@ -29,35 +28,39 @@ export default function ToolList({
onToolConfigure,
onToolAdd,
getToolName,
getToolDefaultParameters
getToolDefaultParameters,
}: ToolListProps) {
const { t } = useTranslation();
const handleToolSelect = (index: number, newOperation: string) => {
const defaultParams = getToolDefaultParameters(newOperation);
onToolUpdate(index, {
operation: newOperation,
name: getToolName(newOperation),
configured: false,
parameters: defaultParams
parameters: defaultParams,
});
};
return (
<div>
<Text size="sm" fw={500} mb="xs" style={{ color: 'var(--mantine-color-text)' }}>
{t('automate.creation.tools.selected', 'Selected Tools')} ({tools.length})
<Text size="sm" fw={500} mb="xs" style={{ color: "var(--mantine-color-text)" }}>
{t("automate.creation.tools.selected", "Selected Tools")} ({tools.length})
</Text>
<Stack gap="0">
{tools.map((tool, index) => (
<React.Fragment key={tool.id}>
<div
style={{
border: '1px solid var(--mantine-color-gray-2)',
borderRadius: 'var(--mantine-radius-sm)',
position: 'relative',
padding: 'var(--mantine-spacing-xs)'
border: "1px solid var(--mantine-color-gray-2)",
borderRadius: tool.operation && !tool.configured
? "var(--mantine-radius-lg) var(--mantine-radius-lg) 0 0"
: "var(--mantine-radius-lg)",
backgroundColor: "var(--mantine-color-gray-2)",
position: "relative",
padding: "var(--mantine-spacing-xs)",
borderBottomWidth: tool.operation && !tool.configured ? "0" : "1px",
}}
>
{/* Delete X in top right */}
@ -65,26 +68,26 @@ export default function ToolList({
variant="subtle"
size="xs"
onClick={() => onToolRemove(index)}
title={t('automate.creation.tools.remove', 'Remove tool')}
title={t("automate.creation.tools.remove", "Remove tool")}
style={{
position: 'absolute',
top: '4px',
right: '4px',
position: "absolute",
top: "4px",
right: "4px",
zIndex: 1,
color: 'var(--mantine-color-gray-6)'
color: "var(--mantine-color-gray-6)",
}}
>
<CloseIcon style={{ fontSize: 12 }} />
<CloseIcon style={{ fontSize: 16 }} />
</ActionIcon>
<div style={{ paddingRight: '1.25rem' }}>
<div style={{ paddingRight: "1.25rem" }}>
{/* Tool Selection Dropdown with inline settings cog */}
<Group gap="xs" align="center" wrap="nowrap">
<div style={{ flex: 1, minWidth: 0 }}>
<ToolSelector
key={`tool-selector-${tool.id}`}
onSelect={(newOperation) => handleToolSelect(index, newOperation)}
excludeTools={['automate']}
excludeTools={["automate"]}
toolRegistry={toolRegistry}
selectedValue={tool.operation}
placeholder={tool.name}
@ -97,26 +100,37 @@ export default function ToolList({
variant="subtle"
size="sm"
onClick={() => onToolConfigure(index)}
title={t('automate.creation.tools.configure', 'Configure tool')}
style={{ color: 'var(--mantine-color-gray-6)' }}
title={t("automate.creation.tools.configure", "Configure tool")}
style={{ color: "var(--mantine-color-gray-6)" }}
>
<SettingsIcon style={{ fontSize: 16 }} />
</ActionIcon>
)}
</Group>
{/* Configuration status underneath */}
{tool.operation && !tool.configured && (
<Text pl="md" size="xs" c="dimmed" mt="xs">
{t('automate.creation.tools.notConfigured', "! Not Configured")}
</Text>
)}
</div>
</div>
{/* Configuration status underneath */}
{tool.operation && !tool.configured && (
<div
style={{
width: "100%",
border: "1px solid var(--mantine-color-gray-2)",
borderTop: "none",
borderRadius: "0 0 var(--mantine-radius-lg) var(--mantine-radius-lg)",
backgroundColor: "var(--active-bg)",
padding: "var(--mantine-spacing-xs)",
}}
>
<Text pl="md" size="xs" >
{t("automate.creation.tools.notConfigured", "! Not Configured")}
</Text>
</div>
)}
{index < tools.length - 1 && (
<div style={{ textAlign: 'center', padding: '8px 0' }}>
<Text size="xs" c="dimmed"></Text>
<div style={{ textAlign: "center", padding: "8px 0" }}>
<Text size="xs" c="dimmed">
</Text>
</div>
)}
</React.Fragment>
@ -124,19 +138,23 @@ export default function ToolList({
{/* Arrow before Add Tool Button */}
{tools.length > 0 && (
<div style={{ textAlign: 'center', padding: '8px 0' }}>
<Text size="xs" c="dimmed"></Text>
<div style={{ textAlign: "center", padding: "8px 0" }}>
<Text size="xs" c="dimmed">
</Text>
</div>
)}
{/* Add Tool Button */}
<div style={{
border: '1px solid var(--mantine-color-gray-2)',
borderRadius: 'var(--mantine-radius-sm)',
overflow: 'hidden'
}}>
<div
style={{
border: "1px solid var(--mantine-color-gray-2)",
borderRadius: "var(--mantine-radius-sm)",
overflow: "hidden",
}}
>
<AutomationEntry
title={t('automate.creation.tools.addTool', 'Add Tool')}
title={t("automate.creation.tools.addTool", "Add Tool")}
badgeIcon={AddCircleOutline}
operations={[]}
onClick={onToolAdd}
@ -146,4 +164,4 @@ export default function ToolList({
</Stack>
</div>
);
}
}

View File

@ -1,10 +1,11 @@
import React, { useState, useMemo, useCallback } from 'react';
import React, { useState, useMemo, useCallback, useRef, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { Menu, Stack, Text, ScrollArea } from '@mantine/core';
import { Stack, Text, ScrollArea } from '@mantine/core';
import { ToolRegistryEntry } from '../../../data/toolsTaxonomy';
import { useToolSections } from '../../../hooks/useToolSections';
import { renderToolButtons } from '../shared/renderToolButtons';
import ToolSearch from '../toolPicker/ToolSearch';
import ToolButton from '../toolPicker/ToolButton';
interface ToolSelectorProps {
onSelect: (toolKey: string) => void;
@ -24,6 +25,8 @@ export default function ToolSelector({
const { t } = useTranslation();
const [opened, setOpened] = useState(false);
const [searchTerm, setSearchTerm] = useState('');
const [shouldAutoFocus, setShouldAutoFocus] = useState(false);
const containerRef = useRef<HTMLDivElement>(null);
// Filter out excluded tools (like 'automate' itself)
const baseFilteredTools = useMemo(() => {
@ -66,13 +69,21 @@ export default function ToolSelector({
}
if (!sections || sections.length === 0) {
// If no sections, create a simple group from filtered tools
if (baseFilteredTools.length > 0) {
return [{
name: 'All Tools',
subcategoryId: 'all' as any,
tools: baseFilteredTools.map(([key, tool]) => ({ id: key, tool }))
}];
}
return [];
}
// Find the "all" section which contains all tools without duplicates
const allSection = sections.find(s => (s as any).key === 'all');
return allSection?.subcategories || [];
}, [isSearching, searchGroups, sections]);
}, [isSearching, searchGroups, sections, baseFilteredTools]);
const handleToolSelect = useCallback((toolKey: string) => {
onSelect(toolKey);
@ -88,8 +99,25 @@ export default function ToolSelector({
const handleSearchFocus = () => {
setOpened(true);
setShouldAutoFocus(true); // Request auto-focus for the input
};
// Handle click outside to close dropdown
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
setOpened(false);
setSearchTerm('');
}
};
if (opened) {
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}
}, [opened]);
const handleSearchChange = (value: string) => {
setSearchTerm(value);
if (!opened) {
@ -97,6 +125,14 @@ export default function ToolSelector({
}
};
const handleInputFocus = () => {
if (!opened) {
setOpened(true);
}
// Clear auto-focus flag since input is now focused
setShouldAutoFocus(false);
};
// Get display value for selected tool
const getDisplayValue = () => {
if (selectedValue && toolRegistry[selectedValue]) {
@ -106,77 +142,63 @@ export default function ToolSelector({
};
return (
<div style={{ position: 'relative', width: '100%' }}>
<Menu
opened={opened}
onChange={(isOpen) => {
setOpened(isOpen);
// Clear search term when menu closes to show proper display
if (!isOpen) {
setSearchTerm('');
}
}}
closeOnClickOutside={true}
closeOnEscape={true}
position="bottom-start"
offset={4}
withinPortal={false}
trapFocus={false}
shadow="sm"
transitionProps={{ duration: 0 }}
>
<Menu.Target>
<div style={{ width: '100%' }}>
{selectedValue && toolRegistry[selectedValue] && !opened ? (
// Show selected tool in AutomationEntry style when tool is selected and not searching
<div onClick={handleSearchFocus} style={{ cursor: 'pointer' }}>
<div style={{
display: 'flex',
alignItems: 'center',
gap: 'var(--mantine-spacing-sm)',
padding: '0 0.5rem',
borderRadius: 'var(--mantine-radius-sm)',
}}>
<div style={{ color: 'var(--mantine-color-text)', fontSize: '1.2rem' }}>
{toolRegistry[selectedValue].icon}
</div>
<Text size="sm" style={{ flex: 1, color: 'var(--mantine-color-text)' }}>
{toolRegistry[selectedValue].name}
</Text>
</div>
</div>
) : (
// Show search input when no tool selected or actively searching
<ToolSearch
value={searchTerm}
onChange={handleSearchChange}
toolRegistry={filteredToolRegistry}
mode="filter"
placeholder={getDisplayValue()}
hideIcon={true}
onFocus={handleSearchFocus}
/>
)}
</div>
</Menu.Target>
<div ref={containerRef} className='rounded-xl'>
{/* Always show the target - either selected tool or search input */}
<Menu.Dropdown p={0} style={{ minWidth: '16rem' }}>
<ScrollArea h={350}>
<Stack gap="sm" p="sm">
{displayGroups.length === 0 ? (
<Text size="sm" c="dimmed" ta="center" p="md">
{isSearching
? t('tools.noSearchResults', 'No tools found')
: t('tools.noTools', 'No tools available')
}
</Text>
) : (
renderedTools
)}
</Stack>
</ScrollArea>
</Menu.Dropdown>
</Menu>
{selectedValue && toolRegistry[selectedValue] && !opened ? (
// Show selected tool in AutomationEntry style when tool is selected and dropdown closed
<div onClick={handleSearchFocus} style={{ cursor: 'pointer',
borderRadius: "var(--mantine-radius-lg)" }}>
<ToolButton id='tool' tool={toolRegistry[selectedValue]} isSelected={false}
onSelect={()=>{}} rounded={true}></ToolButton>
</div>
) : (
// Show search input when no tool selected OR when dropdown is opened
<ToolSearch
value={searchTerm}
onChange={handleSearchChange}
toolRegistry={filteredToolRegistry}
mode="unstyled"
placeholder={getDisplayValue()}
hideIcon={true}
onFocus={handleInputFocus}
autoFocus={shouldAutoFocus}
/>
)}
{/* Custom dropdown */}
{opened && (
<div
style={{
position: 'absolute',
top: '100%',
left: 0,
right: 0,
zIndex: 1000,
backgroundColor: 'var(--mantine-color-body)',
border: '1px solid var(--mantine-color-gray-3)',
borderRadius: 'var(--mantine-radius-sm)',
boxShadow: 'var(--mantine-shadow-sm)',
marginTop: '4px',
minWidth: '16rem'
}}
>
<ScrollArea h={350}>
<Stack gap="sm" p="sm">
{displayGroups.length === 0 ? (
<Text size="sm" c="dimmed" ta="center" p="md">
{isSearching
? t('tools.noSearchResults', 'No tools found')
: t('tools.noTools', 'No tools available')
}
</Text>
) : (
renderedTools
)}
</Stack>
</ScrollArea>
</div>
)}
</div>
);
}

View File

@ -9,9 +9,10 @@ interface ToolButtonProps {
tool: ToolRegistryEntry;
isSelected: boolean;
onSelect: (id: string) => void;
rounded?: boolean;
}
const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect }) => {
const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect, rounded = false }) => {
const handleClick = (id: string) => {
if (tool.link) {
// Open external link in new tab
@ -33,7 +34,17 @@ const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect
fullWidth
justify="flex-start"
className="tool-button"
styles={{ root: { borderRadius: 0, color: "var(--tools-text-and-icon-color)" } }}
styles={{
root: {
borderRadius: rounded ? 'var(--mantine-radius-lg)' : 0,
color: "var(--tools-text-and-icon-color)",
...(rounded && {
'&:hover': {
borderRadius: 'var(--mantine-radius-lg)',
}
})
}
}}
>
<FitText
text={tool.name}

View File

@ -76,4 +76,4 @@
.search-input-container {
margin-top: 0.5rem;
margin-bottom: 0.5rem;
}
}

View File

@ -4,18 +4,19 @@ import { useTranslation } from "react-i18next";
import LocalIcon from '../../shared/LocalIcon';
import { ToolRegistryEntry } from "../../../data/toolsTaxonomy";
import { TextInput } from "../../shared/TextInput";
import './ToolPicker.css';
import "./ToolPicker.css";
interface ToolSearchProps {
value: string;
onChange: (value: string) => void;
toolRegistry: Readonly<Record<string, ToolRegistryEntry>>;
onToolSelect?: (toolId: string) => void;
mode: 'filter' | 'dropdown';
mode: "filter" | "dropdown" | "unstyled";
selectedToolKey?: string | null;
placeholder?: string;
hideIcon?: boolean;
onFocus?: () => void;
autoFocus?: boolean;
}
const ToolSearch = ({
@ -23,11 +24,12 @@ const ToolSearch = ({
onChange,
toolRegistry,
onToolSelect,
mode = 'filter',
mode = "filter",
selectedToolKey,
placeholder,
hideIcon = false,
onFocus
onFocus,
autoFocus = false,
}: ToolSearchProps) => {
const { t } = useTranslation();
const [dropdownOpen, setDropdownOpen] = useState(false);
@ -38,9 +40,10 @@ const ToolSearch = ({
if (!value.trim()) return [];
return Object.entries(toolRegistry)
.filter(([id, tool]) => {
if (mode === 'dropdown' && id === selectedToolKey) return false;
return tool.name.toLowerCase().includes(value.toLowerCase()) ||
tool.description.toLowerCase().includes(value.toLowerCase());
if (mode === "dropdown" && id === selectedToolKey) return false;
return (
tool.name.toLowerCase().includes(value.toLowerCase()) || tool.description.toLowerCase().includes(value.toLowerCase())
);
})
.slice(0, 6)
.map(([id, tool]) => ({ id, tool }));
@ -48,7 +51,7 @@ const ToolSearch = ({
const handleSearchChange = (searchValue: string) => {
onChange(searchValue);
if (mode === 'dropdown') {
if (mode === "dropdown") {
setDropdownOpen(searchValue.trim().length > 0 && filteredTools.length > 0);
}
};
@ -64,12 +67,20 @@ const ToolSearch = ({
setDropdownOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
// Auto-focus the input when requested
useEffect(() => {
if (autoFocus && searchRef.current) {
setTimeout(() => {
searchRef.current?.focus();
}, 10);
}
}, [autoFocus]);
const searchInput = (
<div className="search-input-container">
<TextInput
ref={searchRef}
value={value}
@ -77,36 +88,39 @@ const ToolSearch = ({
placeholder={placeholder || t("toolPicker.searchPlaceholder", "Search tools...")}
icon={hideIcon ? undefined : <LocalIcon icon="search-rounded" width="1.5rem" height="1.5rem" />}
autoComplete="off"
onFocus={onFocus}
/>
</div>
);
if (mode === 'filter') {
if (mode === "filter") {
return <div className="search-input-container">{searchInput}</div>;
}
if (mode === "unstyled") {
return searchInput;
}
return (
<div ref={searchRef} style={{ position: 'relative' }}>
<div ref={searchRef} style={{ position: "relative" }}>
{searchInput}
{dropdownOpen && filteredTools.length > 0 && (
<div
ref={dropdownRef}
style={{
position: 'absolute',
top: '100%',
position: "absolute",
top: "100%",
left: 0,
right: 0,
zIndex: 1000,
backgroundColor: 'var(--mantine-color-body)',
border: '1px solid var(--mantine-color-gray-3)',
borderRadius: '6px',
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
maxHeight: '300px',
overflowY: 'auto'
backgroundColor: "var(--mantine-color-body)",
border: "1px solid var(--mantine-color-gray-3)",
borderRadius: "6px",
boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1)",
maxHeight: "300px",
overflowY: "auto",
}}
>
<Stack gap="xs" style={{ padding: '8px' }}>
<Stack gap="xs" style={{ padding: "8px" }}>
{filteredTools.map(({ id, tool }) => (
<Button
key={id}
@ -115,22 +129,18 @@ const ToolSearch = ({
onToolSelect && onToolSelect(id);
setDropdownOpen(false);
}}
leftSection={
<div style={{ color: 'var(--tools-text-and-icon-color)' }}>
{tool.icon}
</div>
}
leftSection={<div style={{ color: "var(--tools-text-and-icon-color)" }}>{tool.icon}</div>}
fullWidth
justify="flex-start"
style={{
borderRadius: '6px',
color: 'var(--tools-text-and-icon-color)',
padding: '8px 12px'
borderRadius: "6px",
color: "var(--tools-text-and-icon-color)",
padding: "8px 12px",
}}
>
<div style={{ textAlign: 'left' }}>
<div style={{ textAlign: "left" }}>
<div style={{ fontWeight: 500 }}>{tool.name}</div>
<Text size="xs" c="dimmed" style={{ marginTop: '2px' }}>
<Text size="xs" c="dimmed" style={{ marginTop: "2px" }}>
{tool.description}
</Text>
</div>

View File

@ -1,4 +1,4 @@
import { useState, useEffect } from 'react';
import { useState, useEffect, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { AutomationTool, AutomationConfig, AutomationMode } from '../../../types/automation';
import { AUTOMATION_CONSTANTS } from '../../../constants/automation';
@ -16,18 +16,18 @@ export function useAutomationForm({ mode, existingAutomation, toolRegistry }: Us
const [automationName, setAutomationName] = useState('');
const [selectedTools, setSelectedTools] = useState<AutomationTool[]>([]);
const getToolName = (operation: string) => {
const getToolName = useCallback((operation: string) => {
const tool = toolRegistry?.[operation] as any;
return tool?.name || t(`tools.${operation}.name`, operation);
};
}, [toolRegistry, t]);
const getToolDefaultParameters = (operation: string): Record<string, any> => {
const getToolDefaultParameters = useCallback((operation: string): Record<string, any> => {
const config = toolRegistry[operation]?.operationConfig;
if (config?.defaultParameters) {
return { ...config.defaultParameters };
}
return {};
};
}, [toolRegistry]);
// Initialize based on mode and existing automation
useEffect(() => {
@ -58,7 +58,7 @@ export function useAutomationForm({ mode, existingAutomation, toolRegistry }: Us
}));
setSelectedTools(defaultTools);
}
}, [mode, existingAutomation, selectedTools.length, t, getToolName]);
}, [mode, existingAutomation, t, getToolName]);
const addTool = (operation: string) => {
const newTool: AutomationTool = {

View File

@ -14,7 +14,7 @@ import { useAutomateOperation } from "../hooks/tools/automate/useAutomateOperati
import { BaseToolProps } from "../types/tool";
import { useFlatToolRegistry } from "../data/useTranslatedToolRegistry";
import { useSavedAutomations } from "../hooks/tools/automate/useSavedAutomations";
import { AutomationConfig, AutomationStepData, AutomationMode } from "../types/automation";
import { AutomationConfig, AutomationStepData, AutomationMode, AutomationStep } from "../types/automation";
import { AUTOMATION_STEPS } from "../constants/automation";
const Automate = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
@ -22,7 +22,7 @@ const Automate = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
const { selectedFiles } = useFileSelection();
const { setMode } = useNavigation();
const [currentStep, setCurrentStep] = useState<'selection' | 'creation' | 'run'>(AUTOMATION_STEPS.SELECTION);
const [currentStep, setCurrentStep] = useState<AutomationStep>(AUTOMATION_STEPS.SELECTION);
const [stepData, setStepData] = useState<AutomationStepData>({ step: AUTOMATION_STEPS.SELECTION });
const automateOperation = useAutomateOperation();
@ -64,7 +64,7 @@ const Automate = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
const renderCurrentStep = () => {
switch (currentStep) {
case 'selection':
case AUTOMATION_STEPS.SELECTION:
return (
<AutomationSelection
savedAutomations={savedAutomations}
@ -82,7 +82,7 @@ const Automate = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
/>
);
case 'creation':
case AUTOMATION_STEPS.CREATION:
if (!stepData.mode) {
console.error('Creation mode is undefined');
return null;
@ -100,7 +100,7 @@ const Automate = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
/>
);
case 'run':
case AUTOMATION_STEPS.RUN:
if (!stepData.automation) {
console.error('Automation config is undefined');
return null;

View File

@ -24,8 +24,10 @@ export interface AutomationTool {
parameters?: Record<string, any>;
}
export type AutomationStep = typeof import('../constants/automation').AUTOMATION_STEPS[keyof typeof import('../constants/automation').AUTOMATION_STEPS];
export interface AutomationStepData {
step: 'selection' | 'creation' | 'run';
step: AutomationStep;
mode?: AutomationMode;
automation?: AutomationConfig;
}