diff --git a/apps/frontend/src/renderer/components/KanbanBoard.tsx b/apps/frontend/src/renderer/components/KanbanBoard.tsx index 51f13dc9..7522ac2a 100644 --- a/apps/frontend/src/renderer/components/KanbanBoard.tsx +++ b/apps/frontend/src/renderer/components/KanbanBoard.tsx @@ -489,6 +489,8 @@ export function KanbanBoard({ tasks, onTaskClick, onNewTaskClick, onRefresh, isR // Queue settings modal state const [showQueueSettings, setShowQueueSettings] = useState(false); + // Store projectId when modal opens to prevent modal from disappearing if tasks change + const queueSettingsProjectIdRef = useRef(null); // Queue processing lock to prevent race conditions const isProcessingQueueRef = useRef(false); @@ -794,11 +796,15 @@ export function KanbanBoard({ tasks, onTaskClick, onNewTaskClick, onRefresh, isR /** * Save queue settings (maxParallelTasks) + * + * Uses the stored ref value to ensure the save works even if tasks + * change while the modal is open. */ const handleSaveQueueSettings = async (maxParallel: number) => { - if (!projectId) return; + const savedProjectId = queueSettingsProjectIdRef.current || projectId; + if (!savedProjectId) return; - const success = await updateProjectSettings(projectId, { maxParallelTasks: maxParallel }); + const success = await updateProjectSettings(savedProjectId, { maxParallelTasks: maxParallel }); if (success) { toast({ title: t('queue.settings.saved'), @@ -1112,7 +1118,12 @@ export function KanbanBoard({ tasks, onTaskClick, onNewTaskClick, onRefresh, isR isOver={overColumnId === status} onAddClick={status === 'backlog' ? onNewTaskClick : undefined} onQueueAll={status === 'backlog' ? handleQueueAll : undefined} - onQueueSettings={status === 'queue' ? () => setShowQueueSettings(true) : undefined} + onQueueSettings={status === 'queue' ? () => { + // Only open modal if we have a valid projectId + if (!projectId) return; + queueSettingsProjectIdRef.current = projectId; + setShowQueueSettings(true); + } : undefined} onArchiveAll={status === 'done' ? handleArchiveAll : undefined} maxParallelTasks={status === 'in_progress' ? maxParallelTasks : undefined} archivedCount={status === 'done' ? archivedCount : undefined} @@ -1181,11 +1192,16 @@ export function KanbanBoard({ tasks, onTaskClick, onNewTaskClick, onRefresh, isR /> {/* Queue Settings Modal */} - {projectId && ( + {(queueSettingsProjectIdRef.current || projectId) && ( { + setShowQueueSettings(open); + if (!open) { + queueSettingsProjectIdRef.current = null; + } + }} + projectId={queueSettingsProjectIdRef.current || projectId || ''} currentMaxParallel={maxParallelTasks} onSave={handleSaveQueueSettings} /> diff --git a/apps/frontend/src/renderer/components/QueueSettingsModal.tsx b/apps/frontend/src/renderer/components/QueueSettingsModal.tsx index 77460b3f..18342101 100644 --- a/apps/frontend/src/renderer/components/QueueSettingsModal.tsx +++ b/apps/frontend/src/renderer/components/QueueSettingsModal.tsx @@ -12,14 +12,28 @@ import { Button } from './ui/button'; import { Label } from './ui/label'; import { Input } from './ui/input'; +/** + * Props for QueueSettingsModal component + */ interface QueueSettingsModalProps { + /** Whether the modal is currently open */ open: boolean; + /** Callback to control modal open state */ onOpenChange: (open: boolean) => void; + /** The project ID to update settings for */ projectId: string; + /** Current maximum parallel tasks setting (default: 3) */ currentMaxParallel?: number; + /** Callback when user saves the new max parallel value */ onSave: (maxParallel: number) => void; } +/** + * QueueSettingsModal - Modal for configuring queue parallel task limits + * + * Allows users to adjust the maximum number of tasks that can run in parallel + * for a specific project. Validates input between 1-10 tasks. + */ export function QueueSettingsModal({ open, onOpenChange, @@ -39,14 +53,20 @@ export function QueueSettingsModal({ } }, [open, currentMaxParallel]); + /** + * Validates and saves the max parallel tasks setting + * + * Validates that the value is between 1-10, sets an error message + * if invalid, otherwise calls onSave and closes the modal. + */ const handleSave = () => { // Validate the input if (maxParallel < 1) { - setError(t('queue.settings.minValueError')); + setError(t('tasks:queue.settings.minValueError')); return; } if (maxParallel > 10) { - setError(t('queue.settings.maxValueError')); + setError(t('tasks:queue.settings.maxValueError')); return; } @@ -54,6 +74,14 @@ export function QueueSettingsModal({ onOpenChange(false); }; + /** + * Handles input field changes for the max parallel tasks value + * + * Parses the input value, validates it's a number, and updates state. + * Allows empty input for editing purposes (will fail validation on save). + * + * @param e - The input change event from the number input field + */ const handleInputChange = (e: React.ChangeEvent) => { const inputValue = e.target.value; @@ -75,16 +103,16 @@ export function QueueSettingsModal({ - {t('queue.settings.title')} + {t('tasks:queue.settings.title')} - {t('queue.settings.description')} + {t('tasks:queue.settings.description')}
{error}

)}

- {t('queue.settings.hint')} + {t('tasks:queue.settings.hint')}