@@ -75,6 +75,7 @@ export const subtasks = pgTable('subtasks', {
|
||||
id: text('id').primaryKey(),
|
||||
userId: text('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
|
||||
taskId: text('task_id').notNull().references(() => tasks.id, { onDelete: 'cascade' }),
|
||||
parentSubtaskId: text('parent_subtask_id').references(() => subtasks.id, { onDelete: 'cascade' }),
|
||||
title: text('title').notNull(),
|
||||
description: text('description').notNull().default(''),
|
||||
priority: text('priority', { enum: ['none', 'low', 'medium', 'high', 'critical'] }).notNull().default('none'),
|
||||
|
||||
@@ -11,6 +11,17 @@ const router = Router();
|
||||
|
||||
router.use(authMiddleware);
|
||||
|
||||
// Helper to build nested subtask tree
|
||||
const buildSubtaskTree = (allSubtasks: any[], parentId: string | null = null): any[] => {
|
||||
return allSubtasks
|
||||
.filter((s) => s.parentSubtaskId === parentId)
|
||||
.sort((a, b) => a.order - b.order)
|
||||
.map((s) => ({
|
||||
...s,
|
||||
subtasks: buildSubtaskTree(allSubtasks, s.id),
|
||||
}));
|
||||
};
|
||||
|
||||
router.get('/task/:taskId', asyncHandler(async (req: Request, res: Response) => {
|
||||
const userId = req.user!.userId;
|
||||
|
||||
@@ -31,7 +42,9 @@ router.get('/task/:taskId', asyncHandler(async (req: Request, res: Response) =>
|
||||
.where(and(eq(subtasks.taskId, req.params.taskId), eq(subtasks.userId, userId)))
|
||||
.orderBy(asc(subtasks.order));
|
||||
|
||||
res.json({ subtasks: taskSubtasks });
|
||||
const nestedSubtasks = buildSubtaskTree(taskSubtasks);
|
||||
|
||||
res.json({ subtasks: nestedSubtasks });
|
||||
}));
|
||||
|
||||
router.post('/task/:taskId', asyncHandler(async (req: Request, res: Response) => {
|
||||
@@ -49,10 +62,16 @@ router.post('/task/:taskId', asyncHandler(async (req: Request, res: Response) =>
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
const parentSubtaskId = data.parentSubtaskId || null;
|
||||
|
||||
const maxOrder = await db
|
||||
.select({ order: subtasks.order })
|
||||
.from(subtasks)
|
||||
.where(and(eq(subtasks.taskId, req.params.taskId), eq(subtasks.userId, userId)))
|
||||
.where(and(
|
||||
eq(subtasks.taskId, req.params.taskId),
|
||||
eq(subtasks.userId, userId),
|
||||
parentSubtaskId ? eq(subtasks.parentSubtaskId, parentSubtaskId) : eq(subtasks.parentSubtaskId, null)
|
||||
))
|
||||
.orderBy(desc(subtasks.order))
|
||||
.limit(1);
|
||||
|
||||
@@ -62,6 +81,7 @@ router.post('/task/:taskId', asyncHandler(async (req: Request, res: Response) =>
|
||||
id: subtaskId,
|
||||
userId,
|
||||
taskId: req.params.taskId,
|
||||
parentSubtaskId,
|
||||
title: data.title,
|
||||
description: data.description ?? '',
|
||||
priority: data.priority ?? 'none',
|
||||
|
||||
@@ -69,6 +69,7 @@ export const subtaskCreateSchema = z.object({
|
||||
reminders: z.string().max(100).optional(),
|
||||
assigneeId: z.string().nullable().optional(),
|
||||
order: z.number().int().min(0).optional(),
|
||||
parentSubtaskId: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
export const subtaskUpdateSchema = z.object({
|
||||
@@ -88,6 +89,7 @@ export const subtaskUpdateSchema = z.object({
|
||||
reminders: z.string().max(100).optional(),
|
||||
assigneeId: z.string().nullable().optional(),
|
||||
order: z.number().int().min(0).optional(),
|
||||
parentSubtaskId: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
export const userSettingsSchema = z.object({
|
||||
|
||||
Reference in New Issue
Block a user