Add src/app/api/tasks/route.ts
This commit is contained in:
150
src/app/api/tasks/route.ts
Normal file
150
src/app/api/tasks/route.ts
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
import { getDb } from '@/server/db';
|
||||||
|
import { tasks, priorityEnum, statusEnum, recurrenceEnum, type NewTask, type Task } from '@/server/db/schema';
|
||||||
|
import { eq, and, or, gte, lte, asc, desc, like, ilike, sql } from 'drizzle-orm';
|
||||||
|
import { addDays, addWeeks, addMonths, addYears } from 'date-fns';
|
||||||
|
|
||||||
|
export async function GET(request: NextRequest) {
|
||||||
|
const db = getDb();
|
||||||
|
const { searchParams } = new URL(request.url);
|
||||||
|
|
||||||
|
const project = searchParams.get('project');
|
||||||
|
const search = searchParams.get('search');
|
||||||
|
const status = searchParams.get('status');
|
||||||
|
const priority = searchParams.get('priority');
|
||||||
|
const dueBefore = searchParams.get('due_before');
|
||||||
|
const dueAfter = searchParams.get('due_after');
|
||||||
|
const completed = searchParams.get('completed');
|
||||||
|
const sortBy = searchParams.get('sort_by') || 'due_date';
|
||||||
|
const sortOrder = searchParams.get('sort_order') || 'asc';
|
||||||
|
|
||||||
|
try {
|
||||||
|
let query = db.select().from(tasks);
|
||||||
|
|
||||||
|
// Build WHERE conditions
|
||||||
|
const conditions = [];
|
||||||
|
|
||||||
|
if (project) {
|
||||||
|
conditions.push(eq(tasks.projectId, project));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
conditions.push(eq(tasks.status, status as any));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priority) {
|
||||||
|
conditions.push(eq(tasks.priority, priority as any));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (completed !== null && completed !== undefined) {
|
||||||
|
conditions.push(eq(tasks.completed, completed === 'true'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dueBefore) {
|
||||||
|
conditions.push(lte(tasks.dueDate, new Date(dueBefore)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dueAfter) {
|
||||||
|
conditions.push(gte(tasks.dueDate, new Date(dueAfter)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
conditions.push(
|
||||||
|
or(
|
||||||
|
ilike(tasks.title, `%${search}%`),
|
||||||
|
ilike(tasks.description, `%${search}%`)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conditions.length > 0) {
|
||||||
|
query = query.where(and(...conditions));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort
|
||||||
|
let sortColumn: any;
|
||||||
|
switch (sortBy) {
|
||||||
|
case 'priority':
|
||||||
|
sortColumn = sql`CASE ${tasks.priority} WHEN 'urgent' THEN 1 WHEN 'high' THEN 2 WHEN 'medium' THEN 3 WHEN 'low' THEN 4 ELSE 5 END`;
|
||||||
|
break;
|
||||||
|
case 'due_date':
|
||||||
|
default:
|
||||||
|
sortColumn = tasks.dueDate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
query = query.orderBy(sortOrder === 'desc' ? desc(sortColumn) : asc(sortColumn));
|
||||||
|
|
||||||
|
const result = await query;
|
||||||
|
|
||||||
|
return NextResponse.json(result);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching tasks:', error);
|
||||||
|
return NextResponse.json({ error: 'Failed to fetch tasks' }, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST(request: NextRequest) {
|
||||||
|
const db = getDb();
|
||||||
|
let body;
|
||||||
|
|
||||||
|
try {
|
||||||
|
body = await request.json();
|
||||||
|
} catch {
|
||||||
|
return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
title,
|
||||||
|
description = '',
|
||||||
|
projectId,
|
||||||
|
priority = 'medium',
|
||||||
|
dueDate,
|
||||||
|
status = 'todo',
|
||||||
|
parentTaskId,
|
||||||
|
recurrenceRule = 'none',
|
||||||
|
recurrenceInterval = 1,
|
||||||
|
nextOccurrence,
|
||||||
|
} = body as NewTask & { dueDate?: string | null };
|
||||||
|
|
||||||
|
if (!title?.trim()) {
|
||||||
|
return NextResponse.json({ error: 'Title is required' }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
// Get max sort order for this project
|
||||||
|
const maxSort = await db
|
||||||
|
.select({ max: tasks.sortOrder })
|
||||||
|
.from(tasks)
|
||||||
|
.where(eq(tasks.projectId, projectId))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
const sortOrder = (maxSort[0]?.max ?? -1) + 1;
|
||||||
|
|
||||||
|
const [newTask] = await db
|
||||||
|
.insert(tasks)
|
||||||
|
.values({
|
||||||
|
title: title.trim(),
|
||||||
|
description: description?.trim() || '',
|
||||||
|
projectId,
|
||||||
|
priority,
|
||||||
|
dueDate: dueDate ? new Date(dueDate) : null,
|
||||||
|
status,
|
||||||
|
parentTaskId,
|
||||||
|
recurrenceRule,
|
||||||
|
recurrenceInterval,
|
||||||
|
nextOccurrence: nextOccurrence ? new Date(nextOccurrence) : null,
|
||||||
|
sortOrder,
|
||||||
|
createdAt: now,
|
||||||
|
updatedAt: now,
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
return NextResponse.json(newTask, { status: 201 });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error creating task:', error);
|
||||||
|
return NextResponse.json({ error: 'Failed to create task' }, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user