Files
Codewalkers/apps/web/src/components/TaskRow.tsx
Lukas May 34578d39c6 refactor: Restructure monorepo to apps/server/ and apps/web/ layout
Move src/ → apps/server/ and packages/web/ → apps/web/ to adopt
standard monorepo conventions (apps/ for runnable apps, packages/
for reusable libraries). Update all config files, shared package
imports, test fixtures, and documentation to reflect new paths.

Key fixes:
- Update workspace config to ["apps/*", "packages/*"]
- Update tsconfig.json rootDir/include for apps/server/
- Add apps/web/** to vitest exclude list
- Update drizzle.config.ts schema path
- Fix ensure-schema.ts migration path detection (3 levels up in dev,
  2 levels up in dist)
- Fix tests/integration/cli-server.test.ts import paths
- Update packages/shared imports to apps/server/ paths
- Update all docs/ files with new paths
2026-03-03 11:22:53 +01:00

104 lines
2.9 KiB
TypeScript

import { Link } from "@tanstack/react-router";
import { X } from "lucide-react";
import { StatusBadge } from "@/components/StatusBadge";
import { DependencyIndicator } from "@/components/DependencyIndicator";
import { cn } from "@/lib/utils";
/** Task shape as returned by tRPC (Date fields serialized to string over JSON) */
export interface SerializedTask {
id: string;
phaseId: string | null;
initiativeId: string | null;
parentTaskId: string | null;
name: string;
description: string | null;
type: "auto" | "checkpoint:human-verify" | "checkpoint:decision" | "checkpoint:human-action";
category: string;
priority: "low" | "medium" | "high";
status: "pending_approval" | "pending" | "in_progress" | "completed" | "blocked";
requiresApproval: boolean | null;
order: number;
createdAt: string;
updatedAt: string;
}
interface TaskRowProps {
task: SerializedTask;
agentName: string | null;
blockedBy: Array<{ name: string; status: string }>;
isLast: boolean;
onClick: () => void;
onDelete?: () => void;
}
export function TaskRow({
task,
agentName,
blockedBy,
isLast,
onClick,
onDelete,
}: TaskRowProps) {
const connector = isLast ? "└──" : "├──";
return (
<div>
{/* Task row */}
<div
className={cn(
"group flex cursor-pointer items-center gap-2 rounded px-2 py-1 hover:bg-accent",
!isLast && "border-l-2 border-muted-foreground/20",
)}
onClick={onClick}
>
{/* Tree connector */}
<span className="shrink-0 font-mono text-sm text-muted-foreground">
{connector}
</span>
{/* Task name */}
<span className="min-w-0 flex-1 truncate text-sm">{task.name}</span>
{/* Agent assignment */}
{agentName && (
<Link
to="/inbox"
className="shrink-0 text-xs text-primary hover:underline"
onClick={(e) => e.stopPropagation()}
>
[{agentName}]
</Link>
)}
{/* Status badge */}
<StatusBadge status={task.status} className="shrink-0" />
{/* Delete button */}
{onDelete && (
<button
className="shrink-0 rounded p-0.5 text-muted-foreground opacity-0 transition-opacity hover:text-destructive group-hover:opacity-100"
title="Delete task (Shift+click to skip confirmation)"
onClick={(e) => {
e.stopPropagation();
if (e.shiftKey || window.confirm(`Delete "${task.name}"?`)) {
onDelete();
}
}}
>
<X className="h-3.5 w-3.5" />
</button>
)}
</div>
{/* Dependency indicator below the row */}
{blockedBy.length > 0 && (
<DependencyIndicator
blockedBy={blockedBy}
type="task"
className="ml-6"
/>
)}
</div>
);
}