From 2c16c14264bda880b305436953f96db6cfa82672 Mon Sep 17 00:00:00 2001 From: Neeraj-gagat Date: Sun, 30 Nov 2025 12:29:09 +0530 Subject: [PATCH 1/8] seperated addtaskdialog && edittaskdialog --- .../HomeComponents/Tasks/AddTaskDialog.tsx | 219 ++ .../HomeComponents/Tasks/EditTaskDialog.tsx | 1233 +++++++++++ .../components/HomeComponents/Tasks/Tasks.tsx | 1813 +---------------- .../Tasks/__tests__/ReportsView.test.tsx | 18 +- .../Tasks/__tests__/Tasks.test.tsx | 18 +- .../HomeComponents/Tasks/useEditTask.tsx | 107 + 6 files changed, 1697 insertions(+), 1711 deletions(-) create mode 100644 frontend/src/components/HomeComponents/Tasks/AddTaskDialog.tsx create mode 100644 frontend/src/components/HomeComponents/Tasks/EditTaskDialog.tsx create mode 100644 frontend/src/components/HomeComponents/Tasks/useEditTask.tsx diff --git a/frontend/src/components/HomeComponents/Tasks/AddTaskDialog.tsx b/frontend/src/components/HomeComponents/Tasks/AddTaskDialog.tsx new file mode 100644 index 00000000..0f8bc578 --- /dev/null +++ b/frontend/src/components/HomeComponents/Tasks/AddTaskDialog.tsx @@ -0,0 +1,219 @@ +import { Badge } from '@/components/ui/badge'; +import { Button } from '@/components/ui/button'; +import { DatePicker } from '@/components/ui/date-picker'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '@/components/ui/dialog'; +import { Input } from '@/components/ui/input'; +import { Key } from '@/components/ui/key-button'; +import { Label } from '@/components/ui/label'; +import { format } from 'date-fns'; + +export interface TaskFormData { + description: string; + priority: string; + project: string; + due: string; + tags: string[]; +} + +interface AddTaskDialogProps { + isOpen: boolean; + setIsOpen: (value: boolean) => void; + newTask: TaskFormData; + setNewTask: (task: TaskFormData) => void; + tagInput: string; + setTagInput: (value: string) => void; + onSubmit: (task: TaskFormData) => void; +} + +export const AddTaskdialog = ({ + isOpen, + setIsOpen, + newTask, + setNewTask, + tagInput, + setTagInput, + onSubmit, +}: AddTaskDialogProps) => { + // Handle adding a tag + const handleAddTag = () => { + if (tagInput && !newTask.tags.includes(tagInput, 0)) { + setNewTask({ ...newTask, tags: [...newTask.tags, tagInput] }); + setTagInput(''); // Clear the input field + } + }; + + // Handle removing a tag + const handleRemoveTag = (tagToRemove: string) => { + setNewTask({ + ...newTask, + tags: newTask.tags.filter((tag) => tag !== tagToRemove), + }); + }; + + return ( + + + + + + + + + + Add a{' '} + + new task + + + + Fill in the details below to add a new task. + + +
+
+ + + setNewTask({ + ...newTask, + description: e.target.value, + }) + } + required + className="col-span-3" + /> +
+
+ +
+ +
+
+ +
+ + + setNewTask({ + ...newTask, + project: e.target.value, + }) + } + className="col-span-3" + /> +
+
+ +
+ { + setNewTask({ + ...newTask, + due: date ? format(date, 'yyyy-MM-dd') : '', + }); + }} + placeholder="Select a due date" + /> +
+
+
+ + setTagInput(e.target.value)} + onKeyDown={(e) => e.key === 'Enter' && handleAddTag()} // Allow adding tag on pressing Enter + required + className="col-span-3" + /> +
+ +
+ {newTask.tags.length > 0 && ( +
+
+
+ {newTask.tags.map((tag, index) => ( + + {tag} + + + ))} +
+
+ )} +
+
+ + + + +
+
+ ); +}; diff --git a/frontend/src/components/HomeComponents/Tasks/EditTaskDialog.tsx b/frontend/src/components/HomeComponents/Tasks/EditTaskDialog.tsx new file mode 100644 index 00000000..259eca53 --- /dev/null +++ b/frontend/src/components/HomeComponents/Tasks/EditTaskDialog.tsx @@ -0,0 +1,1233 @@ +import { Task } from '../../utils/types'; +import { EditTaskState } from './useEditTask'; +import { Badge } from '@/components/ui/badge'; +import { Button } from '@/components/ui/button'; +import { DatePicker } from '@/components/ui/date-picker'; +import { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '@/components/ui/dialog'; +import { Input } from '@/components/ui/input'; +import { Key } from '@/components/ui/key-button'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { Table, TableBody, TableCell, TableRow } from '@/components/ui/table'; +import { format } from 'date-fns'; +import { + CheckIcon, + CopyIcon, + Folder, + PencilIcon, + Tag, + Trash2Icon, + XIcon, +} from 'lucide-react'; +import CopyToClipboard from 'react-copy-to-clipboard'; +import { formattedDate, handleCopy } from './tasks-utils'; + +interface EditTaskDialogProps { + index: number; + task: Task; + isOpen: boolean; + selectedIndex: number; + onOpenChange: (open: boolean) => void; + onSelectTask: (task: Task, index: number) => void; + editState: EditTaskState; + onUpdateState: (updates: Partial) => void; + allTasks: Task[]; + onSaveDescription: (task: Task, description: string) => void; + onSaveTags: (task: Task, tags: string[]) => void; + onSavePriority: (task: Task, priority: string) => void; + onSaveProject: (task: Task, project: string) => void; + onSaveWaitDate: (task: Task, date: string) => void; + onSaveStartDate: (task: Task, date: string) => void; + onSaveEntryDate: (task: Task, date: string) => void; + onSaveEndDate: (task: Task, date: string) => void; + onSaveDueDate: (task: Task, date: string) => void; + onSaveDepends: (task: Task, depends: string[]) => void; + onMarkComplete: (uuid: string) => void; + onMarkDeleted: (uuid: string) => void; + isOverdue: (due?: string) => boolean; +} + +export const EditTaskDialog = ({ + index, + task, + isOpen, + selectedIndex, + onOpenChange, + onSelectTask, + editState, + onUpdateState, + allTasks, + onSaveDescription, + onSaveTags, + onSavePriority, + onSaveProject, + onSaveWaitDate, + onSaveStartDate, + onSaveEntryDate, + onSaveEndDate, + onSaveDueDate, + onSaveDepends, + onMarkComplete, + onMarkDeleted, + isOverdue, +}: EditTaskDialogProps) => { + const handleDialogOpenChange = (open: boolean) => { + if (open) { + onSelectTask(task, index); // Notify parent that this task is selected + } + onOpenChange(open); + }; + + const handleCancelClick = () => { + onUpdateState({ + editedDescription: task.description, + editedPriority: task.priority, + editedProject: task.project, + editedTags: task.tags, + isEditing: false, + }); + }; + + const handleEditClick = (description: string) => { + onUpdateState({ + isEditing: true, + editedDescription: description, + }); + }; + + return ( + + + { + onSelectTask(task, index); + }} + > + {/* Display task details */} + + + {task.id} + + + + {task.priority === 'H' && ( +
+ )} + {task.priority === 'M' && ( +
+ )} + {task.priority != 'H' && task.priority != 'M' && ( +
+ )} + {task.description} + {task.project != '' && ( + + + {task.project === '' ? '' : task.project} + + )} +
+ + + {task.status === 'pending' && isOverdue(task.due) + ? 'O' + : task.status === 'completed' + ? 'C' + : task.status === 'deleted' + ? 'D' + : 'P'} + + +
+
+ + + + + + Task{' '} + + Details + + + + + {/* Scrollable content */} +
+ + + + + ID: + + {task.id} + {task.status === 'pending' && isOverdue(task.due) && ( + + Overdue + + )} + + + + Description: + + {editState.isEditing ? ( + <> +
+ + onUpdateState({ + editedDescription: e.target.value, + }) + } + className="flex-grow mr-2" + /> + + +
+ + ) : ( + <> + {task.description} + + + )} +
+
+ + Due: + + {editState.isEditingDueDate ? ( +
+ { + try { + const dateStr = + editState.editedDueDate.includes('T') + ? editState.editedDueDate.split('T')[0] + : editState.editedDueDate; + const parsed = new Date( + dateStr + 'T00:00:00' + ); + return isNaN(parsed.getTime()) + ? undefined + : parsed; + } catch { + return undefined; + } + })() + : undefined + } + onDateChange={(date) => + onUpdateState({ + editedDueDate: date + ? format(date, 'yyyy-MM-dd') + : '', + }) + } + placeholder="Select due date" + /> + + +
+ ) : ( + <> + {formattedDate(task.due)} + + + )} +
+
+ + Start: + + {editState.isEditingStartDate ? ( +
+ { + try { + // Handle YYYY-MM-DD format + const dateStr = + editState.editedStartDate.includes('T') + ? editState.editedStartDate.split( + 'T' + )[0] + : editState.editedStartDate; + const parsed = new Date( + dateStr + 'T00:00:00' + ); + return isNaN(parsed.getTime()) + ? undefined + : parsed; + } catch { + return undefined; + } + })() + : undefined + } + onDateChange={(date) => + onUpdateState({ + editedStartDate: date + ? format(date, 'yyyy-MM-dd') + : '', + }) + } + /> + + + + +
+ ) : ( + <> + {formattedDate(task.start)} + + + )} +
+
+ + End: + + {editState.isEditingEndDate ? ( +
+ { + try { + const dateStr = + editState.editedEndDate.includes('T') + ? editState.editedEndDate.split('T')[0] + : editState.editedEndDate; + const parsed = new Date( + dateStr + 'T00:00:00' + ); + return isNaN(parsed.getTime()) + ? undefined + : parsed; + } catch { + return undefined; + } + })() + : undefined + } + onDateChange={(date) => + onUpdateState({ + editedEndDate: date + ? format(date, 'yyyy-MM-dd') + : '', + }) + } + placeholder="Select end date" + /> + + +
+ ) : ( +
+ {formattedDate(task.end)} + +
+ )} +
+
+ + Wait: + + {editState.isEditingWaitDate ? ( +
+ + onUpdateState({ + editedWaitDate: date + ? format(date, 'yyyy-MM-dd') + : '', + }) + } + /> + + + + +
+ ) : ( + <> + {formattedDate(task.wait)} + + + )} +
+
+ + Depends: + + {!editState.isEditingDepends ? ( +
+ {(task.depends || []).map((depUuid) => { + const depTask = allTasks.find( + (t) => t.uuid === depUuid + ); + return ( + { + if (depTask) { + onOpenChange(false); + setTimeout(() => { + const depIndex = allTasks.findIndex( + (t) => t.uuid === depTask?.uuid + ); + onSelectTask(depTask!, depIndex); + onOpenChange(true); + }, 100); + } + }} + > + {depTask?.description || depUuid.substring(0, 8)} + + ); + })} + +
+ ) : ( +
+
+ {editState.editedDepends.map((depUuid) => { + const depTask = allTasks.find( + (t) => t.uuid === depUuid + ); + return ( + + + {depTask?.description || + depUuid.substring(0, 8)} + + + + ); + })} +
+
+
+ + {editState.dependsDropdownOpen && ( +
+ + onUpdateState({ + dependsSearchTerm: e.target.value, + }) + } + className="m-2 w-[calc(100%-1rem)]" + /> + {allTasks + .filter( + (t) => + t.uuid !== task.uuid && + t.status === 'pending' && + !editState.editedDepends.includes( + t.uuid + ) && + t.description + .toLowerCase() + .includes( + editState.dependsSearchTerm.toLowerCase() + ) + ) + .map((t) => ( +
{ + onUpdateState({ + editedDepends: [ + ...editState.editedDepends, + t.uuid, + ], + dependsSearchTerm: '', + }); + }} + > + + + {t.description} + +
+ ))} +
+ )} +
+ + +
+
+ )} +
+
+ + Recur: + {task.recur} + + + RType: + {task.rtype} + + + Priority: + + {editState.isEditingPriority ? ( +
+ + + +
+ ) : ( +
+ + {task.priority + ? task.priority === 'H' + ? 'High (H)' + : task.priority === 'M' + ? 'Medium (M)' + : task.priority === 'L' + ? 'Low (L)' + : task.priority + : 'None'} + + +
+ )} +
+
+ + Project: + + {editState.isEditingProject ? ( + <> +
+ + onUpdateState({ + editedProject: e.target.value, + }) + } + className="flex-grow mr-2" + /> + + +
+ + ) : ( + <> + {task.project} + + + )} +
+
+ + Status: + {task.status} + + + Tags: + + {editState.isEditingTags ? ( +
+
+ { + // For allowing only alphanumeric characters + if (e.target.value.length > 1) { + /^[a-zA-Z0-9]*$/.test(e.target.value.trim()) + ? onUpdateState({ + editTagInput: e.target.value.trim(), + }) + : ''; + } else { + /^[a-zA-Z]*$/.test(e.target.value.trim()) + ? onUpdateState({ + editTagInput: e.target.value.trim(), + }) + : ''; + } + }} + placeholder="Add a tag (press enter to add)" + className="flex-grow mr-2" + onKeyDown={(e) => { + if ( + e.key === 'Enter' && + editState.editTagInput.trim() + ) { + onUpdateState({ + editedTags: [ + ...editState.editedTags, + editState.editTagInput.trim(), + ], + editTagInput: '', + }); + } + }} + /> + + +
+
+ {editState.editedTags != null && + editState.editedTags.length > 0 && ( +
+
+ {editState.editedTags.map((tag, index) => ( + + {tag} + + + ))} +
+
+ )} +
+
+ ) : ( +
+ {task.tags !== null && task.tags.length >= 1 ? ( + task.tags.map((tag, index) => ( + + + {tag} + + )) + ) : ( + No Tags + )} + +
+ )} +
+
+ + Entry: + + {editState.isEditingEntryDate ? ( +
+ { + try { + // Handle YYYY-MM-DD format + const dateStr = + editState.editedEntryDate.includes('T') + ? editState.editedEntryDate.split( + 'T' + )[0] + : editState.editedEntryDate; + const parsed = new Date( + dateStr + 'T00:00:00' + ); + return isNaN(parsed.getTime()) + ? undefined + : parsed; + } catch { + return undefined; + } + })() + : undefined + } + onDateChange={(date) => + onUpdateState({ + editedEntryDate: date + ? format(date, 'yyyy-MM-dd') + : '', + }) + } + /> + + + + +
+ ) : ( + <> + {formattedDate(task.entry)} + + + )} +
+
+ + Urgency: + {task.urgency} + + + UUID: + + {task.uuid} + handleCopy('Task UUID')} + > + + + + +
+
+
+
+ + {/* Non-scrollable footer */} + + {task.status == 'pending' ? ( + + + + + + + + + Are you{' '} + + sure? + + + + + + + + + + + + + ) : null} + + {task.status != 'deleted' ? ( + + + + + + + + + Are you{' '} + + sure? + + + + + + + + + + + + + ) : null} + + + + +
+
+ ); +}; diff --git a/frontend/src/components/HomeComponents/Tasks/Tasks.tsx b/frontend/src/components/HomeComponents/Tasks/Tasks.tsx index d5b0df33..fb52b0dc 100644 --- a/frontend/src/components/HomeComponents/Tasks/Tasks.tsx +++ b/frontend/src/components/HomeComponents/Tasks/Tasks.tsx @@ -1,4 +1,5 @@ import { useEffect, useState, useCallback, useRef } from 'react'; +import { useEditTask } from './useEditTask'; import { Task } from '../../utils/types'; import { ReportsView } from './ReportsView'; import Fuse from 'fuse.js'; @@ -13,42 +14,11 @@ import { } from '@/components/ui/table'; import { Button } from '@/components/ui/button'; import { toast } from 'react-toastify'; -import { Badge } from '@/components/ui/badge'; -import { - Dialog, - DialogClose, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger, -} from '../../ui/dialog'; -import { - ArrowUpDown, - CheckIcon, - CopyIcon, - Folder, - Loader2, - PencilIcon, - Tag, - Trash2Icon, - XIcon, -} from 'lucide-react'; +import { ArrowUpDown, Loader2 } from 'lucide-react'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select'; -import CopyToClipboard from 'react-copy-to-clipboard'; -import { - formattedDate, getDisplayedPages, - handleCopy, handleDate, markTaskAsCompleted, markTaskAsDeleted, @@ -70,10 +40,10 @@ import { TasksDatabase, } from './hooks'; import { debounce } from '@/components/utils/utils'; -import { DatePicker } from '@/components/ui/date-picker'; -import { format } from 'date-fns'; import { Taskskeleton } from './TaskSkeleton'; import { Key } from '@/components/ui/key-button'; +import { AddTaskdialog, TaskFormData } from './AddTaskDialog'; +import { EditTaskDialog } from './EditTaskDialog'; const db = new TasksDatabase(); export let syncTasksWithTwAndDb: () => any; @@ -107,41 +77,23 @@ export const Tasks = ( const [isAddTaskOpen, setIsAddTaskOpen] = useState(false); const [_isDialogOpen, setIsDialogOpen] = useState(false); const [tagInput, setTagInput] = useState(''); - - const [isEditing, setIsEditing] = useState(false); - const [editedDescription, setEditedDescription] = useState(''); const [_selectedTask, setSelectedTask] = useState(null); const [editedTags, setEditedTags] = useState( _selectedTask?.tags || [] ); - const [editTagInput, setEditTagInput] = useState(''); - const [isEditingTags, setIsEditingTags] = useState(false); - const [isEditingPriority, setIsEditingPriority] = useState(false); - const [editedPriority, setEditedPriority] = useState('NONE'); - const [isEditingProject, setIsEditingProject] = useState(false); - const [editedProject, setEditedProject] = useState( - _selectedTask?.project || '' - ); - const [isEditingWaitDate, setIsEditingWaitDate] = useState(false); - const [editedWaitDate, setEditedWaitDate] = useState(''); - const [isEditingStartDate, setIsEditingStartDate] = useState(false); - const [editedStartDate, setEditedStartDate] = useState(''); - const [isEditingEntryDate, setIsEditingEntryDate] = useState(false); - const [editedEntryDate, setEditedEntryDate] = useState(''); - const [isEditingEndDate, setIsEditingEndDate] = useState(false); - const [editedEndDate, setEditedEndDate] = useState(''); - const [isEditingDueDate, setIsEditingDueDate] = useState(false); - const [editedDueDate, setEditedDueDate] = useState(''); - const [isEditingDepends, setIsEditingDepends] = useState(false); - const [editedDepends, setEditedDepends] = useState([]); - const [dependsDropdownOpen, setDependsDropdownOpen] = useState(false); - const [dependsSearchTerm, setDependsSearchTerm] = useState(''); const [searchTerm, setSearchTerm] = useState(''); const [debouncedTerm, setDebouncedTerm] = useState(''); const [lastSyncTime, setLastSyncTime] = useState(null); const tableRef = useRef(null); const [hotkeysEnabled, setHotkeysEnabled] = useState(false); const [selectedIndex, setSelectedIndex] = useState(0); + const { + state: editState, + updateState: updateEditState, + resetState: resetEditState, + } = useEditTask(_selectedTask); + + // Handler for dialog open/close const isOverdue = (due?: string) => { if (!due) return false; @@ -327,27 +279,18 @@ export const Tasks = ( } }, [props.email, props.encryptionSecret, props.UUID]); // Add dependencies - async function handleAddTask( - email: string, - encryptionSecret: string, - UUID: string, - description: string, - project: string, - priority: string, - due: string, - tags: string[] - ) { + async function handleAddTask(task: TaskFormData) { if (handleDate(newTask.due)) { try { await addTaskToBackend({ - email, - encryptionSecret, - UUID, - description, - project, - priority, - due, - tags, + email: props.email, + encryptionSecret: props.encryptionSecret, + UUID: props.UUID, + description: task.description, + project: task.project, + priority: task.priority, + due: task.due, + tags: task.tags, backendURL: url.backendURL, }); @@ -424,13 +367,32 @@ export const Tasks = ( setCurrentPage(1); }; - const handleEditClick = (description: string) => { - setIsEditing(true); - setEditedDescription(description); + const handleMarkComplete = async (taskuuid: string) => { + await markTaskAsCompleted( + props.email, + props.encryptionSecret, + props.UUID, + taskuuid + ); }; - const handleSaveClick = (task: Task) => { - task.description = editedDescription; + const handleMarkDelete = async (taskuuid: string) => { + await markTaskAsDeleted( + props.email, + props.encryptionSecret, + props.UUID, + taskuuid + ); + }; + + const handleSelectTask = (task: Task, index: number) => { + setSelectedTask(task); + setSelectedIndex(index); + resetEditState(); // as before + }; + + const handleSaveDescription = (task: Task, description: string) => { + task.description = description; handleEditTaskOnBackend( props.email, props.encryptionSecret, @@ -446,11 +408,10 @@ export const Tasks = ( task.depends || [], task.due || '' ); - setIsEditing(false); }; - const handleProjectSaveClick = (task: Task) => { - task.project = editedProject; + const handleSaveProject = (task: Task, project: string) => { + task.project = project; handleEditTaskOnBackend( props.email, props.encryptionSecret, @@ -466,11 +427,10 @@ export const Tasks = ( task.depends || [], task.due || '' ); - setIsEditingProject(false); }; - const handleWaitDateSaveClick = (task: Task) => { - task.wait = editedWaitDate; + const handleSaveWaitDate = (task: Task, waitDate: string) => { + task.wait = waitDate; handleEditTaskOnBackend( props.email, @@ -487,12 +447,10 @@ export const Tasks = ( task.depends || [], task.due || '' ); - - setIsEditingWaitDate(false); }; - const handleStartDateSaveClick = (task: Task) => { - task.start = editedStartDate; + const handleSaveStartDate = (task: Task, startDate: string) => { + task.start = startDate; handleEditTaskOnBackend( props.email, @@ -509,12 +467,10 @@ export const Tasks = ( task.depends || [], task.due || '' ); - - setIsEditingStartDate(false); }; - const handleEntryDateSaveClick = (task: Task) => { - task.entry = editedEntryDate; + const handleSaveEntryDate = (task: Task, entryDate: string) => { + task.entry = entryDate; handleEditTaskOnBackend( props.email, @@ -531,12 +487,10 @@ export const Tasks = ( task.depends || [], task.due || '' ); - - setIsEditingEntryDate(false); }; - const handleEndDateSaveClick = (task: Task) => { - task.end = editedEndDate; + const handleSaveEndDate = (task: Task, endDate: string) => { + task.end = endDate; handleEditTaskOnBackend( props.email, @@ -553,12 +507,10 @@ export const Tasks = ( task.depends || [], task.due || '' ); - - setIsEditingEndDate(false); }; - const handleDueDateSaveClick = (task: Task) => { - task.due = editedDueDate; + const handleSaveDueDate = (task: Task, dueDate: string) => { + task.due = dueDate; handleEditTaskOnBackend( props.email, @@ -575,12 +527,10 @@ export const Tasks = ( task.depends || [], task.due ); - - setIsEditingDueDate(false); }; - const handleDependsSaveClick = (task: Task) => { - task.depends = editedDepends; + const handleSaveDepends = (task: Task, depends: string[]) => { + task.depends = depends; handleEditTaskOnBackend( props.email, @@ -597,82 +547,18 @@ export const Tasks = ( task.depends, task.due || '' ); - - setIsEditingDepends(false); - setDependsDropdownOpen(false); - }; - - const handleAddDependency = (uuid: string) => { - if (!editedDepends.includes(uuid)) { - setEditedDepends([...editedDepends, uuid]); - } - }; - - const handleRemoveDependency = (uuid: string) => { - setEditedDepends(editedDepends.filter((dep) => dep !== uuid)); }; - const handleCancelClick = () => { - setIsEditing(false); - }; - - const handleDialogOpenChange = (_isDialogOpen: boolean, task: any) => { - setIsDialogOpen(_isDialogOpen); - if (!_isDialogOpen) { - setIsEditing(false); - setEditedDescription(''); - setIsEditingTags(false); - setEditedTags([]); - setIsEditingPriority(false); - setEditedPriority('NONE'); - setIsEditingStartDate(false); - setEditedStartDate(''); - setIsEditingEntryDate(false); - setEditedEntryDate(''); - setIsEditingEndDate(false); - setEditedEndDate(''); - setIsEditingDueDate(false); - setEditedDueDate(''); - setIsEditingDepends(false); - setEditedDepends([]); - setDependsDropdownOpen(false); - setDependsSearchTerm(''); - } else { + const handleDialogOpenChange = (isOpen: boolean, task?: Task) => { + setIsDialogOpen(isOpen); + if (!isOpen) { + resetEditState(); + setSelectedTask(null); + } else if (task) { setSelectedTask(task); - setEditedDescription(task?.description || ''); - setEditedPriority(task?.priority || 'NONE'); - } - }; - - // Handle adding a tag - const handleAddTag = () => { - if (tagInput && !newTask.tags.includes(tagInput, 0)) { - setNewTask({ ...newTask, tags: [...newTask.tags, tagInput] }); - setTagInput(''); // Clear the input field - } - }; - - // Handle adding a tag while editing - const handleAddEditTag = () => { - if (editTagInput && !editedTags.includes(editTagInput, 0)) { - setEditedTags([...editedTags, editTagInput]); - setEditTagInput(''); } }; - // Handle removing a tag - const handleRemoveTag = (tagToRemove: string) => { - setNewTask({ - ...newTask, - tags: newTask.tags.filter((tag) => tag !== tagToRemove), - }); - }; - - // Handle removing a tag while editing task - const handleRemoveEditTag = (tagToRemove: string) => { - setEditedTags(editedTags.filter((tag) => tag !== tagToRemove)); - }; - const sortWithOverdueOnTop = (tasks: Task[]) => { return [...tasks].sort((a, b) => { const aOverdue = a.status === 'pending' && isOverdue(a.due); @@ -737,16 +623,11 @@ export const Tasks = ( setTempTasks(filteredTasks); }, [selectedProjects, selectedTags, selectedStatuses, tasks, debouncedTerm]); - const handleEditTagsClick = (task: Task) => { - setEditedTags(task.tags || []); - setIsEditingTags(true); - }; - - const handleSaveTags = (task: Task) => { - const currentTags = task.tags || []; + const handleSaveTags = (task: Task, tags: string[]) => { + const currentTags = tags || []; const removedTags = currentTags.filter((tag) => !editedTags.includes(tag)); const updatedTags = editedTags.filter((tag) => tag.trim() !== ''); - const tagsToRemove = removedTags.map((tag) => `-${tag}`); + const tagsToRemove = removedTags.map((tag) => `${tag}`); const finalTags = [...updatedTags, ...tagsToRemove]; console.log(finalTags); handleEditTaskOnBackend( @@ -764,23 +645,11 @@ export const Tasks = ( task.depends || [], task.due || '' ); - - setIsEditingTags(false); - setEditTagInput(''); - }; - - const handleCancelTags = () => { - setIsEditingTags(false); - setEditedTags([]); - }; - const handleEditPriorityClick = (task: Task) => { - setEditedPriority(task.priority || 'NONE'); - setIsEditingPriority(true); }; - const handleSavePriority = async (task: Task) => { + const handleSavePriority = async (task: Task, priority: string) => { try { - const priorityValue = editedPriority === 'NONE' ? '' : editedPriority; + const priorityValue = priority === 'NONE' ? '' : priority; await modifyTaskOnBackend({ email: props.email, @@ -798,20 +667,12 @@ export const Tasks = ( console.log('Priority updated successfully!'); toast.success('Priority updated successfully!'); - setIsEditingPriority(false); } catch (error) { console.error('Failed to update priority:', error); toast.error('Failed to update priority. Please try again.'); } }; - const handleCancelPriority = () => { - setIsEditingPriority(false); - if (_selectedTask) { - setEditedPriority(_selectedTask.priority || 'NONE'); - } - }; - useHotkeys(['f'], () => { if (!showReports) { document.getElementById('search')?.focus(); @@ -999,196 +860,15 @@ export const Tasks = ( icon={} />
- - - - - - - - - - Add a{' '} - - new task - - - - Fill in the details below to add a new task. - - -
-
- - - setNewTask({ - ...newTask, - description: e.target.value, - }) - } - required - className="col-span-3" - /> -
-
- -
- -
-
- -
- - - setNewTask({ - ...newTask, - project: e.target.value, - }) - } - className="col-span-3" - /> -
-
- -
- { - setNewTask({ - ...newTask, - due: date - ? format(date, 'yyyy-MM-dd') - : '', - }); - }} - placeholder="Select a due date" - /> -
-
-
- - setTagInput(e.target.value)} - onKeyDown={(e) => - e.key === 'Enter' && handleAddTag() - } // Allow adding tag on pressing Enter - required - className="col-span-3" - /> -
- -
- {newTask.tags.length > 0 && ( -
-
-
- {newTask.tags.map((tag, index) => ( - - {tag} - - - ))} -
-
- )} -
-
- - - - -
-
+
- - - - - - Are you{' '} - - sure? - - - - - - - - - - - - - ) : null} - - {task.status != 'deleted' ? ( - - - - - - - - - Are you{' '} - - sure? - - - - - - - - - - - - - ) : null} - - - - - - + onOpenChange={handleDialogOpenChange} + editState={editState} + onUpdateState={updateEditState} + allTasks={tasks} + onSaveDescription={handleSaveDescription} + onSaveTags={handleSaveTags} + onSavePriority={handleSavePriority} + onSaveProject={handleSaveProject} + onSaveWaitDate={handleSaveWaitDate} + onSaveStartDate={handleSaveStartDate} + onSaveEntryDate={handleSaveEntryDate} + onSaveEndDate={handleSaveEndDate} + onSaveDueDate={handleSaveDueDate} + onSaveDepends={handleSaveDepends} + onMarkComplete={handleMarkComplete} + onMarkDeleted={handleMarkDelete} + isOverdue={isOverdue} + /> )) )} @@ -2474,151 +1021,15 @@ export const Tasks = (
- - - - - - - - - - Add a{' '} - - new task - - - - Fill in the details below to add a new task. - - -
-
- - - setNewTask({ - ...newTask, - description: e.target.value, - }) - } - className="col-span-3" - /> -
-
- -
- -
-
- -
- - - setNewTask({ - ...newTask, - project: e.target.value, - }) - } - className="col-span-3" - /> -
-
- -
- { - setNewTask({ - ...newTask, - due: date - ? format(date, 'yyyy-MM-dd') - : '', - }); - }} - placeholder="Select a due date" - /> -
-
-
- - - - -
-
+
-
@@ -207,7 +195,7 @@ export const AddTaskdialog = ({ placeholder="Add a tag" value={tagInput} onChange={(e) => setTagInput(e.target.value)} - onKeyDown={(e) => e.key === 'Enter' && handleAddTag()} // Allow adding tag on pressing Enter + onKeyDown={(e) => e.key === 'Enter' && handleAddTag()} required className="col-span-3" /> diff --git a/frontend/src/components/HomeComponents/Tasks/__tests__/AddTaskDialog.test.tsx b/frontend/src/components/HomeComponents/Tasks/__tests__/AddTaskDialog.test.tsx index c25970af..9e114f07 100644 --- a/frontend/src/components/HomeComponents/Tasks/__tests__/AddTaskDialog.test.tsx +++ b/frontend/src/components/HomeComponents/Tasks/__tests__/AddTaskDialog.test.tsx @@ -20,23 +20,31 @@ jest.mock('@/components/ui/date-picker', () => ({ ), })); -jest.mock('@/components/ui/select', () => ({ - Select: ({ children, value }: any) => ( -
- {children} -
- ), - SelectTrigger: ({ children, id }: any) => ( -
- {children} -
- ), - SelectValue: ({ placeholder }: any) =>
{placeholder}
, - SelectContent: ({ children }: any) =>
{children}
, - SelectItem: ({ children, value }: any) => ( - - ), -})); +jest.mock('@/components/ui/select', () => { + return { + Select: ({ children, onValueChange, value }: any) => ( + + ), + SelectTrigger: ({ children }: any) => <>{children}, + SelectValue: ({ placeholder }: any) => ( + + ), + SelectContent: ({ children }: any) => <>{children}, + SelectItem: ({ value, children, ...props }: any) => ( + + ), + }; +}); describe('AddTaskDialog Component', () => { let mockProps: any; diff --git a/frontend/src/components/HomeComponents/Tasks/__tests__/Tasks.test.tsx b/frontend/src/components/HomeComponents/Tasks/__tests__/Tasks.test.tsx index a5f510dc..de26d25d 100644 --- a/frontend/src/components/HomeComponents/Tasks/__tests__/Tasks.test.tsx +++ b/frontend/src/components/HomeComponents/Tasks/__tests__/Tasks.test.tsx @@ -483,7 +483,7 @@ describe('Tasks Component', () => { fireEvent.click(screen.getByRole('button', { name: /add task/i })); const projectSelect = await screen.findByTestId('project-select'); - fireEvent.change(projectSelect, { target: { value: '__CREATE_NEW__' } }); // Empty string triggers "create new project" mode + fireEvent.change(projectSelect, { target: { value: '' } }); const newProjectInput = await screen.findByPlaceholderText('New project name'); diff --git a/frontend/src/components/HomeComponents/Tasks/__tests__/useEditTask.test.ts b/frontend/src/components/HomeComponents/Tasks/__tests__/UseEditTask.test.ts similarity index 100% rename from frontend/src/components/HomeComponents/Tasks/__tests__/useEditTask.test.ts rename to frontend/src/components/HomeComponents/Tasks/__tests__/UseEditTask.test.ts From 18a175b99019ba41ae080467051ba985bc95f907 Mon Sep 17 00:00:00 2001 From: Neeraj-gagat Date: Sun, 7 Dec 2025 20:08:43 +0530 Subject: [PATCH 6/8] resolved conflicts --- backend/controllers/edit_task.go | 3 +- backend/models/request_body.go | 1 + backend/utils/tw/edit_task.go | 9 +- backend/utils/tw/taskwarrior_test.go | 8 +- .../HomeComponents/DevLogs/DevLogs.tsx | 193 +++--- .../DevLogs/__tests__/DevLogs.test.tsx | 58 +- .../__snapshots__/DevLogs.test.tsx.snap | 550 ++++++++++-------- .../HomeComponents/Navbar/NavbarDesktop.tsx | 171 +++--- .../HomeComponents/Navbar/NavbarMobile.tsx | 40 +- .../HomeComponents/Tasks/AddTaskDialog.tsx | 7 +- .../HomeComponents/Tasks/EditTaskDialog.tsx | 111 +++- .../HomeComponents/Tasks/ReportsView.tsx | 4 +- .../components/HomeComponents/Tasks/Tasks.tsx | 69 ++- .../HomeComponents/Tasks/UseEditTask.tsx | 8 + .../Tasks/__tests__/EditTaskDialog.test.tsx | 4 + .../Tasks/__tests__/Tasks.test.tsx | 2 +- .../Tasks/__tests__/UseEditTask.test.ts | 3 + .../components/HomeComponents/Tasks/hooks.ts | 3 + frontend/src/components/utils/types.ts | 4 + 19 files changed, 748 insertions(+), 500 deletions(-) diff --git a/backend/controllers/edit_task.go b/backend/controllers/edit_task.go index c7ca747d..8ef0e5b6 100644 --- a/backend/controllers/edit_task.go +++ b/backend/controllers/edit_task.go @@ -52,6 +52,7 @@ func EditTaskHandler(w http.ResponseWriter, r *http.Request) { end := requestBody.End depends := requestBody.Depends due := requestBody.Due + recur := requestBody.Recur if taskID == "" { http.Error(w, "taskID is required", http.StatusBadRequest) @@ -63,7 +64,7 @@ func EditTaskHandler(w http.ResponseWriter, r *http.Request) { Name: "Edit Task", Execute: func() error { logStore.AddLog("INFO", fmt.Sprintf("Editing task ID: %s", taskID), uuid, "Edit Task") - err := tw.EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskID, tags, project, start, entry, wait, end, depends, due) + err := tw.EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskID, tags, project, start, entry, wait, end, depends, due, recur) if err != nil { logStore.AddLog("ERROR", fmt.Sprintf("Failed to edit task ID %s: %v", taskID, err), uuid, "Edit Task") return err diff --git a/backend/models/request_body.go b/backend/models/request_body.go index 8e1d97ea..7f281c12 100644 --- a/backend/models/request_body.go +++ b/backend/models/request_body.go @@ -37,6 +37,7 @@ type EditTaskRequestBody struct { End string `json:"end"` Depends []string `json:"depends"` Due string `json:"due"` + Recur string `json:"recur"` } type CompleteTaskRequestBody struct { Email string `json:"email"` diff --git a/backend/utils/tw/edit_task.go b/backend/utils/tw/edit_task.go index fb7667f9..bc287aec 100644 --- a/backend/utils/tw/edit_task.go +++ b/backend/utils/tw/edit_task.go @@ -7,7 +7,7 @@ import ( "strings" ) -func EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskID string, tags []string, project string, start string, entry string, wait string, end string, depends []string, due string) error { +func EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskID string, tags []string, project string, start string, entry string, wait string, end string, depends []string, due string, recur string) error { if err := utils.ExecCommand("rm", "-rf", "/root/.task"); err != nil { return fmt.Errorf("error deleting Taskwarrior data: %v", err) } @@ -112,6 +112,13 @@ func EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskID st } } + // Handle recur - this will automatically set rtype field + if recur != "" { + if err := utils.ExecCommand("task", taskID, "modify", "recur:"+recur); err != nil { + return fmt.Errorf("failed to set recur %s: %v", recur, err) + } + } + // Sync Taskwarrior again if err := SyncTaskwarrior(tempDir); err != nil { return err diff --git a/backend/utils/tw/taskwarrior_test.go b/backend/utils/tw/taskwarrior_test.go index c8be55fb..cf35a627 100644 --- a/backend/utils/tw/taskwarrior_test.go +++ b/backend/utils/tw/taskwarrior_test.go @@ -23,7 +23,7 @@ func TestSyncTaskwarrior(t *testing.T) { } func TestEditTaskInATaskwarrior(t *testing.T) { - err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", nil, "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-30T18:30:00.000Z", nil, "2025-12-01T18:30:00.000Z") + err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", nil, "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-30T18:30:00.000Z", nil, "2025-12-01T18:30:00.000Z", "weekly") if err != nil { t.Errorf("EditTaskInTaskwarrior() failed: %v", err) } else { @@ -68,7 +68,7 @@ func TestAddTaskWithTags(t *testing.T) { } func TestEditTaskWithTagAddition(t *testing.T) { - err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", []string{"+urgent", "+important"}, "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-30T18:30:00.000Z", nil, "2025-12-01T18:30:00.000Z") + err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", []string{"+urgent", "+important"}, "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-30T18:30:00.000Z", nil, "2025-12-01T18:30:00.000Z", "daily") if err != nil { t.Errorf("EditTaskInTaskwarrior with tag addition failed: %v", err) } else { @@ -77,7 +77,7 @@ func TestEditTaskWithTagAddition(t *testing.T) { } func TestEditTaskWithTagRemoval(t *testing.T) { - err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", []string{"-work", "-lowpriority"}, "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-30T18:30:00.000Z", nil, "2025-12-01T18:30:00.000Z") + err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", []string{"-work", "-lowpriority"}, "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-30T18:30:00.000Z", nil, "2025-12-01T18:30:00.000Z", "monthly") if err != nil { t.Errorf("EditTaskInTaskwarrior with tag removal failed: %v", err) } else { @@ -86,7 +86,7 @@ func TestEditTaskWithTagRemoval(t *testing.T) { } func TestEditTaskWithMixedTagOperations(t *testing.T) { - err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", []string{"+urgent", "-work", "normal"}, "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-30T18:30:00.000Z", nil, "2025-12-01T18:30:00.000Z") + err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", []string{"+urgent", "-work", "normal"}, "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-30T18:30:00.000Z", nil, "2025-12-01T18:30:00.000Z", "yearly") if err != nil { t.Errorf("EditTaskInTaskwarrior with mixed tag operations failed: %v", err) } else { diff --git a/frontend/src/components/HomeComponents/DevLogs/DevLogs.tsx b/frontend/src/components/HomeComponents/DevLogs/DevLogs.tsx index f05e71f1..c5a2f75c 100644 --- a/frontend/src/components/HomeComponents/DevLogs/DevLogs.tsx +++ b/frontend/src/components/HomeComponents/DevLogs/DevLogs.tsx @@ -1,11 +1,4 @@ import React, { useState, useEffect } from 'react'; -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, -} from '../../ui/dialog'; import { Button } from '../../ui/button'; import { Select, @@ -28,10 +21,9 @@ interface LogEntry { interface DevLogsProps { isOpen: boolean; - onOpenChange: (open: boolean) => void; } -export const DevLogs: React.FC = ({ isOpen, onOpenChange }) => { +export const DevLogs: React.FC = ({ isOpen }) => { const [logs, setLogs] = useState([]); const [filteredLogs, setFilteredLogs] = useState([]); const [selectedLevel, setSelectedLevel] = useState('all'); @@ -116,111 +108,98 @@ export const DevLogs: React.FC = ({ isOpen, onOpenChange }) => { }; return ( - - - - Developer Logs - - View sync operation logs with timestamps and status information. - - - -
- + <> +
+ -
- - -
+
+ +
+
-
- {isLoading ? ( -
- Loading logs... -
- ) : filteredLogs.length === 0 ? ( -
- No logs available -
- ) : ( -
- {filteredLogs.map((log, index) => ( -
-
-
-
- - {formatTimestamp(log.timestamp)} - - - [{log.level}] +
+ {isLoading ? ( +
Loading logs...
+ ) : filteredLogs.length === 0 ? ( +
+ No logs available +
+ ) : ( +
+ {filteredLogs.map((log, index) => ( +
+
+
+
+ + {formatTimestamp(log.timestamp)} + + + [{log.level}] + + {log.operation && ( + + {log.operation} - {log.operation && ( - - {log.operation} - - )} -
-
- {log.message} -
- {log.syncId && ( -
- Sync ID: {log.syncId} -
)}
- +
+ {log.message} +
+ {log.syncId && ( +
+ Sync ID: {log.syncId} +
+ )}
+
- ))} -
- )} -
- -
+
+ ))} +
+ )} +
+ ); }; diff --git a/frontend/src/components/HomeComponents/DevLogs/__tests__/DevLogs.test.tsx b/frontend/src/components/HomeComponents/DevLogs/__tests__/DevLogs.test.tsx index 5487031d..d65fde28 100644 --- a/frontend/src/components/HomeComponents/DevLogs/__tests__/DevLogs.test.tsx +++ b/frontend/src/components/HomeComponents/DevLogs/__tests__/DevLogs.test.tsx @@ -1,15 +1,7 @@ import { render, waitFor, screen } from '@testing-library/react'; import { DevLogs } from '../DevLogs'; -// Mock UI components -jest.mock('../../../ui/dialog', () => ({ - Dialog: ({ children, open }: any) => (open ?
{children}
: null), - DialogContent: ({ children }: any) =>
{children}
, - DialogDescription: ({ children }: any) =>
{children}
, - DialogHeader: ({ children }: any) =>
{children}
, - DialogTitle: ({ children }: any) =>
{children}
, -})); - +// Mock UI components - DevLogs uses Button and Select components jest.mock('../../../ui/button', () => ({ Button: ({ children, ...props }: any) => ( @@ -17,9 +9,15 @@ jest.mock('../../../ui/button', () => ({ })); jest.mock('../../../ui/select', () => ({ - Select: ({ children }: any) =>
{children}
, + Select: ({ children, value }: any) => ( +
+ {children} +
+ ), SelectContent: ({ children }: any) =>
{children}
, - SelectItem: ({ children }: any) =>
{children}
, + SelectItem: ({ children, value }: any) => ( +
{children}
+ ), SelectTrigger: ({ children }: any) =>
{children}
, SelectValue: ({ placeholder }: any) =>
{placeholder}
, })); @@ -82,41 +80,43 @@ global.fetch = jest.fn(() => }) ) as jest.Mock; -describe('DevLogs Component using Snapshot', () => { - const mockOnOpenChange = jest.fn(); - +describe('DevLogs Content Component', () => { beforeEach(() => { jest.clearAllMocks(); }); - it('renders closed dialog correctly', () => { - const { asFragment } = render( - - ); - expect(asFragment()).toMatchSnapshot('devlogs-closed'); + it('renders initial state without fetching logs when isOpen is false', () => { + const { asFragment } = render(); + + // Should render the UI but not fetch logs + expect(screen.getByText('No logs available')).toBeInTheDocument(); + expect(fetch).not.toHaveBeenCalled(); + expect(asFragment()).toMatchSnapshot('devlogs-initial-state'); }); - it('renders open dialog with logs correctly', async () => { - const { asFragment } = render( - - ); + it('renders with logs when isOpen is true', async () => { + const { asFragment } = render(); await waitFor(() => { expect(screen.queryByText('Loading logs...')).not.toBeInTheDocument(); }); + // Verify logs are displayed + expect(screen.getByText('Sync operation started')).toBeInTheDocument(); + expect(screen.getByText('Warning message')).toBeInTheDocument(); + expect(screen.getByText('Error occurred')).toBeInTheDocument(); + expect(asFragment()).toMatchSnapshot('devlogs-with-logs'); }); - it('renders loading state correctly', () => { + it('renders loading state when fetching logs', () => { (fetch as jest.Mock).mockImplementationOnce( () => new Promise(() => {}) // Never resolves to keep loading state ); - const { asFragment } = render( - - ); + const { asFragment } = render(); + expect(screen.getByText('Loading logs...')).toBeInTheDocument(); expect(asFragment()).toMatchSnapshot('devlogs-loading'); }); @@ -128,9 +128,7 @@ describe('DevLogs Component using Snapshot', () => { }) ); - const { asFragment } = render( - - ); + const { asFragment } = render(); await waitFor(() => { expect(screen.getByText('No logs available')).toBeInTheDocument(); diff --git a/frontend/src/components/HomeComponents/DevLogs/__tests__/__snapshots__/DevLogs.test.tsx.snap b/frontend/src/components/HomeComponents/DevLogs/__tests__/__snapshots__/DevLogs.test.tsx.snap index 6d1656d8..1da379a7 100644 --- a/frontend/src/components/HomeComponents/DevLogs/__tests__/__snapshots__/DevLogs.test.tsx.snap +++ b/frontend/src/components/HomeComponents/DevLogs/__tests__/__snapshots__/DevLogs.test.tsx.snap @@ -1,329 +1,389 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`DevLogs Component using Snapshot renders closed dialog correctly: devlogs-closed 1`] = ``; - -exports[`DevLogs Component using Snapshot renders empty logs state correctly: devlogs-empty 1`] = ` +exports[`DevLogs Content Component renders empty logs state correctly: devlogs-empty 1`] = ` -
-
+
+
- Developer Logs -
-
- View sync operation logs with timestamps and status information. + Filter by level
-
-
-
-
- Filter by level -
-
-
-
- All Levels -
-
- INFO -
-
- WARN -
-
- ERROR -
-
+
+
+ All Levels
- - + INFO +
+
+ WARN
-
-
- No logs available + ERROR
+
+ + +
+
+
+
+ No logs available +
`; -exports[`DevLogs Component using Snapshot renders loading state correctly: devlogs-loading 1`] = ` +exports[`DevLogs Content Component renders initial state without fetching logs when isOpen is false: devlogs-initial-state 1`] = ` -
-
+
+
- Developer Logs + Filter by level
-
- View sync operation logs with timestamps and status information. +
+
+
+ All Levels +
+
+ INFO +
+
+ WARN +
+
+ ERROR
-
+
+ + +
+
+
+
+ No logs available +
+
+ +`; + +exports[`DevLogs Content Component renders loading state when fetching logs: devlogs-loading 1`] = ` + +
+
+
-
-
- Filter by level -
-
-
-
- All Levels -
-
- INFO -
-
- WARN -
-
- ERROR -
-
+ Filter by level
+
+
- - + All Levels +
+
+ INFO
-
-
- Loading logs... + WARN +
+
+ ERROR
+
+ + +
+
+
+
+ Loading logs... +
`; -exports[`DevLogs Component using Snapshot renders open dialog with logs correctly: devlogs-with-logs 1`] = ` +exports[`DevLogs Content Component renders with logs when isOpen is true: devlogs-with-logs 1`] = ` -
-
+
+
- Developer Logs + Filter by level
-
- View sync operation logs with timestamps and status information. +
+
+
+ All Levels +
+
+ INFO +
+
+ WARN +
+
+ ERROR
+
+
+ + +
+
+
+
-
-
-
- Filter by level -
-
-
-
- All Levels -
-
- INFO +
+
+
+ + Mon, 01 Jan 2024 12:00:00 GMT + + + [INFO] + + + SYNC_START +
-
- WARN +
+ Sync operation started
-
- ERROR +
+ Sync ID: sync-123
-
-
-
-
-
- - Mon, 01 Jan 2024 12:00:00 GMT - - - [INFO] - - - SYNC_START - -
-
- Sync operation started -
-
- Sync ID: sync-123 -
-
- + [WARN] +
-
-
-
-
- - Mon, 01 Jan 2024 12:01:00 GMT - - - [WARN] - -
-
- Warning message -
-
- + Warning message
+ +
+
+
+
-
+ Mon, 01 Jan 2024 12:02:00 GMT + + -
- - Mon, 01 Jan 2024 12:02:00 GMT - - - [ERROR] - - - SYNC_ERROR - -
-
- Error occurred -
-
- + SYNC_ERROR + +
+
+ Error occurred
+
diff --git a/frontend/src/components/HomeComponents/Navbar/NavbarDesktop.tsx b/frontend/src/components/HomeComponents/Navbar/NavbarDesktop.tsx index 2c250a71..9f25d774 100644 --- a/frontend/src/components/HomeComponents/Navbar/NavbarDesktop.tsx +++ b/frontend/src/components/HomeComponents/Navbar/NavbarDesktop.tsx @@ -31,6 +31,7 @@ import { DialogContent, DialogDescription, DialogHeader, + DialogTitle, DialogTrigger, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; @@ -78,7 +79,7 @@ export const NavbarDesktop = ( }; return ( - + <>
+ + + e.preventDefault()}> + + Developer Logs + + + + + Developer Logs + + View sync operation logs with timestamps and status + information. + + + + + window.open(url.githubRepoURL, '_blank')} > GitHub - - e.preventDefault()}> - - Export tasks - - + + + e.preventDefault()}> + + Export tasks + + + + + + Would you like to download your tasks as a JSON file or a + TXT file? + + +
+ + +
+
+
e.preventDefault()}>
@@ -161,61 +241,6 @@ export const NavbarDesktop = (
- - - - Would you like to download your tasks as a JSON file or a TXT file? - - -
- - -
-
- - - - - - Delete All Tasks? - - -
-

- Are you sure you want to delete all tasks for{' '} - {props.email}? -

-

- This action cannot be undone. All your tasks will be permanently - deleted from the local database. -

-
-
- - -
-
-
- + ); }; diff --git a/frontend/src/components/HomeComponents/Navbar/NavbarMobile.tsx b/frontend/src/components/HomeComponents/Navbar/NavbarMobile.tsx index 5295fd52..8c4191e1 100644 --- a/frontend/src/components/HomeComponents/Navbar/NavbarMobile.tsx +++ b/frontend/src/components/HomeComponents/Navbar/NavbarMobile.tsx @@ -85,7 +85,6 @@ export const NavbarMobile = ( return ( - -
{ - setIsDevLogsOpen(true); - props.setIsOpen(false); - }} - className={`w-[130px] cursor-pointer border ${buttonVariants({ - variant: 'secondary', - })}`} - > - - Developer Logs -
+ + +
{ + // setIsDevLogsOpen(true); + // props.setIsOpen(false); + // }} + className={`w-[130px] cursor-pointer border ${buttonVariants({ + variant: 'secondary', + })}`} + > + + Developer Logs +
+
+ + + Developer Logs + + View sync operation logs with timestamps and status + information. + + + + +
setIsDeleteConfirmOpen(true)} className={`w-[130px] cursor-pointer border ${buttonVariants({ @@ -241,7 +254,6 @@ export const NavbarMobile = ( - diff --git a/frontend/src/components/HomeComponents/Tasks/AddTaskDialog.tsx b/frontend/src/components/HomeComponents/Tasks/AddTaskDialog.tsx index d2ff68b8..04bfd690 100644 --- a/frontend/src/components/HomeComponents/Tasks/AddTaskDialog.tsx +++ b/frontend/src/components/HomeComponents/Tasks/AddTaskDialog.tsx @@ -127,7 +127,7 @@ export const AddTaskdialog = ({ diff --git a/frontend/src/components/HomeComponents/Tasks/EditTaskDialog.tsx b/frontend/src/components/HomeComponents/Tasks/EditTaskDialog.tsx index 29a47ec0..a48f4969 100644 --- a/frontend/src/components/HomeComponents/Tasks/EditTaskDialog.tsx +++ b/frontend/src/components/HomeComponents/Tasks/EditTaskDialog.tsx @@ -55,6 +55,7 @@ export const EditTaskDialog = ({ onSaveEndDate, onSaveDueDate, onSaveDepends, + onSaveRecur, onMarkComplete, onMarkDeleted, isOverdue, @@ -726,14 +727,6 @@ export const EditTaskDialog = ({ )} - - Recur: - {task.recur} - - - RType: - {task.rtype} - Priority: @@ -1097,6 +1090,108 @@ export const EditTaskDialog = ({ )} + + Recur: + + {editState.isEditingRecur ? ( +
+ + + +
+ ) : ( +
+ {task.recur || 'None'} + +
+ )} +
+
+ + RType: + + {task.rtype || 'None'} + {!task.rtype && ( + + (Auto-set by recur) + + )} + + Urgency: {task.urgency} diff --git a/frontend/src/components/HomeComponents/Tasks/ReportsView.tsx b/frontend/src/components/HomeComponents/Tasks/ReportsView.tsx index a45798d7..6be314cb 100644 --- a/frontend/src/components/HomeComponents/Tasks/ReportsView.tsx +++ b/frontend/src/components/HomeComponents/Tasks/ReportsView.tsx @@ -36,7 +36,9 @@ export const ReportsView: React.FC = ({ tasks }) => { }; const dailyData = [{ name: 'Today', ...countStatuses(today) }]; - const weeklyData = [{ name: 'This Week', ...countStatuses(startOfWeek) }]; + const sevenDaysAgo = getStartOfDay(new Date()); + sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7); + const weeklyData = [{ name: 'This Week', ...countStatuses(sevenDaysAgo) }]; const monthlyData = [{ name: 'This Month', ...countStatuses(startOfMonth) }]; return ( diff --git a/frontend/src/components/HomeComponents/Tasks/Tasks.tsx b/frontend/src/components/HomeComponents/Tasks/Tasks.tsx index 48e6b604..97566f0d 100644 --- a/frontend/src/components/HomeComponents/Tasks/Tasks.tsx +++ b/frontend/src/components/HomeComponents/Tasks/Tasks.tsx @@ -338,7 +338,8 @@ export const Tasks = ( wait: string, end: string, depends: string[], - due: string + due: string, + recur: string ) { try { await editTaskOnBackend({ @@ -356,6 +357,7 @@ export const Tasks = ( end, depends, due, + recur, }); console.log('Task edited successfully!'); @@ -422,7 +424,8 @@ export const Tasks = ( task.wait || '', task.end || '', task.depends || [], - task.due || '' + task.due || '', + task.recur || '' ); }; @@ -441,7 +444,8 @@ export const Tasks = ( task.wait || '', task.end || '', task.depends || [], - task.due || '' + task.due || '', + task.recur || '' ); }; @@ -461,7 +465,8 @@ export const Tasks = ( task.wait, task.end || '', task.depends || [], - task.due || '' + task.due || '', + task.recur || '' ); }; @@ -481,7 +486,8 @@ export const Tasks = ( task.wait || '', task.end || '', task.depends || [], - task.due || '' + task.due || '', + task.recur || '' ); }; @@ -501,7 +507,8 @@ export const Tasks = ( task.wait, task.end, task.depends || [], - task.due || '' + task.due || '', + task.recur || '' ); }; @@ -521,7 +528,8 @@ export const Tasks = ( task.wait, task.end, task.depends || [], - task.due || '' + task.due || '', + task.recur || '' ); }; @@ -541,7 +549,8 @@ export const Tasks = ( task.wait, task.end, task.depends || [], - task.due + task.due, + task.recur || '' ); }; @@ -561,7 +570,39 @@ export const Tasks = ( task.wait || '', task.end || '', task.depends, - task.due || '' + task.due || '', + task.recur || '' + ); + }; + + const handleRecurSaveClick = (task: Task, recur: string) => { + if (editState.editedRecur === 'none') { + updateEditState({ isEditingRecur: false }); + return; + } + + if (!editState.editedRecur || editState.editedRecur === '') { + updateEditState({ isEditingRecur: false }); + return; + } + + task.recur = recur; + + handleEditTaskOnBackend( + props.email, + props.encryptionSecret, + props.UUID, + task.description, + task.tags, + task.id.toString(), + task.project, + task.start, + task.entry || '', + task.wait || '', + task.end || '', + task.depends || [], + task.due || '', + task.recur ); }; @@ -659,7 +700,8 @@ export const Tasks = ( task.wait || '', task.end || '', task.depends || [], - task.due || '' + task.due || '', + task.recur || '' ); }; @@ -854,7 +896,7 @@ export const Tasks = ( options={uniqueProjects} selectedValues={selectedProjects} onSelectionChange={setSelectedProjects} - className="hidden sm:flex-1 min-w-[140px]" + className="hidden sm:flex min-w-[140px]" icon={} /> } /> } />
@@ -972,6 +1014,7 @@ export const Tasks = ( onSaveEndDate={handleEndDateSaveClick} onSaveDueDate={handleDueDateSaveClick} onSaveDepends={handleDependsSaveClick} + onSaveRecur={handleRecurSaveClick} onMarkComplete={handleMarkComplete} onMarkDeleted={handleMarkDelete} isOverdue={isOverdue} diff --git a/frontend/src/components/HomeComponents/Tasks/UseEditTask.tsx b/frontend/src/components/HomeComponents/Tasks/UseEditTask.tsx index a39ade88..0a339bc4 100644 --- a/frontend/src/components/HomeComponents/Tasks/UseEditTask.tsx +++ b/frontend/src/components/HomeComponents/Tasks/UseEditTask.tsx @@ -26,6 +26,9 @@ export const useEditTask = (selectedTask: Task | null) => { editedDepends: [], dependsDropdownOpen: false, dependsSearchTerm: '', + isEditingRecur: false, + editedRecur: '', + originalRecur: '', }); // Update edited tags when selected task changes @@ -37,6 +40,8 @@ export const useEditTask = (selectedTask: Task | null) => { editedDescription: selectedTask.description || '', editedPriority: selectedTask.priority || 'NONE', editedProject: selectedTask.project || '', + editedRecur: selectedTask.recur || '', + originalRecur: selectedTask.recur || '', })); } }, [selectedTask]); @@ -66,6 +71,9 @@ export const useEditTask = (selectedTask: Task | null) => { editedDepends: [], dependsDropdownOpen: false, dependsSearchTerm: '', + isEditingRecur: false, + editedRecur: '', + originalRecur: '', }); }; diff --git a/frontend/src/components/HomeComponents/Tasks/__tests__/EditTaskDialog.test.tsx b/frontend/src/components/HomeComponents/Tasks/__tests__/EditTaskDialog.test.tsx index 5e5d5d9d..56a1f14f 100644 --- a/frontend/src/components/HomeComponents/Tasks/__tests__/EditTaskDialog.test.tsx +++ b/frontend/src/components/HomeComponents/Tasks/__tests__/EditTaskDialog.test.tsx @@ -66,6 +66,9 @@ describe('EditTaskDialog Component', () => { editedDepends: mockTask.depends || [], dependsDropdownOpen: false, dependsSearchTerm: '', + isEditingRecur: false, + editedRecur: '', + originalRecur: '', }; const defaultProps = { @@ -88,6 +91,7 @@ describe('EditTaskDialog Component', () => { onSaveEndDate: jest.fn(), onSaveDueDate: jest.fn(), onSaveDepends: jest.fn(), + onSaveRecur: jest.fn(), onMarkComplete: jest.fn(), onMarkDeleted: jest.fn(), isOverdue: jest.fn(() => false), diff --git a/frontend/src/components/HomeComponents/Tasks/__tests__/Tasks.test.tsx b/frontend/src/components/HomeComponents/Tasks/__tests__/Tasks.test.tsx index de26d25d..a5f510dc 100644 --- a/frontend/src/components/HomeComponents/Tasks/__tests__/Tasks.test.tsx +++ b/frontend/src/components/HomeComponents/Tasks/__tests__/Tasks.test.tsx @@ -483,7 +483,7 @@ describe('Tasks Component', () => { fireEvent.click(screen.getByRole('button', { name: /add task/i })); const projectSelect = await screen.findByTestId('project-select'); - fireEvent.change(projectSelect, { target: { value: '' } }); + fireEvent.change(projectSelect, { target: { value: '__CREATE_NEW__' } }); // Empty string triggers "create new project" mode const newProjectInput = await screen.findByPlaceholderText('New project name'); diff --git a/frontend/src/components/HomeComponents/Tasks/__tests__/UseEditTask.test.ts b/frontend/src/components/HomeComponents/Tasks/__tests__/UseEditTask.test.ts index 075024da..605c64dc 100644 --- a/frontend/src/components/HomeComponents/Tasks/__tests__/UseEditTask.test.ts +++ b/frontend/src/components/HomeComponents/Tasks/__tests__/UseEditTask.test.ts @@ -51,6 +51,9 @@ describe('useEditTask Hook', () => { editedDepends: [], dependsDropdownOpen: false, dependsSearchTerm: '', + isEditingRecur: false, + editedRecur: '', + originalRecur: '', }); }); diff --git a/frontend/src/components/HomeComponents/Tasks/hooks.ts b/frontend/src/components/HomeComponents/Tasks/hooks.ts index f0931f96..d0c0d93d 100644 --- a/frontend/src/components/HomeComponents/Tasks/hooks.ts +++ b/frontend/src/components/HomeComponents/Tasks/hooks.ts @@ -95,6 +95,7 @@ export const editTaskOnBackend = async ({ end, depends, due, + recur, }: { email: string; encryptionSecret: string; @@ -110,6 +111,7 @@ export const editTaskOnBackend = async ({ end: string; depends: string[]; due: string; + recur: string; }) => { const response = await fetch(`${backendURL}edit-task`, { method: 'POST', @@ -127,6 +129,7 @@ export const editTaskOnBackend = async ({ end, depends, due, + recur, }), headers: { 'Content-Type': 'application/json', diff --git a/frontend/src/components/utils/types.ts b/frontend/src/components/utils/types.ts index 88552d8d..7555700b 100644 --- a/frontend/src/components/utils/types.ts +++ b/frontend/src/components/utils/types.ts @@ -83,6 +83,9 @@ export interface EditTaskState { editedDepends: string[]; dependsDropdownOpen: boolean; dependsSearchTerm: string; + isEditingRecur: boolean; + editedRecur: string; + originalRecur: string; } export interface TaskFormData { @@ -126,6 +129,7 @@ export interface EditTaskDialogProps { onSaveEndDate: (task: Task, date: string) => void; onSaveDueDate: (task: Task, date: string) => void; onSaveDepends: (task: Task, depends: string[]) => void; + onSaveRecur: (task: Task, recur: string) => void; onMarkComplete: (uuid: string) => void; onMarkDeleted: (uuid: string) => void; isOverdue: (due?: string) => boolean; From 9eec7954768eb289a1c41ddffc4541512b8cf04f Mon Sep 17 00:00:00 2001 From: Neeraj-gagat Date: Mon, 8 Dec 2025 18:50:53 +0530 Subject: [PATCH 7/8] changed names to TaskDialog --- .../{EditTaskDialog.tsx => TaskDialog.tsx} | 2 +- .../components/HomeComponents/Tasks/Tasks.tsx | 4 +- .../Tasks/__tests__/EditTaskDialog.test.tsx | 134 ++++++------------ 3 files changed, 46 insertions(+), 94 deletions(-) rename frontend/src/components/HomeComponents/Tasks/{EditTaskDialog.tsx => TaskDialog.tsx} (99%) diff --git a/frontend/src/components/HomeComponents/Tasks/EditTaskDialog.tsx b/frontend/src/components/HomeComponents/Tasks/TaskDialog.tsx similarity index 99% rename from frontend/src/components/HomeComponents/Tasks/EditTaskDialog.tsx rename to frontend/src/components/HomeComponents/Tasks/TaskDialog.tsx index 88c37da5..80a0c500 100644 --- a/frontend/src/components/HomeComponents/Tasks/EditTaskDialog.tsx +++ b/frontend/src/components/HomeComponents/Tasks/TaskDialog.tsx @@ -35,7 +35,7 @@ import { import CopyToClipboard from 'react-copy-to-clipboard'; import { formattedDate, handleCopy } from './tasks-utils'; -export const EditTaskDialog = ({ +export const TaskDialog = ({ index, task, isOpen, diff --git a/frontend/src/components/HomeComponents/Tasks/Tasks.tsx b/frontend/src/components/HomeComponents/Tasks/Tasks.tsx index 97566f0d..d9bf20e5 100644 --- a/frontend/src/components/HomeComponents/Tasks/Tasks.tsx +++ b/frontend/src/components/HomeComponents/Tasks/Tasks.tsx @@ -43,7 +43,7 @@ import { debounce } from '@/components/utils/utils'; import { Taskskeleton } from './TaskSkeleton'; import { Key } from '@/components/ui/key-button'; import { AddTaskdialog } from './AddTaskDialog'; -import { EditTaskDialog } from './EditTaskDialog'; +import { TaskDialog } from './TaskDialog'; import { TaskFormData } from '../../utils/types'; const db = new TasksDatabase(); @@ -991,7 +991,7 @@ export const Tasks = ( ) : ( currentTasks.map((task: Task, index: number) => ( - ({ @@ -9,7 +9,7 @@ jest.mock('react-copy-to-clipboard', () => ({ ), })); -describe('EditTaskDialog Component', () => { +describe('TaskDialog Component', () => { const mockTask: Task = { id: 1, modified: '', @@ -103,7 +103,7 @@ describe('EditTaskDialog Component', () => { describe('Rendering', () => { test('should render the task row with correct data', () => { - render(); + render(); expect(screen.getByText(mockTask.id.toString())).toBeInTheDocument(); expect(screen.getByText(mockTask.description)).toBeInTheDocument(); @@ -116,21 +116,21 @@ describe('EditTaskDialog Component', () => { isOverdue: jest.fn(() => true), }; - render(); + render(); const statusBadge = screen.getByText('O'); expect(statusBadge).toBeInTheDocument(); }); test('should display correct priority indicator', () => { - const { container } = render(); + const { container } = render(); const priorityIndicator = container.querySelector('.bg-red-500'); expect(priorityIndicator).toBeInTheDocument(); }); test('should render dialog content when opened', async () => { - render(); + render(); const dialog = await screen.findByRole('dialog'); expect(dialog).toBeInTheDocument(); @@ -145,7 +145,7 @@ describe('EditTaskDialog Component', () => { describe('Dialog Interactions', () => { test('should call onSelectTask when row is clicked', () => { - render(); + render(); const taskRow = screen.getByText(mockTask.description).closest('tr'); fireEvent.click(taskRow!); @@ -154,7 +154,7 @@ describe('EditTaskDialog Component', () => { }); test('should open dialog when trigger is clicked', async () => { - render(); + render(); const taskRow = screen.getByText(mockTask.description).closest('tr'); fireEvent.click(taskRow!); @@ -163,7 +163,7 @@ describe('EditTaskDialog Component', () => { }); test('should call onOpenChange when dialog state changes', () => { - render(); + render(); const taskRow = screen.getByTestId(`task-row-${mockTask.id}`); fireEvent.click(taskRow); @@ -176,11 +176,7 @@ describe('EditTaskDialog Component', () => { test('should enable edit mode when pencil icon is clicked', async () => { const editingState = { ...mockEditState, isEditing: false }; render( - + ); const editButton = screen @@ -199,11 +195,7 @@ describe('EditTaskDialog Component', () => { test('should update description when input changes', () => { const editingState = { ...mockEditState, isEditing: true }; render( - + ); const input = screen.getByDisplayValue(mockTask.description); @@ -217,11 +209,7 @@ describe('EditTaskDialog Component', () => { test('should save description when check icon is clicked', () => { const editingState = { ...mockEditState, isEditing: true }; render( - + ); const saveButton = screen @@ -240,11 +228,7 @@ describe('EditTaskDialog Component', () => { test('should cancel editing when X icon is clicked', () => { const editingState = { ...mockEditState, isEditing: true }; render( - + ); const cancelButtons = screen @@ -260,13 +244,13 @@ describe('EditTaskDialog Component', () => { describe('Priority Editing', () => { test('should display current priority correctly', () => { - render(); + render(); expect(screen.getByText('High (H)')).toBeInTheDocument(); }); test('should enable priority editing mode', () => { - render(); + render(); const priorityRow = screen.getByText('Priority:').closest('tr'); const editButton = priorityRow?.querySelector('button'); @@ -287,11 +271,7 @@ describe('EditTaskDialog Component', () => { editedPriority: 'M', }; render( - + ); const saveButton = screen @@ -307,14 +287,14 @@ describe('EditTaskDialog Component', () => { describe('Tags Editing', () => { test('should display existing tags', () => { - render(); + render(); expect(screen.getByText('tag1')).toBeInTheDocument(); expect(screen.getByText('tag2')).toBeInTheDocument(); }); test('should enable tags editing mode', () => { - render(); + render(); const tagsRow = screen.getByText('Tags:').closest('tr'); const editButton = tagsRow?.querySelector('button'); @@ -338,11 +318,7 @@ describe('EditTaskDialog Component', () => { }; render( - + ); const input = screen.getByPlaceholderText( @@ -364,11 +340,7 @@ describe('EditTaskDialog Component', () => { }; render( - + ); const removeButtons = screen.getAllByText('✖'); @@ -386,11 +358,7 @@ describe('EditTaskDialog Component', () => { }; render( - + ); const saveButton = screen @@ -410,14 +378,14 @@ describe('EditTaskDialog Component', () => { describe('Project Editing', () => { test('should display current project', () => { - render(); + render(); const projectCells = screen.getAllByText(mockTask.project); expect(projectCells.length).toBeGreaterThan(0); }); test('should enable project editing mode', () => { - render(); + render(); const projectRow = screen.getByText('Project:').closest('tr'); const editButton = projectRow?.querySelector('button'); @@ -439,11 +407,7 @@ describe('EditTaskDialog Component', () => { }; render( - + ); const saveButton = screen @@ -468,14 +432,14 @@ describe('EditTaskDialog Component', () => { }; render( - + ); expect(screen.getByText('Dependency Task')).toBeInTheDocument(); }); test('should enable dependencies editing mode', () => { - render(); + render(); const dependsRow = screen.getByText('Depends:').closest('tr'); const editButton = dependsRow?.querySelector('button'); @@ -497,11 +461,7 @@ describe('EditTaskDialog Component', () => { }; render( - + ); const addButton = screen.getByText('Add Dependency'); @@ -520,11 +480,7 @@ describe('EditTaskDialog Component', () => { }; render( - + ); const saveButton = screen @@ -542,7 +498,7 @@ describe('EditTaskDialog Component', () => { describe('Date Editing', () => { test('should enable due date editing mode', () => { - render(); + render(); const dueRow = screen.getByText('Due:').closest('tr'); const editButton = dueRow?.querySelector('button'); @@ -564,11 +520,7 @@ describe('EditTaskDialog Component', () => { }; render( - + ); const saveButton = screen @@ -587,7 +539,7 @@ describe('EditTaskDialog Component', () => { describe('Task Actions', () => { test('should display Mark As Completed button for pending tasks', () => { - render(); + render(); expect(screen.getByText(/Mark As Completed/)).toBeInTheDocument(); }); @@ -595,14 +547,14 @@ describe('EditTaskDialog Component', () => { test('should not display Mark As Completed button for completed tasks', () => { const completedTask = { ...mockTask, status: 'completed' }; render( - + ); expect(screen.queryByText(/Mark As Completed/)).not.toBeInTheDocument(); }); test('should display delete button for non-deleted tasks', () => { - render(); + render(); const deleteButton = document.getElementById( `mark-task-as-deleted-${mockTask.id}` @@ -611,7 +563,7 @@ describe('EditTaskDialog Component', () => { }); test('should call onMarkComplete when confirmed', () => { - render(); + render(); const markCompleteButton = screen.getByText(/Mark As Completed/); fireEvent.click(markCompleteButton); @@ -624,7 +576,7 @@ describe('EditTaskDialog Component', () => { }); test('should call onMarkDeleted when confirmed', () => { - render(); + render(); const deleteButton = screen.getByRole('button', { name: /^d$/i, @@ -641,13 +593,13 @@ describe('EditTaskDialog Component', () => { describe('UUID Copy Functionality', () => { test('should display UUID in dialog', () => { - render(); + render(); expect(screen.getByText(mockTask.uuid)).toBeInTheDocument(); }); test('should have copy button for UUID', () => { - render(); + render(); const uuidRow = screen.getByText('UUID:').closest('tr'); const copyButton = uuidRow?.querySelector('button'); @@ -658,7 +610,7 @@ describe('EditTaskDialog Component', () => { describe('Selected State', () => { test('should highlight selected task row', () => { const { container } = render( - + ); const taskRow = container.querySelector('.bg-muted\\/50'); @@ -666,7 +618,7 @@ describe('EditTaskDialog Component', () => { }); test('should not highlight non-selected task row', () => { - render(); + render(); const taskRow = screen.getByText(mockTask.description).closest('tr'); expect(taskRow).not.toHaveClass('bg-muted/50'); @@ -675,21 +627,21 @@ describe('EditTaskDialog Component', () => { describe('Status Display', () => { test('should display P badge for pending tasks', () => { - render(); + render(); expect(screen.getByText('P')).toBeInTheDocument(); }); test('should display C badge for completed tasks', () => { const completedTask = { ...mockTask, status: 'completed' }; - render(); + render(); expect(screen.getByText('C')).toBeInTheDocument(); }); test('should display D badge for deleted tasks', () => { const deletedTask = { ...mockTask, status: 'deleted' }; - render(); + render(); expect(screen.getByText('D')).toBeInTheDocument(); }); @@ -700,7 +652,7 @@ describe('EditTaskDialog Component', () => { isOverdue: jest.fn(() => true), }; - render(); + render(); expect(screen.getByText('O')).toBeInTheDocument(); }); From b647481c4aed1c04ea53360e57a04e0ffebe13eb Mon Sep 17 00:00:00 2001 From: Neeraj-gagat Date: Mon, 8 Dec 2025 19:01:49 +0530 Subject: [PATCH 8/8] fixed name of testfile --- .../__tests__/{EditTaskDialog.test.tsx => TaskDialog.test.tsx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename frontend/src/components/HomeComponents/Tasks/__tests__/{EditTaskDialog.test.tsx => TaskDialog.test.tsx} (100%) diff --git a/frontend/src/components/HomeComponents/Tasks/__tests__/EditTaskDialog.test.tsx b/frontend/src/components/HomeComponents/Tasks/__tests__/TaskDialog.test.tsx similarity index 100% rename from frontend/src/components/HomeComponents/Tasks/__tests__/EditTaskDialog.test.tsx rename to frontend/src/components/HomeComponents/Tasks/__tests__/TaskDialog.test.tsx