"use client"; import { Calendar, User } from "lucide-react"; import { BaseBoxShapeUtil, HTMLContainer, T, type TLBaseShape, toDomPrecision, } from "tldraw"; import { cn } from "@/lib/utils"; export type TaskCardShape = TLBaseShape< "task-card", { w: number; h: number; objectId: string; title: string; status: "open" | "in_progress" | "done" | "closed"; priority: string; assignees: string; dueDate: string; } >; declare module "@tldraw/tlschema" { interface TLGlobalShapePropsMap { "task-card": TaskCardShape["props"]; } } const STATUS_DOT: Record = { open: "bg-zinc-400 dark:bg-zinc-500", in_progress: "bg-blue-500 dark:bg-blue-400", done: "bg-emerald-500 dark:bg-emerald-400", closed: "bg-slate-500 dark:bg-slate-400", }; function priorityClass(priority: string): string { const p = priority.toLowerCase(); if (p.includes("high")) return "bg-red-500/15 text-red-700 ring-1 ring-red-500/30 dark:text-red-300"; if (p.includes("low")) return "bg-emerald-500/15 text-emerald-700 ring-1 ring-emerald-500/30 dark:text-emerald-300"; return "bg-amber-500/15 text-amber-800 ring-1 ring-amber-500/30 dark:text-amber-200"; } function initialsFromName(name: string): string { const parts = name.trim().split(/\s+/).filter(Boolean); if (parts.length === 0) return "?"; if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase(); return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase(); } function TaskCardBody({ shape }: { shape: TaskCardShape }) { const { title, status, priority, assignees, dueDate } = shape.props; const names = assignees .split(",") .map((s) => s.trim()) .filter(Boolean) .slice(0, 2); return (
{title || "Untitled task"}
{priority || "—"} {names.length > 0 ? (
{names.map((name, i) => ( {initialsFromName(name)} ))}
) : ( Unassigned )}
{dueDate ? (
{dueDate}
) : (
No due date
)}
Task
); } export class TaskCardShapeUtil extends BaseBoxShapeUtil { static override type = "task-card"; static override props = { w: T.number, h: T.number, objectId: T.string, title: T.string, status: T.literalEnum("open", "in_progress", "done", "closed"), priority: T.string, assignees: T.string, dueDate: T.string, }; override getDefaultProps(): TaskCardShape["props"] { return { w: 240, h: 160, objectId: "", title: "Task", status: "open", priority: "Medium", assignees: "", dueDate: "", }; } override getAriaDescriptor(shape: TaskCardShape) { return shape.props.title; } override component(shape: TaskCardShape) { return ; } override indicator(shape: TaskCardShape) { return ( ); } override useLegacyIndicator() { return false; } override getIndicatorPath(shape: TaskCardShape) { const p = new Path2D(); p.roundRect(0, 0, shape.props.w, shape.props.h, 10); return p; } }