versioning added
Deploy / deploy (push) Successful in 3m12s

This commit is contained in:
twotalesanimation
2026-06-25 22:19:22 +02:00
parent 935a293777
commit 75d5149bea
8 changed files with 172 additions and 3 deletions
@@ -28,12 +28,15 @@ import {
ExternalLink,
XCircle,
FileVideo,
Pencil,
Check,
} from "lucide-react";
import { Input } from "@/components/ui/input";
import type { ShotWithDetails } from "@/types";
import { ShotSettingsTab } from "@/components/shots/ShotSettingsTab";
import { FootageViewer } from "@/components/shots/FootageViewer";
import { HighResUploadDialog } from "@/components/shots/HighResUploadDialog";
import { duplicateShot, internallyApproveShot, shareWithClient, unshareFromClient, unapproveShot } from "@/actions/shots";
import { duplicateShot, internallyApproveShot, shareWithClient, unshareFromClient, unapproveShot, updateShotVersion } from "@/actions/shots";
const STATUS_CONFIG: Record<
string,
@@ -87,6 +90,9 @@ export default function ShotDetailPage() {
const [isActioning, setIsActioning] = useState(false);
const [highResDialogOpen, setHighResDialogOpen] = useState(false);
const [activeTab, setActiveTab] = useState<"tasks" | "reviews" | "footage" | "settings">("tasks");
const [editingVersion, setEditingVersion] = useState(false);
const [versionInput, setVersionInput] = useState("");
const [savingVersion, setSavingVersion] = useState(false);
const fetchShot = async () => {
try {
@@ -171,6 +177,26 @@ export default function ShotDetailPage() {
}
};
const handleSaveVersion = async () => {
if (!shot) return;
const trimmed = versionInput.trim().toLowerCase();
if (!/^v\d{3}$/.test(trimmed)) {
toast({ title: "Invalid format", description: "Version must be v### (e.g. v001)", variant: "destructive" });
return;
}
setSavingVersion(true);
try {
await updateShotVersion(shot.id, trimmed);
toast({ title: `Version set to ${trimmed}` });
setEditingVersion(false);
fetchShot();
} catch (e) {
toast({ title: "Failed", description: e instanceof Error ? e.message : undefined, variant: "destructive" });
} finally {
setSavingVersion(false);
}
};
const handleDuplicate = async () => {
if (!shot) return;
setIsDuplicating(true);
@@ -237,6 +263,55 @@ export default function ShotDetailPage() {
<div className="flex items-center gap-3">
<h1 className="text-2xl font-bold font-mono">{shot.shotCode}</h1>
{/* Shot version badge — click to edit for managers */}
{canManage ? (
editingVersion ? (
<div className="flex items-center gap-1.5">
<Input
autoFocus
value={versionInput}
onChange={(e) => setVersionInput(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter") handleSaveVersion();
if (e.key === "Escape") setEditingVersion(false);
}}
className="h-7 w-20 font-mono text-xs px-2"
placeholder="v001"
maxLength={4}
/>
<button
onClick={handleSaveVersion}
disabled={savingVersion}
className="text-emerald-400 hover:text-emerald-300 disabled:opacity-50"
title="Save version"
>
<Check className="h-4 w-4" />
</button>
<button
onClick={() => setEditingVersion(false)}
className="text-zinc-500 hover:text-zinc-300"
title="Cancel"
>
<XCircle className="h-4 w-4" />
</button>
</div>
) : (
<button
onClick={() => { setVersionInput(shot.shotVersion ?? "v001"); setEditingVersion(true); }}
className="group flex items-center gap-1 font-mono text-sm px-2 py-0.5 rounded border border-zinc-700 bg-zinc-800/60 text-zinc-300 hover:border-amber-500/50 hover:text-amber-400 transition-colors"
title="Click to edit version"
>
{shot.shotVersion ?? "v001"}
<Pencil className="h-3 w-3 opacity-0 group-hover:opacity-100 transition-opacity" />
</button>
)
) : (
<span className="font-mono text-sm px-2 py-0.5 rounded border border-zinc-700 bg-zinc-800/60 text-zinc-300">
{shot.shotVersion ?? "v001"}
</span>
)}
{shot.sequence && (
<span className="text-sm text-muted-foreground">Seq: {shot.sequence}</span>
)}