Skip to content

Commit ee10b42

Browse files
committed
Refactor TaskDialog to use individual input change handlers; update KanbanBoard to debounce title and description changes; enhance ProjectList with new styles and navigation; modify Sidebar to remove unnecessary props; clean up styles in main layout; adjust ProjectPage to navigate after project updates; improve TaskListPage with new subtask handling and inline editing for main task descriptions.
1 parent 2ae9e56 commit ee10b42

11 files changed

Lines changed: 770 additions & 288 deletions

File tree

src/components/dialogs/CreateMainTaskDialog.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,7 @@ export default function CreateMainTaskDialog({
482482
isLoadingCategories={isLoadingCategories}
483483
categoriesError={null}
484484
hideProjectField={true}
485+
hideMainTaskField={true}
485486
currentUser={currentUser}
486487
isSubmitting={isSubmittingSubTask}
487488
submitError={subTaskSubmitError}

src/components/dialogs/CreateTaskDialog.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,17 @@ export interface CreateTaskDialogProps {
2727
createdBy: string;
2828
category: string;
2929
projectId?: string | null;
30+
mainTaskId?: string | null;
3031
};
3132
onInputChange: (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => void;
3233
onSubmit: (e: React.FormEvent) => void;
3334
assignableUsers?: User[];
3435
projects?: Project[];
36+
// Optional list of main tasks to attach the subtask to
37+
mainTasks?: { id: string; title: string }[];
38+
isLoadingMainTasks?: boolean;
39+
mainTasksError?: string | null;
40+
hideMainTaskField?: boolean;
3541
isLoadingProjects?: boolean;
3642
projectsError?: string | null;
3743
categories?: Category[];
@@ -63,6 +69,10 @@ export default function CreateTaskDialog({
6369
isLoadingCategories = false,
6470
categoriesError = null,
6571
hideProjectField = false,
72+
mainTasks = [],
73+
isLoadingMainTasks = false,
74+
mainTasksError = null,
75+
hideMainTaskField = false,
6676
currentUser = null
6777
}: CreateTaskDialogProps) {
6878
const [editingTitle, setEditingTitle] = useState(false);
@@ -95,6 +105,7 @@ export default function CreateTaskDialog({
95105
onInputChange(syntheticEvent);
96106
}
97107

108+
98109
function handleTitleBlur() {
99110
setEditingTitle(false);
100111
}
@@ -295,6 +306,24 @@ export default function CreateTaskDialog({
295306
)}
296307
</Field>
297308
)}
309+
{!hideMainTaskField && (
310+
<Field label="Main Task" style={{ flex: 1 }}>
311+
<Select name="mainTaskId" value={form.mainTaskId || ''} onChange={onInputChange} disabled={isLoadingMainTasks || !(mainTasks && mainTasks.length > 0)}>
312+
<option value="">{isLoadingMainTasks ? 'Loading main tasks…' : 'Select main task'}</option>
313+
{!isLoadingMainTasks && (!mainTasks || mainTasks.length === 0) && (
314+
<option value="" disabled>No main tasks available</option>
315+
)}
316+
{!isLoadingMainTasks && mainTasks && mainTasks.map(mt => (
317+
<option key={mt.id} value={mt.id}>{mt.title}</option>
318+
))}
319+
</Select>
320+
{mainTasksError && (
321+
<span style={{ color: tokens.colorPaletteRedForeground3, fontSize: tokens.fontSizeBase100 }}>
322+
{mainTasksError}
323+
</span>
324+
)}
325+
</Field>
326+
)}
298327
<Field label="Category" style={{ flex: 1 }}>
299328
<Select name="category" value={form.category} onChange={onInputChange} disabled={isLoadingCategories || (categories.length === 0 && !hideProjectField && !form.projectId)}>
300329
<option value="">{isLoadingCategories ? 'Loading categories…' : 'Select category'}</option>

src/components/dialogs/EditMainTaskDialog.tsx

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,7 @@ export default function EditMainTaskDialog({
541541
categories={categories}
542542
isLoadingCategories={isLoadingCategories}
543543
hideProjectField={true}
544+
hideMainTaskField={true}
544545
currentUser={currentUser}
545546
/>
546547

@@ -555,11 +556,28 @@ export default function EditMainTaskDialog({
555556
}
556557
}}
557558
form={editSubTaskForm}
558-
onInputChange={(e) => {
559-
const { name, value } = e.target;
560-
setEditSubTaskForm(prev => ({ ...prev, [name]: value }));
559+
onTitleChange={(title: string) => {
560+
setEditSubTaskForm(prev => ({ ...prev, title }));
561561
}}
562-
onSubmit={async (e) => {
562+
onDescriptionChange={(description: string) => {
563+
setEditSubTaskForm(prev => ({ ...prev, description }));
564+
}}
565+
onPriorityChange={(priority: string) => {
566+
setEditSubTaskForm(prev => ({ ...prev, priority }));
567+
}}
568+
onStatusChange={(status: string) => {
569+
setEditSubTaskForm(prev => ({ ...prev, status }));
570+
}}
571+
onStartDateChange={(startDate: string) => {
572+
setEditSubTaskForm(prev => ({ ...prev, startDate }));
573+
}}
574+
onEndDateChange={(endDate: string) => {
575+
setEditSubTaskForm(prev => ({ ...prev, endDate }));
576+
}}
577+
onAssignedToChange={(assignedTo: string[]) => {
578+
setEditSubTaskForm(prev => ({ ...prev, assignedTo }));
579+
}}
580+
onSubmit={async (e: React.FormEvent) => {
563581
e.preventDefault();
564582
if (!selectedSubTask?.id) return;
565583

src/components/dialogs/EditTaskDialog.tsx

Lines changed: 61 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,13 @@ export interface EditTaskDialogProps {
3333
projectId?: string | null;
3434
comments?: string;
3535
};
36-
onInputChange: (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => void;
36+
onTitleChange?: (title: string) => Promise<void> | void;
37+
onDescriptionChange?: (description: string) => Promise<void> | void;
38+
onPriorityChange?: (priority: string) => Promise<void> | void;
39+
onStatusChange?: (status: string) => Promise<void> | void;
40+
onStartDateChange?: (startDate: string) => Promise<void> | void;
41+
onEndDateChange?: (endDate: string) => Promise<void> | void;
42+
onAssignedToChange?: (assignedTo: string[]) => Promise<void> | void;
3743
onSubmit: (e: React.FormEvent) => void;
3844
assignableUsers?: User[];
3945
isLoadingAssignableUsers?: boolean;
@@ -67,7 +73,13 @@ export default function EditTaskDialog({
6773
open,
6874
onOpenChange,
6975
form,
70-
onInputChange,
76+
onTitleChange,
77+
onDescriptionChange,
78+
onPriorityChange,
79+
onStatusChange,
80+
onStartDateChange,
81+
onEndDateChange,
82+
onAssignedToChange,
7183
onSubmit,
7284
onDeleteClick,
7385
isSubmitting = false,
@@ -104,17 +116,9 @@ export default function EditTaskDialog({
104116

105117
function handleAssignedUserSelect(_: unknown, data: { optionValue?: string | number; selectedOptions: string[] }) {
106118
const selectedIds = data.selectedOptions;
107-
108-
const syntheticEvent = {
109-
target: {
110-
name: 'assignedTo',
111-
value: selectedIds,
112-
tagName: 'SELECT',
113-
type: 'select-multiple',
114-
},
115-
} as unknown as React.ChangeEvent<HTMLSelectElement>;
116-
117-
onInputChange(syntheticEvent);
119+
if (onAssignedToChange) {
120+
onAssignedToChange(selectedIds);
121+
}
118122
}
119123

120124
function handleTitleBlur() {
@@ -140,32 +144,25 @@ export default function EditTaskDialog({
140144
return isNaN(date.getTime()) ? undefined : date;
141145
}
142146

143-
// Helper to convert Date to YYYY-MM-DD string
147+
// Helper to convert Date to YYYY-MM-DD string using local timezone
144148
function formatDateToString(date: Date): string {
145-
return date.toISOString().split('T')[0];
149+
const year = date.getFullYear();
150+
const month = String(date.getMonth() + 1).padStart(2, '0');
151+
const day = String(date.getDate()).padStart(2, '0');
152+
return `${year}-${month}-${day}`;
146153
}
147154

148155
function handleStartDateSelect(date: Date): void {
149-
const syntheticEvent = {
150-
target: {
151-
name: 'startDate',
152-
value: formatDateToString(date),
153-
type: 'date',
154-
},
155-
} as React.ChangeEvent<HTMLInputElement>;
156-
onInputChange(syntheticEvent);
156+
if (onStartDateChange) {
157+
onStartDateChange(formatDateToString(date));
158+
}
157159
setStartDatePopoverOpen(false);
158160
}
159161

160162
function handleEndDateSelect(date: Date): void {
161-
const syntheticEvent = {
162-
target: {
163-
name: 'endDate',
164-
value: formatDateToString(date),
165-
type: 'date',
166-
},
167-
} as React.ChangeEvent<HTMLInputElement>;
168-
onInputChange(syntheticEvent);
163+
if (onEndDateChange) {
164+
onEndDateChange(formatDateToString(date));
165+
}
169166
setEndDatePopoverOpen(false);
170167
}
171168

@@ -200,7 +197,11 @@ export default function EditTaskDialog({
200197
<Input
201198
name="title"
202199
value={form.title}
203-
onChange={onInputChange}
200+
onChange={(e) => {
201+
if (onTitleChange) {
202+
onTitleChange(e.target.value);
203+
}
204+
}}
204205
onBlur={handleTitleBlur}
205206
onKeyDown={handleTitleKeyDown}
206207
ref={inputRef}
@@ -406,19 +407,44 @@ export default function EditTaskDialog({
406407
</div>
407408
{/* Row 3: Description */}
408409
<Field label="Description" style={{ marginBottom: 16 }}>
409-
<Input name="description" value={form.description} onChange={onInputChange} placeholder="Build low-fidelity wireframes for the dashboard layout and task management screens." />
410+
<Input
411+
name="description"
412+
value={form.description}
413+
onChange={(e) => {
414+
if (onDescriptionChange) {
415+
onDescriptionChange(e.target.value);
416+
}
417+
}}
418+
placeholder="Build low-fidelity wireframes for the dashboard layout and task management screens."
419+
/>
410420
</Field>
411421
{/* Row 4: Status, Priority */}
412422
<div style={{ display: 'flex', gap: 16, marginBottom: 16 }}>
413423
<Field label="Status" style={{ flex: 1 }}>
414-
<Select name="status" value={form.status} onChange={onInputChange}>
424+
<Select
425+
name="status"
426+
value={form.status}
427+
onChange={(e) => {
428+
if (onStatusChange) {
429+
onStatusChange(e.target.value);
430+
}
431+
}}
432+
>
415433
<option value="To Do">To Do</option>
416434
<option value="In Progress">In Progress</option>
417435
<option value="Done">Done</option>
418436
</Select>
419437
</Field>
420438
<Field label="Priority" style={{ flex: 1 }}>
421-
<Select name="priority" value={form.priority} onChange={onInputChange}>
439+
<Select
440+
name="priority"
441+
value={form.priority}
442+
onChange={(e) => {
443+
if (onPriorityChange) {
444+
onPriorityChange(e.target.value);
445+
}
446+
}}
447+
>
422448
<option value="Low">Low</option>
423449
<option value="Medium">Medium</option>
424450
<option value="Important">Important</option>

src/components/dialogs/TaskDialog.tsx

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,67 @@ export interface TaskDialogProps {
5858
}
5959

6060
export default function TaskDialog(props: TaskDialogProps) {
61+
// Convert onInputChange to individual handlers for EditTaskDialog
62+
const createIndividualHandlers = (onInputChange: (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => void) => ({
63+
onTitleChange: (title: string) => {
64+
const event = {
65+
target: { name: 'title', value: title }
66+
} as unknown as React.ChangeEvent<HTMLInputElement>;
67+
onInputChange(event);
68+
},
69+
onDescriptionChange: (description: string) => {
70+
const event = {
71+
target: { name: 'description', value: description }
72+
} as unknown as React.ChangeEvent<HTMLTextAreaElement>;
73+
onInputChange(event);
74+
},
75+
onPriorityChange: (priority: string) => {
76+
const event = {
77+
target: { name: 'priority', value: priority }
78+
} as unknown as React.ChangeEvent<HTMLSelectElement>;
79+
onInputChange(event);
80+
},
81+
onStatusChange: (status: string) => {
82+
const event = {
83+
target: { name: 'status', value: status }
84+
} as unknown as React.ChangeEvent<HTMLSelectElement>;
85+
onInputChange(event);
86+
},
87+
onStartDateChange: (startDate: string) => {
88+
const event = {
89+
target: { name: 'startDate', value: startDate }
90+
} as unknown as React.ChangeEvent<HTMLInputElement>;
91+
onInputChange(event);
92+
},
93+
onEndDateChange: (endDate: string) => {
94+
const event = {
95+
target: { name: 'endDate', value: endDate }
96+
} as unknown as React.ChangeEvent<HTMLInputElement>;
97+
onInputChange(event);
98+
},
99+
onAssignedToChange: (assignedTo: string[]) => {
100+
const event = {
101+
target: { name: 'assignedTo', value: assignedTo.join(',') }
102+
} as unknown as React.ChangeEvent<HTMLInputElement>;
103+
onInputChange(event);
104+
},
105+
});
106+
107+
const handlers = createIndividualHandlers(props.onInputChange);
108+
61109
if (props.dialogMode === 'edit') {
62110
return (
63111
<EditTaskDialog
64112
open={props.open}
65113
onOpenChange={props.onOpenChange}
66114
form={props.form}
67-
onInputChange={props.onInputChange}
115+
onTitleChange={handlers.onTitleChange}
116+
onDescriptionChange={handlers.onDescriptionChange}
117+
onPriorityChange={handlers.onPriorityChange}
118+
onStatusChange={handlers.onStatusChange}
119+
onStartDateChange={handlers.onStartDateChange}
120+
onEndDateChange={handlers.onEndDateChange}
121+
onAssignedToChange={handlers.onAssignedToChange}
68122
onSubmit={props.onSubmit}
69123
assignableUsers={props.assignableUsers}
70124
isLoadingAssignableUsers={props.isLoadingAssignableUsers}

0 commit comments

Comments
 (0)