1
0
Fork 0

feat(voiceover): surface script context and spoiler warnings on script pages

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
Jason Fraley 2026-09-07 00:34:34 -04:00
parent 92117374b7
commit 11ed89fd10
2 changed files with 44 additions and 4 deletions

View file

@ -4,8 +4,9 @@ import { getPayload } from "payload";
import { headers as nextHeaders } from "next/headers"; import { headers as nextHeaders } from "next/headers";
import Link from "next/link"; import Link from "next/link";
import { notFound, redirect } from "next/navigation"; import { notFound, redirect } from "next/navigation";
import { ArrowLeftIcon, MicIcon } from "lucide-react"; import { ArrowLeftIcon, MicIcon, TriangleAlertIcon } from "lucide-react";
import { Card, CardContent } from "@/components/ui/card";
import { VoiceoverRecorder } from "@/components/frontend/voiceover/VoiceoverRecorder"; import { VoiceoverRecorder } from "@/components/frontend/voiceover/VoiceoverRecorder";
import { getScriptForUser } from "@/lib/voiceover"; import { getScriptForUser } from "@/lib/voiceover";
import { isPayloadUser } from "@/utils/access-control/isPayloadUser"; import { isPayloadUser } from "@/utils/access-control/isPayloadUser";
@ -50,12 +51,30 @@ export default async function VoiceoverScriptPage({
<MicIcon className="mt-0.5 size-5 shrink-0 text-muted-foreground" /> <MicIcon className="mt-0.5 size-5 shrink-0 text-muted-foreground" />
<div className="flex min-w-0 flex-col gap-1"> <div className="flex min-w-0 flex-col gap-1">
<h1 className="text-lg font-semibold">{script.title}</h1> <h1 className="text-lg font-semibold">{script.title}</h1>
{script.description?.trim() && (
<p className="text-sm text-foreground/80">{script.description}</p>
)}
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
Read the script at your pace, then submit one clear take for review. Read the script at your pace, then submit one clear take for review.
</p> </p>
</div> </div>
</div> </div>
{script.spoiler && (
<Card className="border-amber-500/40">
<CardContent className="flex items-start gap-3 p-4 text-sm">
<TriangleAlertIcon className="mt-0.5 size-4 shrink-0 text-amber-400" />
<div className="flex flex-col gap-1">
<p className="font-medium text-amber-300">Potential campaign spoilers ahead</p>
<p className="text-muted-foreground">
This script may include, infer, or otherwise expose campaign spoilers. Read on only
if you are comfortable with that.
</p>
</div>
</CardContent>
</Card>
)}
<VoiceoverRecorder script={script} /> <VoiceoverRecorder script={script} />
</div> </div>
); );

View file

@ -4,7 +4,14 @@ import { headers as nextHeaders } from "next/headers";
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
import { getPayload } from "payload"; import { getPayload } from "payload";
import config from "@payload-config"; import config from "@payload-config";
import { ArrowRightIcon, CheckCircle2Icon, Clock3Icon, MicIcon, SparklesIcon } from "lucide-react"; import {
ArrowRightIcon,
CheckCircle2Icon,
Clock3Icon,
MicIcon,
SparklesIcon,
TriangleAlertIcon,
} from "lucide-react";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@ -64,6 +71,15 @@ function SubmissionStatuses({ script }: { readonly script: VoiceoverScriptView }
); );
} }
function SpoilerBadge() {
return (
<Badge variant="outline" className="gap-1 border-amber-500/40 text-amber-300">
<TriangleAlertIcon className="size-3" />
Spoilers
</Badge>
);
}
function ScriptCard({ script }: { readonly script: VoiceoverScriptView }) { function ScriptCard({ script }: { readonly script: VoiceoverScriptView }) {
const isOpen = script.status === "open"; const isOpen = script.status === "open";
@ -84,10 +100,15 @@ function ScriptCard({ script }: { readonly script: VoiceoverScriptView }) {
script.title script.title
)} )}
</CardTitle> </CardTitle>
<CardDescription>Voiceover script #{script.id}</CardDescription> <CardDescription>
{script.description?.trim() || `Voiceover script #${script.id}`}
</CardDescription>
</div> </div>
<div className="flex shrink-0 flex-wrap items-center justify-end gap-2">
{script.spoiler && <SpoilerBadge />}
<ScriptAvailability status={script.status} /> <ScriptAvailability status={script.status} />
</div> </div>
</div>
<div className="flex flex-wrap items-center gap-2"> <div className="flex flex-wrap items-center gap-2">
<Badge variant="outline" className="gap-1 border-amber-500/40 text-amber-300"> <Badge variant="outline" className="gap-1 border-amber-500/40 text-amber-300">