Files
twotalesanimation 0fbe856dce Initial commit
2026-05-19 22:20:29 +02:00

129 lines
4.0 KiB
TypeScript

"use client";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { TaskCard } from "./TaskCard";
import { NewTaskDialog } from "./NewTaskDialog";
import { Plus, ListTodo } from "lucide-react";
import { TaskType } from "@prisma/client";
const SHOT_QUICK_TYPES: TaskType[] = ["TRACK", "ROTO", "KEY", "COMP", "FX"];
const ASSET_QUICK_TYPES: TaskType[] = ["MODEL", "TEXTURE", "RIG", "LOOKDEV"];
interface Artist {
id: string;
name: string | null;
email: string;
}
interface Task {
id: string;
title: string;
type: TaskType;
status: any;
priority: string;
dueDate: Date | null;
assignedArtist?: { id: string; name: string | null; email: string; image: string | null } | null;
_count?: { versions: number };
versions?: { id: string; versionNumber: number; approvalStatus: string; createdAt: Date }[];
}
interface TaskListProps {
tasks: Task[];
projectId: string;
shotId?: string;
assetId?: string;
artists: Artist[];
canManage?: boolean;
onTaskCreated?: () => void;
}
export function TaskList({ tasks, projectId, shotId, assetId, artists, canManage = false, onTaskCreated }: TaskListProps) {
const [showNew, setShowNew] = useState(false);
const [prefillType, setPrefillType] = useState<TaskType | undefined>();
const quickTypes = shotId ? SHOT_QUICK_TYPES : assetId ? ASSET_QUICK_TYPES : [];
const openQuick = (type: TaskType) => {
setPrefillType(type);
setShowNew(true);
};
return (
<div className="space-y-2">
<div className="flex items-center justify-between">
<h3 className="text-sm font-medium text-zinc-300">Tasks</h3>
{canManage && (
<Button size="sm" variant="ghost" className="h-7 gap-1.5 text-xs" onClick={() => { setPrefillType(undefined); setShowNew(true); }}>
<Plus className="h-3.5 w-3.5" />
Add Task
</Button>
)}
</div>
{/* Quick-add templates */}
{canManage && quickTypes.length > 0 && (
<div className="flex flex-wrap gap-1.5">
{quickTypes.map((t) => {
const label = {
TRACK: "Track",
ROTO: "Roto",
KEY: "Key",
COMP: "Comp",
FX: "FX",
ANIMATION: "Animation",
GENERAL: "General",
LIGHTING: "Lighting",
RENDER: "Render",
MODEL: "Model",
TEXTURE: "Texture",
RIG: "Rig",
LOOKDEV: "Lookdev"
}[t] ?? t;
const alreadyExists = tasks.some((task) => task.type === t);
return (
<button
key={t}
type="button"
disabled={alreadyExists}
onClick={() => openQuick(t)}
className={`px-2 py-0.5 rounded text-[11px] font-medium border transition-colors ${
alreadyExists
? "opacity-30 cursor-not-allowed bg-zinc-900 text-zinc-500 border-zinc-800"
: "bg-zinc-800 text-zinc-400 border-zinc-700 hover:border-amber-500/50 hover:text-amber-400"
}`}
>
+ {label}
</button>
);
})}
</div>
)}
{tasks.length === 0 ? (
<div className="text-center py-6 border border-dashed border-border rounded-lg">
<ListTodo className="h-6 w-6 mx-auto mb-2 text-zinc-600" />
<p className="text-xs text-muted-foreground">No tasks yet</p>
</div>
) : (
<div className="space-y-1">
{tasks.map((task) => (
<TaskCard key={task.id} task={task} projectId={projectId} canManage={canManage} />
))}
</div>
)}
<NewTaskDialog
projectId={projectId}
shotId={shotId}
assetId={assetId}
artists={artists}
open={showNew}
prefillType={prefillType}
onClose={() => setShowNew(false)}
onSuccess={onTaskCreated}
/>
</div>
);
}