"use client"; import { useRouter } from "next/navigation"; import { useActionState, useEffect, useState } from "react"; import { useTranslations } from "next-intl"; import { CheckCircle2, Compass, Loader2, MinusCircle, Pencil, Sparkles, XCircle, type LucideIcon } from "lucide-react"; import { LOTSE_IDLE, type LotseActionState } from "@/lib/lotse/action-state"; import { cn } from "@/lib/utils"; import { adoptSummaryAction, saveTranscriptAction, summarizeVoiceNoteAction } from "@/server/actions/lotse/assist"; type Status = "pending" | "done" | "failed" | "disabled"; const STATUS: Record = { pending: { icon: Loader2, cls: "bg-[color-mix(in_oklch,var(--info)_12%,transparent)] text-[var(--info)]" }, done: { icon: CheckCircle2, cls: "bg-[color-mix(in_oklch,var(--ok)_12%,transparent)] text-[var(--ok)]" }, failed: { icon: XCircle, cls: "bg-[color-mix(in_oklch,var(--risk)_12%,transparent)] text-[var(--risk)]" }, disabled: { icon: MinusCircle, cls: "bg-muted text-muted-foreground" }, }; const btnOutline = "inline-flex min-h-12 items-center justify-center gap-2 rounded-xl border border-border bg-card px-4 font-heading text-[15px] font-semibold text-primary hover:bg-muted disabled:opacity-50 focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-ring/50"; function ErrorLine({ state }: { state: LotseActionState }) { const t = useTranslations("lotse"); if (state.status !== "error") return null; return (

{t(`errors.${state.code}`)}

); } export function VoiceNoteLotse({ voiceNoteId, status, transcript, canEdit, canSummarize, latestSummary, }: { voiceNoteId: string; status: Status; transcript: string | null; canEdit: boolean; canSummarize: boolean; latestSummary: { generationId: string; summary: string } | null; }) { const t = useTranslations("lotse"); const router = useRouter(); // timestamps instead of effects: a newer successful action result closes the editor / re-shows the summary const [editingAt, setEditingAt] = useState(null); const [hiddenAt, setHiddenAt] = useState(null); const [saveState, save, saving] = useActionState(saveTranscriptAction, LOTSE_IDLE); const [sumState, summarize, summarizing] = useActionState(summarizeVoiceNoteAction, LOTSE_IDLE); const [adoptState, adopt, adopting] = useActionState(adoptSummaryAction, LOTSE_IDLE); const S = STATUS[status]; const editing = editingAt !== null && !(saveState.status === "ok" && saveState.at > editingAt); const hidden = hiddenAt !== null && !(sumState.status === "ok" && sumState.at > hiddenAt); useEffect(() => { if (saveState.status === "ok") router.refresh(); }, [saveState, router]); useEffect(() => { if (adoptState.status === "ok") router.refresh(); }, [adoptState, router]); const summary = sumState.status === "ok" && sumState.summary ? { generationId: sumState.generationId ?? "", summary: sumState.summary } : latestSummary; return (
{t(`voice.status.${status}`)} {editing ? (