teqo-backup/src/components/campaign/reels/ReelTranscriptCard.tsx
Francisco Solla 448f3845a2 feat(C194): biblioteca de reels na vertical Comunicação (#1153)
Lista publicados-only com capas 9:16, detalhe com player 9:16 e painel de
downloads (vídeo sem áudio primário; com áudio, mp3 e .srt quando existem;
transcrição em texto) e kill switch reversível: despublicar tira da lista e
bloqueia a mídia (C193 decision B), republicar reativa e re-carimba publishedAt.

Fiação: gate communicationCatalog (rename do speechCatalog), 3º sub-item da
vertical no nav, rule da lista no chrome, paths e loader próprios; sem
migration, sem Consent, sem tocar Media.read ou a rota de mídia do C193.

Design tier: DEGRADED — crítica final do designer indisponível no tier
primário (limite de uso); requer sign-off humano antes de push/PR.
2026-09-18 21:54:37 -03:00

54 lines
1.7 KiB
TypeScript

'use client'
import { CheckIcon, CopyIcon } from 'lucide-react'
import { Button } from '@/components/ui/button'
import {
copyFeedbackLabelsFor,
copyFeedbackLiveMessagesFor,
useCopyFeedback,
} from '@/lib/copyFeedback'
/**
* C194 — the reel's script/narration text. DB content, not a served file: it
* stays readable while the reel is unpublished. Each stored line is a block,
* the same shape the production writes.
*/
export const ReelTranscriptCard = ({ transcript }: { transcript: string }) => {
const { feedback, copy } = useCopyFeedback()
const labels = copyFeedbackLabelsFor('text')
const liveMessages = copyFeedbackLiveMessagesFor('text')
const blocks = transcript
.split(/\n+/)
.map((block) => block.trim())
.filter(Boolean)
return (
<section id="transcricao" className="scroll-mt-20 rounded-xl border border-border p-4">
<div className="flex items-center justify-between gap-3">
<h2 className="text-sm font-semibold">Transcrição / roteiro</h2>
<Button
type="button"
variant="ghost"
className="min-h-10 px-2 text-xs text-muted-foreground"
onClick={() => void copy(transcript)}
>
{feedback === 'copied' ? (
<CheckIcon data-icon="inline-start" aria-hidden="true" />
) : (
<CopyIcon data-icon="inline-start" aria-hidden="true" />
)}
{labels[feedback]}
</Button>
</div>
<div className="mt-3 space-y-3 text-sm leading-6">
{blocks.map((block, index) => (
<p key={`${index}-${block}`}>{block}</p>
))}
</div>
<span aria-live="polite" className="sr-only">
{liveMessages[feedback]}
</span>
</section>
)
}