diff --git a/frontend/public/locales/en-GB/translation.toml b/frontend/public/locales/en-GB/translation.toml index 57e109eb56..df41a29913 100644 --- a/frontend/public/locales/en-GB/translation.toml +++ b/frontend/public/locales/en-GB/translation.toml @@ -7384,6 +7384,7 @@ automation = "Automation" automationSaved = "Automation saved" createFolder = "Create Folder" stepsSaved = "Steps saved — click Create Folder to finish" +automationRequired = "Add at least one configured step before saving." color = "Accent color" createTitle = "New Watch Folder" description = "Description" diff --git a/frontend/src/core/components/smartFolders/SmartFolderHomePage.tsx b/frontend/src/core/components/smartFolders/SmartFolderHomePage.tsx index b315d163a5..7e77881326 100644 --- a/frontend/src/core/components/smartFolders/SmartFolderHomePage.tsx +++ b/frontend/src/core/components/smartFolders/SmartFolderHomePage.tsx @@ -6,6 +6,7 @@ import EditIcon from '@mui/icons-material/Edit'; import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; import ChevronRightIcon from '@mui/icons-material/ChevronRight'; import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; +import CloseIcon from '@mui/icons-material/Close'; import FolderPlusIcon from '@mui/icons-material/CreateNewFolder'; import PauseCircleOutlineIcon from '@mui/icons-material/PauseCircleOutline'; import PlayCircleOutlineIcon from '@mui/icons-material/PlayCircleOutline'; @@ -348,6 +349,10 @@ function FolderCard({ function HowItWorks() { const { t } = useTranslation(); + const [dismissed, setDismissed] = useState(() => sessionStorage.getItem('wf_howItWorks_dismissed') === '1'); + + if (dismissed) return null; + const steps = [ { n: '1', @@ -376,11 +381,22 @@ function HowItWorks() { backgroundColor: 'var(--bg-toolbar)', }} > - - - - {t('smartFolders.howItWorks.title', 'How Watch Folders work')} - + + + + + {t('smartFolders.howItWorks.title', 'How Watch Folders work')} + + + { sessionStorage.setItem('wf_howItWorks_dismissed', '1'); setDismissed(true); }} + aria-label="Dismiss" + > + + {steps.map((step) => ( @@ -632,25 +648,7 @@ export function SmartFolderHomePage() { setCreateModalOpen(true)} /> ) : ( - {folders.map((folder) => { - const status = statuses[folder.id] ?? 'idle'; - const isProcessing = - processingFolderIds.has(folder.id) || status === 'processing'; - return ( - - ); - })} + {/* Add another prompt */} - + {folders.map((folder) => { + const status = statuses[folder.id] ?? 'idle'; + const isProcessing = + processingFolderIds.has(folder.id) || status === 'processing'; + return ( + + ); + })} )} diff --git a/frontend/src/core/components/smartFolders/SmartFolderManagementModal.tsx b/frontend/src/core/components/smartFolders/SmartFolderManagementModal.tsx index eafefa477a..e04613cbd5 100644 --- a/frontend/src/core/components/smartFolders/SmartFolderManagementModal.tsx +++ b/frontend/src/core/components/smartFolders/SmartFolderManagementModal.tsx @@ -7,9 +7,7 @@ import { TextInput, Textarea, Divider, - ColorSwatch, - SimpleGrid, - Text, + ColorInput, } from '@mantine/core'; import { useTranslation } from 'react-i18next'; import { SmartFolder } from '@app/types/smartFolders'; @@ -19,7 +17,7 @@ import AutomationCreation from '@app/components/tools/automate/AutomationCreatio import { useToolWorkflow } from '@app/contexts/ToolWorkflowContext'; import { smartFolderStorage } from '@app/services/smartFolderStorage'; -const ACCENT_COLORS = [ +const ACCENT_SWATCHES = [ '#3b82f6', '#0ea5e9', '#14b8a6', '#22c55e', '#f97316', '#ef4444', '#9333ea', '#ec4899', '#6366f1', '#eab308', '#64748b', '#0f172a', @@ -48,9 +46,9 @@ export function SmartFolderManagementModal({ const [description, setDescription] = useState(editFolder?.description ?? ''); const [icon, setIcon] = useState(editFolder?.icon ?? 'FolderIcon'); const [accentColor, setAccentColor] = useState(editFolder?.accentColor ?? '#3b82f6'); - const [customColor, setCustomColor] = useState(''); const [saving, setSaving] = useState(false); const [nameError, setNameError] = useState(''); + const [automationError, setAutomationError] = useState(''); // AutomationCreation exposes its save function via this ref const automationSaveTrigger = useRef<(() => void) | null>(null); @@ -60,8 +58,9 @@ export function SmartFolderManagementModal({ setDescription(editFolder?.description ?? ''); setIcon(editFolder?.icon ?? 'FolderIcon'); setAccentColor(editFolder?.accentColor ?? '#3b82f6'); - setCustomColor(''); + setNameError(''); + setAutomationError(''); setSaving(false); }, [editFolder]); @@ -116,6 +115,7 @@ export function SmartFolderManagementModal({ setNameError(t('smartFolders.modal.nameTooLong', 'Folder name must be 50 characters or less')); return; } + setAutomationError(''); setSaving(true); // Trigger automation save; onComplete handles the rest automationSaveTrigger.current?.(); @@ -161,34 +161,15 @@ export function SmartFolderManagementModal({ size="sm" /> - - {t('smartFolders.modal.color', 'Accent color')} - - - {ACCENT_COLORS.map((color) => ( - setAccentColor(color)} - /> - ))} - - { - setCustomColor(e.currentTarget.value); - if (/^#[0-9a-fA-F]{6}$/.test(e.currentTarget.value)) { - setAccentColor(e.currentTarget.value); - } - }} - size="xs" - style={{ width: '5.5rem' }} - /> - - + @@ -198,11 +179,15 @@ export function SmartFolderManagementModal({ existingAutomation={existingAutomation ?? undefined} onBack={handleClose} onComplete={handleAutomationComplete} + onSaveFailed={() => { setSaving(false); setAutomationError(t('smartFolders.modal.automationRequired', 'Add at least one configured step before saving.')); }} toolRegistry={toolRegistry} hideMetadata nameOverride={name.trim() || 'Watch Folder Automation'} saveTriggerRef={automationSaveTrigger} /> + {automationError && ( + {automationError} + )} diff --git a/frontend/src/core/components/smartFolders/WatchFolderFileList.tsx b/frontend/src/core/components/smartFolders/WatchFolderFileList.tsx index 9f36811e80..c05fcf8814 100644 --- a/frontend/src/core/components/smartFolders/WatchFolderFileList.tsx +++ b/frontend/src/core/components/smartFolders/WatchFolderFileList.tsx @@ -643,9 +643,14 @@ export function WatchFolderFileList({ files, folderId, onSendToFolder, onNavigat {files.length === 0 ? ( - - {t('smartFolders.fileList.empty', 'No files yet')} - + + + {t('smartFolders.fileList.empty', 'No files yet')} + + + ) : processedFiles.length === 0 ? ( {search ? t('smartFolders.fileList.noResults', 'No files match your search') : t('smartFolders.fileList.noneInFilter', 'No files in this category')} diff --git a/frontend/src/core/components/tools/automate/AutomationCreation.tsx b/frontend/src/core/components/tools/automate/AutomationCreation.tsx index 2293951215..b501aab569 100644 --- a/frontend/src/core/components/tools/automate/AutomationCreation.tsx +++ b/frontend/src/core/components/tools/automate/AutomationCreation.tsx @@ -31,9 +31,10 @@ interface AutomationCreationProps { hideMetadata?: boolean; nameOverride?: string; saveTriggerRef?: React.MutableRefObject<(() => void) | null>; + onSaveFailed?: () => void; } -export default function AutomationCreation({ mode, existingAutomation, onBack, onComplete, toolRegistry, hideMetadata = false, nameOverride, saveTriggerRef }: AutomationCreationProps) { +export default function AutomationCreation({ mode, existingAutomation, onBack, onComplete, toolRegistry, hideMetadata = false, nameOverride, saveTriggerRef, onSaveFailed }: AutomationCreationProps) { const { t } = useTranslation(); const { @@ -104,7 +105,7 @@ export default function AutomationCreation({ mode, existingAutomation, onBack, o }; const saveAutomation = async () => { - if (!canSave()) return; + if (!canSave()) { onSaveFailed?.(); return; } const automationData = { name: effectiveName.trim(),