"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const express_1 = require("express"); const asyncHandler_1 = require("../utils/asyncHandler"); const db_1 = require("../db"); const schema_1 = require("../db/schema"); const drizzle_orm_1 = require("drizzle-orm"); const auth_1 = require("../utils/auth"); const errorHandler_1 = require("../middleware/errorHandler"); const validation_1 = require("../utils/validation"); const zod_1 = require("zod"); const router = (0, express_1.Router)(); router.use(auth_1.authMiddleware); function applyTaskFilters(query, userId, filters) { const conditions = [(0, drizzle_orm_1.eq)(schema_1.tasks.userId, userId)]; if (filters.categoryId) { conditions.push((0, drizzle_orm_1.eq)(schema_1.tasks.categoryId, filters.categoryId)); } if (filters.completed !== undefined) { conditions.push((0, drizzle_orm_1.eq)(schema_1.tasks.completed, filters.completed)); } if (filters.dueBefore) { conditions.push((0, drizzle_orm_1.lte)(schema_1.tasks.dueDate, filters.dueBefore)); } if (filters.dueAfter) { conditions.push((0, drizzle_orm_1.gte)(schema_1.tasks.dueDate, filters.dueAfter)); } if (filters.priority) { conditions.push((0, drizzle_orm_1.eq)(schema_1.tasks.priority, filters.priority)); } return query.where((0, drizzle_orm_1.and)(...conditions)); } function applyTaskSorting(query, sortBy = 'dueDate', sortOrder = 'asc') { const orderFn = sortOrder === 'desc' ? drizzle_orm_1.desc : drizzle_orm_1.asc; const columnMap = { dueDate: schema_1.tasks.dueDate, priority: schema_1.tasks.priority, createdAt: schema_1.tasks.createdAt, title: schema_1.tasks.title, }; return query.orderBy(orderFn(columnMap[sortBy] || schema_1.tasks.dueDate)); } router.get('/', (0, asyncHandler_1.asyncHandler)(async (req, res) => { const filters = validation_1.taskQuerySchema.parse(req.query); const userId = req.user.userId; let query = db_1.db.select().from(schema_1.tasks); query = applyTaskFilters(query, userId, filters); query = applyTaskSorting(query, filters.sortBy, filters.sortOrder); const limit = filters.limit ?? 50; const offset = filters.offset ?? 0; query = query.limit(limit).offset(offset); const results = await query; // Fetch subtasks for each task const taskIds = results.map((t) => t.id); let taskSubtasks = []; if (taskIds.length > 0) { taskSubtasks = await db_1.db .select() .from(schema_1.subtasks) .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.subtasks.userId, userId), (0, drizzle_orm_1.inArray)(schema_1.subtasks.taskId, taskIds))) .orderBy((0, drizzle_orm_1.asc)(schema_1.subtasks.order)); } const subtasksByTask = taskSubtasks.reduce((acc, st) => { if (!acc[st.taskId]) acc[st.taskId] = []; acc[st.taskId].push(st); return acc; }, {}); const tasksWithSubtasks = results.map((task) => ({ ...task, subtasks: subtasksByTask[task.id] || [], })); // Get total count const countQuery = db_1.db.select({ count: (0, drizzle_orm_1.sql) `count(*)` }).from(schema_1.tasks); const countResult = await applyTaskFilters(countQuery, userId, filters); const total = Number(countResult[0]?.count ?? 0); res.json({ tasks: tasksWithSubtasks, total, limit, offset }); })); router.get('/:id', (0, asyncHandler_1.asyncHandler)(async (req, res) => { const userId = req.user.userId; const task = await db_1.db .select() .from(schema_1.tasks) .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.tasks.id, req.params.id), (0, drizzle_orm_1.eq)(schema_1.tasks.userId, userId))) .limit(1); if (task.length === 0) { throw new errorHandler_1.AppError('NOT_FOUND', 'Task not found', 404); } const taskSubtasks = await db_1.db .select() .from(schema_1.subtasks) .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.subtasks.taskId, req.params.id), (0, drizzle_orm_1.eq)(schema_1.subtasks.userId, userId))) .orderBy((0, drizzle_orm_1.asc)(schema_1.subtasks.order)); res.json({ ...task[0], subtasks: taskSubtasks }); })); router.post('/', (0, asyncHandler_1.asyncHandler)(async (req, res) => { const data = validation_1.taskCreateSchema.parse(req.body); const userId = req.user.userId; const now = Date.now(); // Verify category exists and belongs to user const cat = await db_1.db .select() .from(schema_1.categories) .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.categories.id, data.categoryId), (0, drizzle_orm_1.eq)(schema_1.categories.userId, userId))) .limit(1); if (cat.length === 0) { throw new errorHandler_1.AppError('NOT_FOUND', 'Category not found', 404); } const taskId = `task_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`; const newTask = { id: taskId, userId, categoryId: data.categoryId, title: data.title, description: data.description ?? '', priority: data.priority ?? 'none', completed: false, dueDate: data.dueDate ?? 0, dueTime: data.dueTime ?? '', endTime: data.endTime ?? '', assigneeId: data.assigneeId ?? null, createdAt: now, updatedAt: now, }; await db_1.db.insert(schema_1.tasks).values(newTask); // Create subtasks if provided if (data.subtasks && data.subtasks.length > 0) { const subtaskValues = data.subtasks.map((st, index) => ({ id: `sub_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}${index}`, userId, taskId, title: st.title, completed: false, order: index, createdAt: now, updatedAt: now, })); await db_1.db.insert(schema_1.subtasks).values(subtaskValues); } const createdSubtasks = await db_1.db .select() .from(schema_1.subtasks) .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.subtasks.taskId, taskId), (0, drizzle_orm_1.eq)(schema_1.subtasks.userId, userId))) .orderBy((0, drizzle_orm_1.asc)(schema_1.subtasks.order)); res.status(201).json({ ...newTask, subtasks: createdSubtasks }); })); router.patch('/:id', (0, asyncHandler_1.asyncHandler)(async (req, res) => { const data = validation_1.taskUpdateSchema.parse(req.body); const userId = req.user.userId; const existing = await db_1.db .select() .from(schema_1.tasks) .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.tasks.id, req.params.id), (0, drizzle_orm_1.eq)(schema_1.tasks.userId, userId))) .limit(1); if (existing.length === 0) { throw new errorHandler_1.AppError('NOT_FOUND', 'Task not found', 404); } // Verify category if provided if (data.categoryId) { const cat = await db_1.db .select() .from(schema_1.categories) .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.categories.id, data.categoryId), (0, drizzle_orm_1.eq)(schema_1.categories.userId, userId))) .limit(1); if (cat.length === 0) { throw new errorHandler_1.AppError('NOT_FOUND', 'Category not found', 404); } } const now = Date.now(); const updated = await db_1.db .update(schema_1.tasks) .set({ ...data, updatedAt: now }) .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.tasks.id, req.params.id), (0, drizzle_orm_1.eq)(schema_1.tasks.userId, userId))) .returning(); const taskSubtasks = await db_1.db .select() .from(schema_1.subtasks) .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.subtasks.taskId, req.params.id), (0, drizzle_orm_1.eq)(schema_1.subtasks.userId, userId))) .orderBy((0, drizzle_orm_1.asc)(schema_1.subtasks.order)); res.json({ ...updated[0], subtasks: taskSubtasks }); })); router.delete('/:id', (0, asyncHandler_1.asyncHandler)(async (req, res) => { const userId = req.user.userId; const existing = await db_1.db .select() .from(schema_1.tasks) .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.tasks.id, req.params.id), (0, drizzle_orm_1.eq)(schema_1.tasks.userId, userId))) .limit(1); if (existing.length === 0) { throw new errorHandler_1.AppError('NOT_FOUND', 'Task not found', 404); } await db_1.db .delete(schema_1.tasks) .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.tasks.id, req.params.id), (0, drizzle_orm_1.eq)(schema_1.tasks.userId, userId))); res.status(204).send(); })); router.post('/batch', (0, asyncHandler_1.asyncHandler)(async (req, res) => { const { operations } = zod_1.z.object({ operations: zod_1.z.array(zod_1.z.union([ zod_1.z.object({ type: zod_1.z.literal('create'), data: validation_1.taskCreateSchema }), zod_1.z.object({ type: zod_1.z.literal('update'), id: zod_1.z.string(), data: validation_1.taskUpdateSchema }), zod_1.z.object({ type: zod_1.z.literal('delete'), id: zod_1.z.string() }), ])), }).parse(req.body); const userId = req.user.userId; const results = []; for (const op of operations) { try { if (op.type === 'create') { const cat = await db_1.db .select() .from(schema_1.categories) .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.categories.id, op.data.categoryId), (0, drizzle_orm_1.eq)(schema_1.categories.userId, userId))) .limit(1); if (cat.length === 0) throw new errorHandler_1.AppError('NOT_FOUND', 'Category not found', 404); const taskId = `task_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`; const now = Date.now(); await db_1.db.insert(schema_1.tasks).values({ id: taskId, userId, ...op.data, completed: false, createdAt: now, updatedAt: now, }); results.push({ id: taskId, success: true }); } else if (op.type === 'update') { await db_1.db .update(schema_1.tasks) .set({ ...op.data, updatedAt: Date.now() }) .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.tasks.id, op.id), (0, drizzle_orm_1.eq)(schema_1.tasks.userId, userId))); results.push({ id: op.id, success: true }); } else if (op.type === 'delete') { await db_1.db .delete(schema_1.tasks) .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.tasks.id, op.id), (0, drizzle_orm_1.eq)(schema_1.tasks.userId, userId))); results.push({ id: op.id, success: true }); } } catch (error) { results.push({ id: 'id' in op ? op.id : 'unknown', success: false, error: error instanceof Error ? error.message : 'Unknown error' }); } } res.json({ results }); })); exports.default = router; //# sourceMappingURL=tasks.js.map