feat: add task comments feature with CRUD operations

- Introduced a new `task_comments` table in the database schema.
- Implemented task comments API endpoints for listing, creating, and deleting comments.
- Enhanced the task detail dialog to display comments and allow users to add new comments.
- Updated task row component to handle click events for viewing task details.
- Added a theme provider to manage light/dark mode across the application.
- Refactored Milkdown editor to use Crepe for improved markdown editing experience.
- Updated global styles to accommodate new editor and theme changes.
- Enhanced task filtering and sorting functionality in the tasks page.
This commit is contained in:
Roberto Musso
2026-02-23 12:54:14 +01:00
parent 98acf6220e
commit c1aa6829c9
24 changed files with 996 additions and 234 deletions

View File

@@ -41,7 +41,11 @@ export function parseAssignees(raw: string | null): string[] {
function formatDueDate(timestamp: number): string {
const d = new Date(timestamp);
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
return `Due ${months[d.getMonth()]} ${d.getDate()}`;
const date = `Due ${months[d.getMonth()]} ${d.getDate()}`;
if (d.getHours() === 0 && d.getMinutes() === 0) return date;
const h = String(d.getHours()).padStart(2, '0');
const m = String(d.getMinutes()).padStart(2, '0');
return `${date}, ${h}:${m}`;
}
export function TaskRow({
@@ -49,12 +53,14 @@ export function TaskRow({
onToggle,
onEdit,
onDelete,
onClick,
hideBreadcrumb,
}: {
task: TaskItem;
onToggle: (id: string, status: string | null) => void;
onEdit?: (task: TaskItem) => void;
onDelete?: (id: string) => void;
onClick?: (task: TaskItem) => void;
hideBreadcrumb?: boolean;
}) {
const isDone = task.status === 'done';
@@ -80,15 +86,17 @@ export function TaskRow({
<ContextMenu>
<ContextMenuTrigger asChild>
<div
className={`flex flex-col gap-1.5 px-4 py-3 rounded-md border cursor-default select-none ${
isDone ? 'bg-green-50 border-green-200' : 'bg-white border-border'
}`}
className={`flex flex-col gap-1.5 px-4 py-3 rounded-md border select-none transition-colors ${
isDone ? 'bg-green-50 dark:bg-green-950/30 border-green-200 dark:border-green-900' : 'bg-card border-border'
} ${onClick ? 'cursor-pointer hover:bg-accent/50' : 'cursor-default'}`}
onClick={() => onClick?.(task)}
>
{/* Row 1: checkbox + title + description */}
<div className="flex items-start gap-3">
<Checkbox
checked={checkboxState}
onCheckedChange={() => onToggle(task.id, task.status)}
onClick={(e) => e.stopPropagation()}
className="mt-0.5 shrink-0"
/>
<div className="flex-1 min-w-0">