edit task title added
Deploy / deploy (push) Successful in 2m33s

This commit is contained in:
twotalesanimation
2026-06-12 12:57:29 +02:00
parent 874a1a98f2
commit 450f969007
@@ -27,6 +27,7 @@ import { submitApproval, unapproveVersion } from "@/actions/approvals";
import { useToast } from "@/components/ui/use-toast";
import { TaskStatus, TaskType } from "@prisma/client";
import { formatDistanceToNow, format } from "date-fns";
import { Input } from "@/components/ui/input";
import {
ArrowLeft,
CalendarDays,
@@ -46,6 +47,7 @@ import {
Trash2,
XCircle,
MoreHorizontal,
Pencil,
} from "lucide-react";
import { TASK_STATUS_CONFIG, TASK_TYPE_LABELS } from "@/components/tasks/TaskCard";
import { VersionUpload } from "@/components/versions/VersionUpload";
@@ -133,6 +135,9 @@ export function TaskDetailClient({
const [estimatedHoursValue, setEstimatedHoursValue] = useState(
task.estimatedHours != null ? String(task.estimatedHours) : ""
);
const [editingTitle, setEditingTitle] = useState(false);
const [titleValue, setTitleValue] = useState(task.title);
const titleInputRef = useRef<HTMLInputElement>(null);
const statusCfg = TASK_STATUS_CONFIG[task.status];
const StatusIcon = statusCfg.icon;
@@ -178,6 +183,33 @@ export function TaskDetailClient({
}
};
const handleTitleSave = async () => {
const trimmed = titleValue.trim();
if (!trimmed || trimmed === task.title) {
setTitleValue(task.title);
setEditingTitle(false);
return;
}
try {
await updateTask(task.id, { title: trimmed });
router.refresh();
} catch {
toast({ title: "Failed to update title", variant: "destructive" });
setTitleValue(task.title);
} finally {
setEditingTitle(false);
}
};
const handleTitleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") {
e.currentTarget.blur();
} else if (e.key === "Escape") {
setTitleValue(task.title);
setEditingTitle(false);
}
};
const handleAssigneeChange = async (artistId: string) => {
try {
await updateTask(task.id, { assignedArtistId: artistId === "__none__" ? null : artistId });
@@ -241,7 +273,7 @@ export function TaskDetailClient({
</>
)}
<span>/</span>
<span className="text-zinc-300">{task.title}</span>
<span className="text-zinc-300">{titleValue}</span>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
@@ -251,7 +283,33 @@ export function TaskDetailClient({
<div className="space-y-3">
<div className="flex items-start justify-between gap-3">
<div className="space-y-1">
<h1 className="text-2xl font-bold text-white">{task.title}</h1>
{editingTitle && canManage ? (
<Input
ref={titleInputRef}
value={titleValue}
onChange={(e) => setTitleValue(e.target.value)}
onBlur={handleTitleSave}
onKeyDown={handleTitleKeyDown}
className="text-2xl font-bold h-auto py-0.5 px-1 bg-transparent border-zinc-700 focus-visible:ring-amber-500/50"
autoFocus
/>
) : (
<div className="flex items-center gap-2 group">
<h1 className="text-2xl font-bold text-white">{titleValue}</h1>
{canManage && (
<button
onClick={() => {
setEditingTitle(true);
setTimeout(() => titleInputRef.current?.select(), 0);
}}
className="opacity-0 group-hover:opacity-100 transition-opacity text-zinc-500 hover:text-zinc-300"
title="Edit title"
>
<Pencil className="h-3.5 w-3.5" />
</button>
)}
</div>
)}
<div className="flex items-center gap-2 text-sm text-zinc-500">
<span className="font-mono">{TASK_TYPE_LABELS[task.type]}</span>
{parentLink && (