@burdenoff/microfe-hookmovies 2026.705.2 → 2026.706.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/hookmovies/HookMoviesRoutes.js +26 -6
- package/dist/hookmovies/HookMoviesRoutes.js.map +1 -1
- package/dist/hookmovies/components/index.js +7 -0
- package/dist/hookmovies/hooks/index.js +1 -0
- package/dist/hookmovies/hooks/useFiles.js +97 -0
- package/dist/hookmovies/hooks/useFiles.js.map +1 -0
- package/dist/hookmovies/hooks/useHookMoviesApi.js +204 -52
- package/dist/hookmovies/hooks/useHookMoviesApi.js.map +1 -1
- package/dist/hookmovies/pages/HomePage.js +2 -2
- package/dist/hookmovies/pages/HomePage.js.map +1 -1
- package/dist/hookmovies/pages/auditions/AuditionDetailPage.js +100 -45
- package/dist/hookmovies/pages/auditions/AuditionDetailPage.js.map +1 -1
- package/dist/hookmovies/pages/catalog/TitleDetailPage.js +1340 -266
- package/dist/hookmovies/pages/catalog/TitleDetailPage.js.map +1 -1
- package/dist/hookmovies/pages/catalog/TitlesPage.js +95 -74
- package/dist/hookmovies/pages/catalog/TitlesPage.js.map +1 -1
- package/dist/hookmovies/pages/consumer/DiscoveryPage.js +422 -0
- package/dist/hookmovies/pages/consumer/DiscoveryPage.js.map +1 -0
- package/dist/hookmovies/pages/consumer/HooksPage.js +252 -0
- package/dist/hookmovies/pages/consumer/HooksPage.js.map +1 -0
- package/dist/hookmovies/pages/consumer/PlayerPage.js +542 -0
- package/dist/hookmovies/pages/consumer/PlayerPage.js.map +1 -0
- package/dist/hookmovies/pages/consumer/TitleViewPage.js +424 -0
- package/dist/hookmovies/pages/consumer/TitleViewPage.js.map +1 -0
- package/dist/hookmovies/pages/distribution/RightsWindowsPage.js +1 -1
- package/dist/hookmovies/pages/distribution/RightsWindowsPage.js.map +1 -1
- package/dist/hookmovies/pages/library/EntitlementsPage.js +273 -75
- package/dist/hookmovies/pages/library/EntitlementsPage.js.map +1 -1
- package/dist/hookmovies/pages/library/ListsPage.js +186 -106
- package/dist/hookmovies/pages/library/ListsPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TitleDetailPage.js","names":[],"sources":["../../../../src/hookmovies/pages/catalog/TitleDetailPage.tsx"],"sourcesContent":["/**\n * Title detail (Catalog)\n * @module hookmovies/pages/catalog\n *\n * Reads a single title via `hokTitle` (with credits, seasons, episodes &\n * studio), and drives the publication lifecycle via `hokTransitionTitle` plus\n * archive via `hokArchiveTitle`. The reviews for the title come from\n * `hokListReviews(titleId)`.\n */\n\nimport { type FC, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n Calendar,\n Clock,\n Globe,\n Languages,\n ShieldCheck,\n Sparkles,\n Users,\n Layers,\n Archive,\n Star,\n} from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { LoadingState, ErrorState } from '../../components/StateViews';\nimport { RatingDisplay } from '../../components/RatingDisplay';\nimport { useHookMovies } from '../../context/HookMoviesContext';\nimport {\n useTitle,\n useTransitionTitle,\n useArchiveTitle,\n useReviews,\n type HokTitleStatus,\n} from '../../hooks/useHookMoviesApi';\nimport { formatDate, formatEnumLabel } from '../../utils/formatters';\n\nconst NEXT_STATUSES: Record<HokTitleStatus, HokTitleStatus[]> = {\n DRAFT: ['IN_REVIEW', 'SCHEDULED', 'PUBLISHED'],\n IN_REVIEW: ['DRAFT', 'SCHEDULED', 'PUBLISHED'],\n SCHEDULED: ['PUBLISHED', 'DRAFT'],\n PUBLISHED: ['UNPUBLISHED'],\n UNPUBLISHED: ['PUBLISHED', 'ARCHIVED'],\n ARCHIVED: [],\n};\n\nexport const TitleDetailPage: FC = () => {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useHookMovies();\n const { data: title, isLoading, isError, refetch } = useTitle(id);\n const transition = useTransitionTitle();\n const archive = useArchiveTitle();\n const reviews = useReviews({ titleId: id }, { limit: 10, offset: 0 });\n const [actionError, setActionError] = useState<string | null>(null);\n\n if (isLoading) {\n return (\n <PageLayout title=\"Title\">\n <LoadingState label=\"Loading title…\" />\n </PageLayout>\n );\n }\n\n if (isError || !title) {\n return (\n <PageLayout title=\"Title\">\n <ErrorState\n title=\"Title not found\"\n message=\"This title may have been archived or you may not have access.\"\n onRetry={() => void refetch()}\n />\n </PageLayout>\n );\n }\n\n const handleTransition = async (status: HokTitleStatus) => {\n setActionError(null);\n try {\n await transition.mutateAsync({ id: title.id, status });\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Transition failed');\n }\n };\n\n const handleArchive = async () => {\n setActionError(null);\n try {\n await archive.mutateAsync(title.id);\n navigateTo('catalog/titles');\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Archive failed');\n }\n };\n\n const reviewItems = reviews.data?.items ?? [];\n const avgScore =\n reviewItems.length > 0\n ? reviewItems.reduce((sum, r) => sum + r.score, 0) / reviewItems.length\n : null;\n\n const facts: Array<{ icon: typeof Calendar; label: string; value: string }> = [];\n if (title.releaseDate)\n facts.push({ icon: Calendar, label: 'Release', value: formatDate(title.releaseDate) });\n else if (title.releaseYear)\n facts.push({ icon: Calendar, label: 'Year', value: String(title.releaseYear) });\n if (title.runtimeMinutes)\n facts.push({ icon: Clock, label: 'Runtime', value: `${title.runtimeMinutes} min` });\n if (title.originalLanguage)\n facts.push({ icon: Languages, label: 'Language', value: title.originalLanguage });\n if (title.originCountry)\n facts.push({ icon: Globe, label: 'Country', value: title.originCountry });\n if (title.certification)\n facts.push({ icon: ShieldCheck, label: 'Certification', value: title.certification });\n\n return (\n <PageLayout\n title={\n <span className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigateTo('catalog/titles')}\n className=\"rounded-md p-1 text-text-secondary hover:text-text-primary\"\n aria-label=\"Back to catalog\"\n >\n <ArrowLeft className=\"h-5 w-5\" />\n </button>\n {title.title}\n </span>\n }\n description={`${formatEnumLabel(title.type)}${title.studio ? ` · ${title.studio.name}` : ''}`}\n actions={<StatusBadge status={title.status} />}\n >\n {actionError && (\n <div className=\"mb-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text\">\n {actionError}\n </div>\n )}\n\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Main column */}\n <div className=\"space-y-6 lg:col-span-2\">\n {title.synopsis && (\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-5\">\n <h2 className=\"mb-2 text-sm font-semibold text-text-primary\">Synopsis</h2>\n <p className=\"text-sm leading-relaxed text-text-secondary\">{title.synopsis}</p>\n </GlassCard>\n )}\n\n {/* Credits */}\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-5\">\n <h2 className=\"mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <Users className=\"h-4 w-4\" />\n Cast & Crew ({title.credits.length})\n </h2>\n {title.credits.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">No credits recorded yet.</p>\n ) : (\n <ul className=\"divide-y divide-border-seam\">\n {[...title.credits]\n .sort((a, b) => a.billingOrder - b.billingOrder)\n .map((credit) => (\n <li key={credit.id} className=\"flex items-center justify-between py-2\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {credit.person?.name ?? 'Unknown person'}\n </p>\n {credit.characterName && (\n <p className=\"text-xs text-text-secondary\">as {credit.characterName}</p>\n )}\n </div>\n <span className=\"rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs text-text-secondary\">\n {formatEnumLabel(credit.role)}\n </span>\n </li>\n ))}\n </ul>\n )}\n </GlassCard>\n\n {/* Seasons & episodes */}\n {(title.seasons.length > 0 || title.episodes.length > 0) && (\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-5\">\n <h2 className=\"mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <Layers className=\"h-4 w-4\" />\n Episodes ({title.episodes.length})\n </h2>\n <ul className=\"divide-y divide-border-seam\">\n {[...title.episodes]\n .sort((a, b) => a.number - b.number)\n .map((ep) => (\n <li key={ep.id} className=\"flex items-center justify-between py-2\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {ep.number}. {ep.name}\n </p>\n {ep.synopsis && (\n <p className=\"line-clamp-1 text-xs text-text-secondary\">{ep.synopsis}</p>\n )}\n </div>\n {ep.runtimeMinutes && (\n <span className=\"text-xs text-text-secondary\">{ep.runtimeMinutes} min</span>\n )}\n </li>\n ))}\n </ul>\n </GlassCard>\n )}\n\n {/* Reviews */}\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-5\">\n <div className=\"mb-3 flex items-center justify-between\">\n <h2 className=\"flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <Star className=\"h-4 w-4\" />\n Reviews ({reviewItems.length})\n </h2>\n {avgScore !== null && (\n <RatingDisplay value={avgScore / 2} count={reviewItems.length} />\n )}\n </div>\n {reviewItems.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">No reviews yet.</p>\n ) : (\n <ul className=\"space-y-3\">\n {reviewItems.map((review) => (\n <li\n key={review.id}\n className=\"rounded-lg border border-border-seam bg-bg-surface p-3\"\n >\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-semibold text-text-primary\">\n {review.score}/10\n </span>\n <div className=\"flex items-center gap-2\">\n {review.verifiedWatch && (\n <span className=\"rounded-full bg-status-success-bg-subtle px-2 py-0.5 text-[10px] text-status-success-text\">\n Verified\n </span>\n )}\n <StatusBadge status={review.status} />\n </div>\n </div>\n {review.body && (\n <p className=\"mt-1 text-sm text-text-secondary\">{review.body}</p>\n )}\n </li>\n ))}\n </ul>\n )}\n </GlassCard>\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-6\">\n {/* Lifecycle actions */}\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-5\">\n <h2 className=\"mb-3 text-sm font-semibold text-text-primary\">Publication</h2>\n <div className=\"flex flex-wrap gap-2\">\n {NEXT_STATUSES[title.status].map((status) => (\n <button\n key={status}\n type=\"button\"\n disabled={transition.isPending}\n onClick={() => void handleTransition(status)}\n className=\"rounded-lg bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50 transition-opacity\"\n >\n {formatEnumLabel(status)}\n </button>\n ))}\n {NEXT_STATUSES[title.status].length === 0 && (\n <p className=\"text-xs text-text-secondary\">No further transitions available.</p>\n )}\n </div>\n {title.status !== 'ARCHIVED' && (\n <button\n type=\"button\"\n disabled={archive.isPending}\n onClick={() => void handleArchive()}\n className=\"mt-3 inline-flex items-center gap-2 rounded-lg border border-status-error-border px-3 py-1.5 text-xs font-medium text-status-error-text hover:bg-status-error-bg-subtle disabled:opacity-50 transition-colors\"\n >\n <Archive className=\"h-3.5 w-3.5\" />\n Archive Title\n </button>\n )}\n </GlassCard>\n\n {/* Facts */}\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-5\">\n <h2 className=\"mb-3 text-sm font-semibold text-text-primary\">Details</h2>\n <dl className=\"space-y-2\">\n {facts.map((fact) => {\n const Icon = fact.icon;\n return (\n <div key={fact.label} className=\"flex items-center justify-between text-sm\">\n <dt className=\"flex items-center gap-2 text-text-secondary\">\n <Icon className=\"h-3.5 w-3.5\" />\n {fact.label}\n </dt>\n <dd className=\"font-medium text-text-primary\">{fact.value}</dd>\n </div>\n );\n })}\n {title.aiDisclosure && (\n <div className=\"flex items-center gap-2 pt-1 text-sm text-text-secondary\">\n <Sparkles className=\"h-3.5 w-3.5\" />\n Contains AI-generated content\n </div>\n )}\n </dl>\n </GlassCard>\n\n {/* Genres & tags */}\n {(title.genres.length > 0 || title.tags.length > 0) && (\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-5\">\n {title.genres.length > 0 && (\n <>\n <h3 className=\"mb-2 text-xs font-semibold uppercase tracking-wide text-text-secondary\">\n Genres\n </h3>\n <div className=\"mb-3 flex flex-wrap gap-1.5\">\n {title.genres.map((g) => (\n <span\n key={g}\n className=\"rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs text-text-primary\"\n >\n {g}\n </span>\n ))}\n </div>\n </>\n )}\n {title.tags.length > 0 && (\n <>\n <h3 className=\"mb-2 text-xs font-semibold uppercase tracking-wide text-text-secondary\">\n Tags\n </h3>\n <div className=\"flex flex-wrap gap-1.5\">\n {title.tags.map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs text-text-secondary\"\n >\n {tag}\n </span>\n ))}\n </div>\n </>\n )}\n </GlassCard>\n )}\n </div>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TitleDetailPage.js","names":[],"sources":["../../../../src/hookmovies/pages/catalog/TitleDetailPage.tsx"],"sourcesContent":["/**\n * Title detail (Catalog)\n * @module hookmovies/pages/catalog\n *\n * Reads a single title via `hokTitle` (with credits, seasons, episodes &\n * studio), and drives the publication lifecycle via `hokTransitionTitle` plus\n * archive via `hokArchiveTitle`. The reviews for the title come from\n * `hokListReviews(titleId)`.\n *\n * Editing surfaces (all inline, tokenized panels):\n * - Edit title metadata (`hokUpdateTitle`, reuses the create-form field set).\n * - Add a cast/crew credit (`hokAddCredit`, person picker with live search).\n * - Add a season / episode for SERIES titles (`hokCreateSeason` / `hokCreateEpisode`).\n * - Watch / mark-as-watched, gated by `hokCheckEntitlement` before recording\n * progress via `hokRecordProgress` (no fake player when not entitled).\n */\n\nimport { type FC, type FormEvent, type ChangeEvent, useRef, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n Calendar,\n Clock,\n Globe,\n Languages,\n ShieldCheck,\n Sparkles,\n Users,\n Layers,\n Archive,\n Star,\n Pencil,\n UserPlus,\n Plus,\n Play,\n Lock,\n CheckCircle2,\n X,\n Image as ImageIcon,\n Film,\n Clapperboard,\n Video,\n Upload,\n Trash2,\n Loader2,\n type LucideIcon,\n} from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { LoadingState, ErrorState } from '../../components/StateViews';\nimport { EmptyState } from '../../components/EmptyState';\nimport { Pagination } from '../../components/Pagination';\nimport { RatingDisplay } from '../../components/RatingDisplay';\nimport { useHookMovies } from '../../context/HookMoviesContext';\nimport {\n useTitle,\n useTransitionTitle,\n useArchiveTitle,\n useReviews,\n useUpdateTitle,\n useAddCredit,\n useCreateSeason,\n useCreateEpisode,\n useCheckEntitlement,\n useRecordProgress,\n useClips,\n useCreateClip,\n useDeleteClip,\n usePeople,\n useStudios,\n type HokClip,\n type HokTitleStatus,\n type HokTitleType,\n type HokCreditRole,\n type UpdateTitleInput,\n} from '../../hooks/useHookMoviesApi';\nimport { uploadFileToFiles, useFileUrl, FILE_FOLDERS } from '../../hooks/useFiles';\nimport { formatDate, formatEnumLabel } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\n\n/**\n * Publication transitions surfaced as buttons. Mirrors the backend\n * `TITLE_TRANSITIONS` map (catalog.service.ts) exactly, minus `ARCHIVED` —\n * archiving is offered by the dedicated Archive button (via `hokArchiveTitle`)\n * on every non-archived state, so it is intentionally omitted here.\n */\nconst NEXT_STATUSES: Record<HokTitleStatus, HokTitleStatus[]> = {\n DRAFT: ['IN_REVIEW'],\n IN_REVIEW: ['SCHEDULED', 'PUBLISHED', 'DRAFT'],\n SCHEDULED: ['PUBLISHED', 'IN_REVIEW'],\n PUBLISHED: ['UNPUBLISHED'],\n UNPUBLISHED: ['PUBLISHED'],\n ARCHIVED: [],\n};\n\nconst TYPE_OPTIONS: HokTitleType[] = [\n 'MOVIE',\n 'SERIES',\n 'SHORT_FILM',\n 'DOCUMENTARY',\n 'ANIME',\n 'SHORT_FORM',\n 'LONG_FORM',\n 'LIVE_EVENT',\n 'TRAILER',\n 'COLLECTION',\n 'FAST_CHANNEL',\n];\n\nconst CREDIT_ROLE_OPTIONS: HokCreditRole[] = [\n 'ACTOR',\n 'DIRECTOR',\n 'WRITER',\n 'PRODUCER',\n 'CINEMATOGRAPHER',\n 'EDITOR',\n 'COMPOSER',\n 'VOICE_ACTOR',\n 'CREW',\n];\n\nconst fieldClass =\n 'w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\nconst labelClass = 'mb-1.5 block text-xs font-medium text-text-secondary';\n\nconst splitCsv = (value: string): string[] =>\n value\n .split(',')\n .map((v) => v.trim())\n .filter(Boolean);\n\n/** Convert a `<input type=\"date\">` value to an ISO DateTime, or undefined when empty. */\nconst dateToIso = (value: string): string | undefined =>\n value.trim() ? new Date(value).toISOString() : undefined;\n\n/** Format a clip's duration (seconds) as `m:ss`. */\nconst formatClipDuration = (seconds: number): string => {\n const m = Math.floor(seconds / 60);\n const s = Math.floor(seconds % 60);\n return `${m}:${s.toString().padStart(2, '0')}`;\n};\n\n// ── Artwork & Media slots ─────────────────────────────────────────────────────\n// The four media refs a title carries. Posters/backdrops are images filed under\n// Artwork; trailer/video masters are video files filed under Masters. Each slot\n// uploads to wspace-files-svc, then persists only the returned file id on the\n// title (this MFE stores no blobs).\ntype MediaField = 'posterFileId' | 'backdropFileId' | 'trailerFileId' | 'videoFileId';\n\ninterface MediaSlotConfig {\n field: MediaField;\n label: string;\n kind: 'image' | 'video';\n folder: string;\n accept: string;\n icon: LucideIcon;\n hint: string;\n}\n\nconst MEDIA_SLOTS: MediaSlotConfig[] = [\n {\n field: 'posterFileId',\n label: 'Poster',\n kind: 'image',\n folder: FILE_FOLDERS.artwork,\n accept: 'image/*',\n icon: ImageIcon,\n hint: 'Vertical key art (2:3).',\n },\n {\n field: 'backdropFileId',\n label: 'Backdrop',\n kind: 'image',\n folder: FILE_FOLDERS.artwork,\n accept: 'image/*',\n icon: ImageIcon,\n hint: 'Wide hero art (16:9).',\n },\n {\n field: 'trailerFileId',\n label: 'Trailer',\n kind: 'video',\n folder: FILE_FOLDERS.masters,\n accept: 'video/*',\n icon: Film,\n hint: 'Promo trailer master.',\n },\n {\n field: 'videoFileId',\n label: 'Video master',\n kind: 'video',\n folder: FILE_FOLDERS.masters,\n accept: 'video/*',\n icon: Clapperboard,\n hint: 'Full-length feature master.',\n },\n];\n\ninterface MediaSlotProps {\n titleId: string;\n config: MediaSlotConfig;\n currentFileId: string | null;\n}\n\n/**\n * A single Artwork/Media upload slot: shows the current file (image thumbnail\n * or inline video), and offers upload / replace / remove. Upload goes through\n * `uploadFileToFiles` (3-step wspace-files) then persists the id on the title\n * via `useUpdateTitle`; remove clears the ref.\n */\nconst MediaSlot: FC<MediaSlotProps> = ({ titleId, config, currentFileId }) => {\n const updateTitle = useUpdateTitle();\n const fileUrl = useFileUrl(currentFileId);\n const inputRef = useRef<HTMLInputElement>(null);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const Icon = config.icon;\n\n const handleSelect = async (e: ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n // Reset the input so the same file can be re-picked after an error.\n e.target.value = '';\n if (!file) return;\n setError(null);\n setBusy(true);\n try {\n const uploaded = await uploadFileToFiles(file, config.folder);\n const input: UpdateTitleInput = {};\n input[config.field] = uploaded.id;\n await updateTitle.mutateAsync({ id: titleId, input });\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Upload failed');\n } finally {\n setBusy(false);\n }\n };\n\n const handleRemove = async () => {\n setError(null);\n setBusy(true);\n try {\n // Explicit null clears the column on the backend. UpdateTitleInput types\n // the media fields as optional strings for the common set-path, so this\n // single clear payload is cast.\n const input = { [config.field]: null } as UpdateTitleInput;\n await updateTitle.mutateAsync({ id: titleId, input });\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Could not remove file');\n } finally {\n setBusy(false);\n }\n };\n\n return (\n <div className=\"rounded-xl border border-border-seam bg-bg-surface p-4\">\n <div className=\"mb-3 flex items-center justify-between gap-2\">\n <div className=\"flex items-center gap-2\">\n <Icon className=\"h-4 w-4 text-text-secondary\" />\n <h3 className=\"text-sm font-medium text-text-primary\">{config.label}</h3>\n </div>\n {currentFileId && (\n <span className=\"rounded-full bg-status-success-bg-subtle px-2 py-0.5 text-[10px] font-medium text-status-success-text\">\n Set\n </span>\n )}\n </div>\n\n <div\n className={cn(\n 'relative flex items-center justify-center overflow-hidden rounded-lg border border-border-subtle bg-bg-sunken',\n config.kind === 'image' ? 'aspect-[3/4]' : 'aspect-video'\n )}\n >\n {busy ? (\n <div className=\"flex flex-col items-center gap-2 text-text-secondary\">\n <Loader2 className=\"h-6 w-6 animate-spin\" />\n <span className=\"text-xs\">Uploading…</span>\n </div>\n ) : currentFileId ? (\n fileUrl.isLoading ? (\n <Loader2 className=\"h-6 w-6 animate-spin text-text-secondary\" />\n ) : fileUrl.data ? (\n config.kind === 'image' ? (\n <img\n src={fileUrl.data}\n alt={`${config.label} artwork`}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <video\n src={fileUrl.data}\n controls\n preload=\"metadata\"\n className=\"h-full w-full bg-bg-canvas object-contain\"\n >\n <track kind=\"captions\" />\n </video>\n )\n ) : (\n <div className=\"flex flex-col items-center gap-1 text-text-secondary\">\n <Icon className=\"h-6 w-6\" />\n <span className=\"text-xs\">Preview unavailable</span>\n </div>\n )\n ) : (\n <button\n type=\"button\"\n onClick={() => inputRef.current?.click()}\n className=\"flex h-full w-full flex-col items-center justify-center gap-2 text-text-secondary transition-colors hover:text-text-primary\"\n >\n <Upload className=\"h-6 w-6\" />\n <span className=\"text-xs\">Upload {config.label.toLowerCase()}</span>\n </button>\n )}\n </div>\n\n <p className=\"mt-2 text-[11px] text-text-secondary\">{config.hint}</p>\n\n {error && <p className=\"mt-2 text-xs text-status-error-text\">{error}</p>}\n\n <div className=\"mt-3 flex items-center gap-2\">\n <button\n type=\"button\"\n disabled={busy}\n onClick={() => inputRef.current?.click()}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-2.5 py-1 text-xs font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-50\"\n >\n <Upload className=\"h-3.5 w-3.5\" />\n {currentFileId ? 'Replace' : 'Upload'}\n </button>\n {currentFileId && (\n <button\n type=\"button\"\n disabled={busy}\n onClick={() => void handleRemove()}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-status-error-border px-2.5 py-1 text-xs font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-50\"\n >\n <Trash2 className=\"h-3.5 w-3.5\" />\n Remove\n </button>\n )}\n </div>\n\n <input\n ref={inputRef}\n type=\"file\"\n accept={config.accept}\n className=\"hidden\"\n onChange={(e) => void handleSelect(e)}\n aria-label={`Upload ${config.label}`}\n />\n </div>\n );\n};\n\ninterface ClipCardProps {\n clip: HokClip;\n onDelete: (clip: HokClip) => void;\n deleting: boolean;\n}\n\n/** A single clip tile: vertical poster (or inline video) + caption/meta + delete. */\nconst ClipCard: FC<ClipCardProps> = ({ clip, onDelete, deleting }) => {\n const posterUrl = useFileUrl(clip.posterFileId);\n // Only resolve the video URL when there is no poster to preview.\n const videoUrl = useFileUrl(clip.posterFileId ? undefined : clip.videoFileId);\n\n return (\n <div className=\"flex flex-col overflow-hidden rounded-xl border border-border-seam bg-bg-surface\">\n <div className=\"relative aspect-[9/16] w-full overflow-hidden bg-bg-sunken\">\n {clip.posterFileId ? (\n posterUrl.isLoading ? (\n <div className=\"flex h-full w-full items-center justify-center\">\n <Loader2 className=\"h-5 w-5 animate-spin text-text-secondary\" />\n </div>\n ) : posterUrl.data ? (\n <img\n src={posterUrl.data}\n alt={clip.caption ?? 'Clip poster'}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <div className=\"flex h-full w-full items-center justify-center text-text-secondary\">\n <Film className=\"h-6 w-6\" />\n </div>\n )\n ) : videoUrl.isLoading ? (\n <div className=\"flex h-full w-full items-center justify-center\">\n <Loader2 className=\"h-5 w-5 animate-spin text-text-secondary\" />\n </div>\n ) : videoUrl.data ? (\n <video\n src={videoUrl.data}\n controls\n preload=\"metadata\"\n className=\"h-full w-full bg-bg-canvas object-contain\"\n >\n <track kind=\"captions\" />\n </video>\n ) : (\n <div className=\"flex h-full w-full items-center justify-center text-text-secondary\">\n <Film className=\"h-6 w-6\" />\n </div>\n )}\n {clip.durationSeconds != null && (\n <span className=\"absolute bottom-2 right-2 rounded bg-bg-canvas/80 px-1.5 py-0.5 text-[10px] font-medium text-text-primary\">\n {formatClipDuration(clip.durationSeconds)}\n </span>\n )}\n </div>\n <div className=\"flex flex-1 flex-col gap-2 p-3\">\n <p className=\"line-clamp-2 text-sm text-text-primary\">{clip.caption ?? 'Untitled clip'}</p>\n {clip.language && (\n <span className=\"w-fit rounded-full bg-bg-sunken px-2 py-0.5 text-[10px] uppercase tracking-wide text-text-secondary\">\n {clip.language}\n </span>\n )}\n <button\n type=\"button\"\n disabled={deleting}\n onClick={() => onDelete(clip)}\n className=\"mt-auto inline-flex items-center justify-center gap-1.5 rounded-lg border border-status-error-border px-2.5 py-1 text-xs font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-50\"\n >\n <Trash2 className=\"h-3.5 w-3.5\" />\n {deleting ? 'Deleting…' : 'Delete'}\n </button>\n </div>\n </div>\n );\n};\n\nconst CLIP_PAGE_SIZE = 12;\n\nexport const TitleDetailPage: FC = () => {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useHookMovies();\n const { data: title, isLoading, isError, refetch } = useTitle(id);\n const transition = useTransitionTitle();\n const archive = useArchiveTitle();\n const reviews = useReviews({ titleId: id }, { limit: 10, offset: 0 });\n const updateTitle = useUpdateTitle();\n const addCredit = useAddCredit();\n const createSeason = useCreateSeason();\n const createEpisode = useCreateEpisode();\n const recordProgress = useRecordProgress();\n const entitlement = useCheckEntitlement(id);\n const studios = useStudios({ limit: 200, offset: 0 });\n\n // ── Clips (short-form promos attached to the title) ────────────────────────\n const [clipPage, setClipPage] = useState(0);\n const clips = useClips(id, undefined, {\n limit: CLIP_PAGE_SIZE,\n offset: clipPage * CLIP_PAGE_SIZE,\n });\n const createClip = useCreateClip();\n const deleteClip = useDeleteClip();\n\n const [actionError, setActionError] = useState<string | null>(null);\n\n // ── Add-clip form state ─────────────────────────────────────────────────────\n const [showClipForm, setShowClipForm] = useState(false);\n const [clipFile, setClipFile] = useState<File | null>(null);\n const [clipCaption, setClipCaption] = useState('');\n const [clipDuration, setClipDuration] = useState('');\n const [clipLanguage, setClipLanguage] = useState('');\n const [clipUploading, setClipUploading] = useState(false);\n const [clipError, setClipError] = useState<string | null>(null);\n const [deletingClipId, setDeletingClipId] = useState<string | null>(null);\n const clipInputRef = useRef<HTMLInputElement>(null);\n\n // ── Which inline panel (if any) is open ────────────────────────────────────\n const [openPanel, setOpenPanel] = useState<'none' | 'edit' | 'credit' | 'season' | 'episode'>(\n 'none'\n );\n\n // ── Edit-title form state ──────────────────────────────────────────────────\n const [eTitle, setETitle] = useState('');\n const [eType, setEType] = useState<HokTitleType>('MOVIE');\n const [eSynopsis, setESynopsis] = useState('');\n const [eGenres, setEGenres] = useState('');\n const [eTags, setETags] = useState('');\n const [eLanguage, setELanguage] = useState('');\n const [eCountry, setECountry] = useState('');\n const [eYear, setEYear] = useState('');\n const [eRuntime, setERuntime] = useState('');\n const [eCertification, setECertification] = useState('');\n const [eStudioId, setEStudioId] = useState('');\n const [eAiDisclosure, setEAiDisclosure] = useState(false);\n\n // ── Add-credit form state ──────────────────────────────────────────────────\n const [personSearch, setPersonSearch] = useState('');\n const [creditPersonId, setCreditPersonId] = useState('');\n const [creditRole, setCreditRole] = useState<HokCreditRole>('ACTOR');\n const [creditCharacter, setCreditCharacter] = useState('');\n const [creditBilling, setCreditBilling] = useState('');\n const people = usePeople({ limit: 50, offset: 0 }, personSearch);\n\n // ── Add-season form state ──────────────────────────────────────────────────\n const [seasonNumber, setSeasonNumber] = useState('');\n const [seasonName, setSeasonName] = useState('');\n const [seasonSynopsis, setSeasonSynopsis] = useState('');\n const [seasonAirDate, setSeasonAirDate] = useState('');\n\n // ── Add-episode form state ─────────────────────────────────────────────────\n const [episodeSeasonId, setEpisodeSeasonId] = useState('');\n const [episodeNumber, setEpisodeNumber] = useState('');\n const [episodeName, setEpisodeName] = useState('');\n const [episodeSynopsis, setEpisodeSynopsis] = useState('');\n const [episodeRuntime, setEpisodeRuntime] = useState('');\n const [episodeAirDate, setEpisodeAirDate] = useState('');\n\n const [watchedNotice, setWatchedNotice] = useState(false);\n\n if (isLoading) {\n return (\n <PageLayout title=\"Title\">\n <LoadingState label=\"Loading title…\" />\n </PageLayout>\n );\n }\n\n if (isError || !title) {\n return (\n <PageLayout title=\"Title\">\n <ErrorState\n title=\"Title not found\"\n message=\"This title may have been archived or you may not have access.\"\n onRetry={() => void refetch()}\n />\n </PageLayout>\n );\n }\n\n const isSeries = title.type === 'SERIES';\n\n const closePanel = () => setOpenPanel('none');\n\n const openEdit = () => {\n setActionError(null);\n setETitle(title.title);\n setEType(title.type);\n setESynopsis(title.synopsis ?? '');\n setEGenres(title.genres.join(', '));\n setETags(title.tags.join(', '));\n setELanguage(title.originalLanguage ?? '');\n setECountry(title.originCountry ?? '');\n setEYear(title.releaseYear ? String(title.releaseYear) : '');\n setERuntime(title.runtimeMinutes ? String(title.runtimeMinutes) : '');\n setECertification(title.certification ?? '');\n setEStudioId(title.studioId ?? '');\n setEAiDisclosure(title.aiDisclosure);\n setOpenPanel('edit');\n };\n\n const handleTransition = async (status: HokTitleStatus) => {\n setActionError(null);\n try {\n await transition.mutateAsync({ id: title.id, status });\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Transition failed');\n }\n };\n\n const handleArchive = async () => {\n setActionError(null);\n try {\n await archive.mutateAsync(title.id);\n navigateTo('catalog/titles');\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Archive failed');\n }\n };\n\n const handleEditSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setActionError(null);\n if (!eTitle.trim()) {\n setActionError('Title is required.');\n return;\n }\n const input: UpdateTitleInput = {\n title: eTitle.trim(),\n type: eType,\n synopsis: eSynopsis.trim() || undefined,\n genres: splitCsv(eGenres),\n tags: splitCsv(eTags),\n originalLanguage: eLanguage.trim() || undefined,\n originCountry: eCountry.trim() || undefined,\n releaseYear: eYear.trim() ? Number(eYear) : undefined,\n runtimeMinutes: eRuntime.trim() ? Number(eRuntime) : undefined,\n certification: eCertification.trim() || undefined,\n studioId: eStudioId || undefined,\n aiDisclosure: eAiDisclosure,\n };\n try {\n await updateTitle.mutateAsync({ id: title.id, input });\n closePanel();\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to update title');\n }\n };\n\n const handleCreditSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setActionError(null);\n if (!creditPersonId) {\n setActionError('Select a person to credit.');\n return;\n }\n try {\n await addCredit.mutateAsync({\n personId: creditPersonId,\n titleId: title.id,\n role: creditRole,\n characterName: creditCharacter.trim() || undefined,\n billingOrder: creditBilling.trim() ? Number(creditBilling) : undefined,\n });\n setCreditPersonId('');\n setCreditCharacter('');\n setCreditBilling('');\n setPersonSearch('');\n closePanel();\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to add credit');\n }\n };\n\n const handleSeasonSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setActionError(null);\n if (!seasonNumber.trim()) {\n setActionError('Season number is required.');\n return;\n }\n try {\n await createSeason.mutateAsync({\n titleId: title.id,\n number: Number(seasonNumber),\n name: seasonName.trim() || undefined,\n synopsis: seasonSynopsis.trim() || undefined,\n airDate: dateToIso(seasonAirDate),\n });\n setSeasonNumber('');\n setSeasonName('');\n setSeasonSynopsis('');\n setSeasonAirDate('');\n closePanel();\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to add season');\n }\n };\n\n const handleEpisodeSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setActionError(null);\n if (!episodeNumber.trim()) {\n setActionError('Episode number is required.');\n return;\n }\n if (!episodeName.trim()) {\n setActionError('Episode name is required.');\n return;\n }\n try {\n await createEpisode.mutateAsync({\n titleId: title.id,\n number: Number(episodeNumber),\n name: episodeName.trim(),\n seasonId: episodeSeasonId || undefined,\n synopsis: episodeSynopsis.trim() || undefined,\n runtimeMinutes: episodeRuntime.trim() ? Number(episodeRuntime) : undefined,\n airDate: dateToIso(episodeAirDate),\n });\n setEpisodeSeasonId('');\n setEpisodeNumber('');\n setEpisodeName('');\n setEpisodeSynopsis('');\n setEpisodeRuntime('');\n setEpisodeAirDate('');\n closePanel();\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to add episode');\n }\n };\n\n const handleMarkWatched = async () => {\n setActionError(null);\n setWatchedNotice(false);\n if (!entitlement.data?.entitled) {\n setActionError('You are not entitled to watch this title.');\n return;\n }\n try {\n await recordProgress.mutateAsync({\n titleId: title.id,\n positionSeconds: (title.runtimeMinutes ?? 0) * 60,\n completed: true,\n lastDevice: 'web',\n });\n setWatchedNotice(true);\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to record progress');\n }\n };\n\n const handleClipSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setClipError(null);\n if (!clipFile) {\n setClipError('Select a clip video to upload.');\n return;\n }\n setClipUploading(true);\n try {\n const uploaded = await uploadFileToFiles(clipFile, FILE_FOLDERS.clips);\n await createClip.mutateAsync({\n titleId: title.id,\n videoFileId: uploaded.id,\n caption: clipCaption.trim() || undefined,\n durationSeconds: clipDuration.trim() ? Number(clipDuration) : undefined,\n language: clipLanguage.trim() || undefined,\n });\n setClipFile(null);\n setClipCaption('');\n setClipDuration('');\n setClipLanguage('');\n if (clipInputRef.current) clipInputRef.current.value = '';\n setShowClipForm(false);\n setClipPage(0);\n } catch (err) {\n setClipError(err instanceof Error ? err.message : 'Failed to add clip');\n } finally {\n setClipUploading(false);\n }\n };\n\n const handleClipDelete = async (clip: HokClip) => {\n setActionError(null);\n setDeletingClipId(clip.id);\n try {\n await deleteClip.mutateAsync({ id: clip.id, titleId: title.id });\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to delete clip');\n } finally {\n setDeletingClipId(null);\n }\n };\n\n const reviewItems = reviews.data?.items ?? [];\n const clipItems = clips.data?.items ?? [];\n const avgScore =\n reviewItems.length > 0\n ? reviewItems.reduce((sum, r) => sum + r.score, 0) / reviewItems.length\n : null;\n\n const facts: Array<{ icon: typeof Calendar; label: string; value: string }> = [];\n if (title.releaseDate)\n facts.push({ icon: Calendar, label: 'Release', value: formatDate(title.releaseDate) });\n else if (title.releaseYear)\n facts.push({ icon: Calendar, label: 'Year', value: String(title.releaseYear) });\n if (title.runtimeMinutes)\n facts.push({ icon: Clock, label: 'Runtime', value: `${title.runtimeMinutes} min` });\n if (title.originalLanguage)\n facts.push({ icon: Languages, label: 'Language', value: title.originalLanguage });\n if (title.originCountry)\n facts.push({ icon: Globe, label: 'Country', value: title.originCountry });\n if (title.certification)\n facts.push({ icon: ShieldCheck, label: 'Certification', value: title.certification });\n\n const peopleItems = people.data?.items ?? [];\n\n return (\n <PageLayout\n title={\n <span className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigateTo('catalog/titles')}\n className=\"rounded-md p-1 text-text-secondary hover:text-text-primary\"\n aria-label=\"Back to catalog\"\n >\n <ArrowLeft className=\"h-5 w-5\" />\n </button>\n {title.title}\n </span>\n }\n description={`${formatEnumLabel(title.type)}${title.studio ? ` · ${title.studio.name}` : ''}`}\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={openEdit}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n <Pencil className=\"h-3.5 w-3.5\" />\n Edit\n </button>\n <StatusBadge status={title.status} />\n </div>\n }\n >\n {actionError && (\n <div className=\"mb-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text\">\n {actionError}\n </div>\n )}\n\n {/* Edit-title panel (full-width; reuses the create-form field set) */}\n {openPanel === 'edit' && (\n <GlassCard treatment=\"metric\" className=\"mb-6 border-border-seam p-5\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-sm font-semibold text-text-primary\">Edit title</h2>\n <button\n type=\"button\"\n onClick={closePanel}\n className=\"rounded-md p-1 text-text-secondary hover:text-text-primary\"\n aria-label=\"Close edit form\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n <form onSubmit={handleEditSubmit} className=\"space-y-5\">\n <div>\n <label htmlFor=\"edit-title\" className={labelClass}>\n Title *\n </label>\n <input\n id=\"edit-title\"\n type=\"text\"\n value={eTitle}\n onChange={(e) => setETitle(e.target.value)}\n className={fieldClass}\n required\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"edit-type\" className={labelClass}>\n Type\n </label>\n <select\n id=\"edit-type\"\n value={eType}\n onChange={(e) => setEType(e.target.value as HokTitleType)}\n className={fieldClass}\n >\n {TYPE_OPTIONS.map((t) => (\n <option key={t} value={t}>\n {formatEnumLabel(t)}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor=\"edit-studio\" className={labelClass}>\n Studio\n </label>\n <select\n id=\"edit-studio\"\n value={eStudioId}\n onChange={(e) => setEStudioId(e.target.value)}\n className={fieldClass}\n >\n <option value=\"\">No studio</option>\n {(studios.data?.items ?? []).map((s) => (\n <option key={s.id} value={s.id}>\n {s.name}\n </option>\n ))}\n </select>\n </div>\n </div>\n\n <div>\n <label htmlFor=\"edit-synopsis\" className={labelClass}>\n Synopsis\n </label>\n <textarea\n id=\"edit-synopsis\"\n value={eSynopsis}\n onChange={(e) => setESynopsis(e.target.value)}\n rows={4}\n className={fieldClass}\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"edit-genres\" className={labelClass}>\n Genres (comma-separated)\n </label>\n <input\n id=\"edit-genres\"\n type=\"text\"\n value={eGenres}\n onChange={(e) => setEGenres(e.target.value)}\n placeholder=\"Drama, Thriller\"\n className={fieldClass}\n />\n </div>\n <div>\n <label htmlFor=\"edit-tags\" className={labelClass}>\n Tags (comma-separated)\n </label>\n <input\n id=\"edit-tags\"\n type=\"text\"\n value={eTags}\n onChange={(e) => setETags(e.target.value)}\n placeholder=\"award-winner, indie\"\n className={fieldClass}\n />\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div>\n <label htmlFor=\"edit-language\" className={labelClass}>\n Original Language\n </label>\n <input\n id=\"edit-language\"\n type=\"text\"\n value={eLanguage}\n onChange={(e) => setELanguage(e.target.value)}\n placeholder=\"en\"\n className={fieldClass}\n />\n </div>\n <div>\n <label htmlFor=\"edit-country\" className={labelClass}>\n Origin Country\n </label>\n <input\n id=\"edit-country\"\n type=\"text\"\n value={eCountry}\n onChange={(e) => setECountry(e.target.value)}\n placeholder=\"US\"\n className={fieldClass}\n />\n </div>\n <div>\n <label htmlFor=\"edit-certification\" className={labelClass}>\n Certification\n </label>\n <input\n id=\"edit-certification\"\n type=\"text\"\n value={eCertification}\n onChange={(e) => setECertification(e.target.value)}\n placeholder=\"PG-13\"\n className={fieldClass}\n />\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"edit-year\" className={labelClass}>\n Release Year\n </label>\n <input\n id=\"edit-year\"\n type=\"number\"\n value={eYear}\n onChange={(e) => setEYear(e.target.value)}\n placeholder=\"2026\"\n className={fieldClass}\n />\n </div>\n <div>\n <label htmlFor=\"edit-runtime\" className={labelClass}>\n Runtime (minutes)\n </label>\n <input\n id=\"edit-runtime\"\n type=\"number\"\n value={eRuntime}\n onChange={(e) => setERuntime(e.target.value)}\n placeholder=\"120\"\n className={fieldClass}\n />\n </div>\n </div>\n\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={eAiDisclosure}\n onChange={(e) => setEAiDisclosure(e.target.checked)}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n This title contains AI-generated or AI-altered content\n </label>\n\n <div className=\"flex items-center gap-3 pt-1\">\n <button\n type=\"submit\"\n disabled={updateTitle.isPending}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50 transition-opacity\"\n >\n {updateTitle.isPending ? 'Saving…' : 'Save changes'}\n </button>\n <button\n type=\"button\"\n onClick={closePanel}\n className=\"rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n Cancel\n </button>\n </div>\n </form>\n </GlassCard>\n )}\n\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Main column */}\n <div className=\"space-y-6 lg:col-span-2\">\n {/* Artwork & Media */}\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-5\">\n <div className=\"mb-4\">\n <h2 className=\"flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <ImageIcon className=\"h-4 w-4\" />\n Artwork & Media\n </h2>\n <p className=\"mt-1 text-xs text-text-secondary\">\n Poster and backdrop art, plus the trailer and full-length video master. Files are\n stored in the workspace files service; the title keeps only a reference.\n </p>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4\">\n {MEDIA_SLOTS.map((slot) => (\n <MediaSlot\n key={slot.field}\n titleId={title.id}\n config={slot}\n currentFileId={title[slot.field]}\n />\n ))}\n </div>\n </GlassCard>\n\n {title.synopsis && (\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-5\">\n <h2 className=\"mb-2 text-sm font-semibold text-text-primary\">Synopsis</h2>\n <p className=\"text-sm leading-relaxed text-text-secondary\">{title.synopsis}</p>\n </GlassCard>\n )}\n\n {/* Credits */}\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-5\">\n <div className=\"mb-3 flex items-center justify-between gap-3\">\n <h2 className=\"flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <Users className=\"h-4 w-4\" />\n Cast & Crew ({title.credits.length})\n </h2>\n <button\n type=\"button\"\n onClick={() => {\n setActionError(null);\n setOpenPanel(openPanel === 'credit' ? 'none' : 'credit');\n }}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-2.5 py-1 text-xs font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n <UserPlus className=\"h-3.5 w-3.5\" />\n Add credit\n </button>\n </div>\n\n {openPanel === 'credit' && (\n <form\n onSubmit={handleCreditSubmit}\n className=\"mb-4 space-y-4 rounded-lg border border-border-seam bg-bg-surface p-4\"\n >\n <div>\n <label htmlFor=\"credit-search\" className={labelClass}>\n Search people\n </label>\n <input\n id=\"credit-search\"\n type=\"text\"\n value={personSearch}\n onChange={(e) => setPersonSearch(e.target.value)}\n placeholder=\"Type a name to filter…\"\n className={fieldClass}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"credit-person\" className={labelClass}>\n Person *\n </label>\n <select\n id=\"credit-person\"\n value={creditPersonId}\n onChange={(e) => setCreditPersonId(e.target.value)}\n className={fieldClass}\n required\n >\n <option value=\"\">\n {people.isLoading\n ? 'Loading people…'\n : peopleItems.length === 0\n ? 'No people found'\n : 'Select a person'}\n </option>\n {peopleItems.map((p) => (\n <option key={p.id} value={p.id}>\n {p.name}\n {p.knownFor ? ` — ${p.knownFor}` : ''}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor=\"credit-role\" className={labelClass}>\n Role\n </label>\n <select\n id=\"credit-role\"\n value={creditRole}\n onChange={(e) => setCreditRole(e.target.value as HokCreditRole)}\n className={fieldClass}\n >\n {CREDIT_ROLE_OPTIONS.map((r) => (\n <option key={r} value={r}>\n {formatEnumLabel(r)}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"credit-character\" className={labelClass}>\n Character name\n </label>\n <input\n id=\"credit-character\"\n type=\"text\"\n value={creditCharacter}\n onChange={(e) => setCreditCharacter(e.target.value)}\n placeholder=\"e.g. Captain Rao\"\n className={fieldClass}\n />\n </div>\n <div>\n <label htmlFor=\"credit-billing\" className={labelClass}>\n Billing order\n </label>\n <input\n id=\"credit-billing\"\n type=\"number\"\n value={creditBilling}\n onChange={(e) => setCreditBilling(e.target.value)}\n placeholder=\"1\"\n className={fieldClass}\n />\n </div>\n </div>\n <div className=\"flex items-center gap-3\">\n <button\n type=\"submit\"\n disabled={addCredit.isPending}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50 transition-opacity\"\n >\n <Plus className=\"h-4 w-4\" />\n {addCredit.isPending ? 'Adding…' : 'Add credit'}\n </button>\n <button\n type=\"button\"\n onClick={closePanel}\n className=\"rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n Cancel\n </button>\n </div>\n </form>\n )}\n\n {title.credits.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">No credits recorded yet.</p>\n ) : (\n <ul className=\"divide-y divide-border-seam\">\n {[...title.credits]\n .sort((a, b) => a.billingOrder - b.billingOrder)\n .map((credit) => (\n <li key={credit.id} className=\"flex items-center justify-between py-2\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {credit.person?.name ?? 'Unknown person'}\n </p>\n {credit.characterName && (\n <p className=\"text-xs text-text-secondary\">as {credit.characterName}</p>\n )}\n </div>\n <span className=\"rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs text-text-secondary\">\n {formatEnumLabel(credit.role)}\n </span>\n </li>\n ))}\n </ul>\n )}\n </GlassCard>\n\n {/* Seasons & episodes */}\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-5\">\n <div className=\"mb-3 flex flex-wrap items-center justify-between gap-3\">\n <h2 className=\"flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <Layers className=\"h-4 w-4\" />\n Episodes ({title.episodes.length})\n </h2>\n {isSeries && (\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => {\n setActionError(null);\n setOpenPanel(openPanel === 'season' ? 'none' : 'season');\n }}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-2.5 py-1 text-xs font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n <Plus className=\"h-3.5 w-3.5\" />\n Add season\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setActionError(null);\n setOpenPanel(openPanel === 'episode' ? 'none' : 'episode');\n }}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-2.5 py-1 text-xs font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n <Plus className=\"h-3.5 w-3.5\" />\n Add episode\n </button>\n </div>\n )}\n </div>\n\n {isSeries && openPanel === 'season' && (\n <form\n onSubmit={handleSeasonSubmit}\n className=\"mb-4 space-y-4 rounded-lg border border-border-seam bg-bg-surface p-4\"\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"season-number\" className={labelClass}>\n Season number *\n </label>\n <input\n id=\"season-number\"\n type=\"number\"\n value={seasonNumber}\n onChange={(e) => setSeasonNumber(e.target.value)}\n placeholder=\"1\"\n className={fieldClass}\n required\n />\n </div>\n <div>\n <label htmlFor=\"season-name\" className={labelClass}>\n Name\n </label>\n <input\n id=\"season-name\"\n type=\"text\"\n value={seasonName}\n onChange={(e) => setSeasonName(e.target.value)}\n placeholder=\"Season 1\"\n className={fieldClass}\n />\n </div>\n </div>\n <div>\n <label htmlFor=\"season-synopsis\" className={labelClass}>\n Synopsis\n </label>\n <textarea\n id=\"season-synopsis\"\n value={seasonSynopsis}\n onChange={(e) => setSeasonSynopsis(e.target.value)}\n rows={3}\n className={fieldClass}\n />\n </div>\n <div>\n <label htmlFor=\"season-airdate\" className={labelClass}>\n Air date\n </label>\n <input\n id=\"season-airdate\"\n type=\"date\"\n value={seasonAirDate}\n onChange={(e) => setSeasonAirDate(e.target.value)}\n className={fieldClass}\n />\n </div>\n <div className=\"flex items-center gap-3\">\n <button\n type=\"submit\"\n disabled={createSeason.isPending}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50 transition-opacity\"\n >\n <Plus className=\"h-4 w-4\" />\n {createSeason.isPending ? 'Adding…' : 'Add season'}\n </button>\n <button\n type=\"button\"\n onClick={closePanel}\n className=\"rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n Cancel\n </button>\n </div>\n </form>\n )}\n\n {isSeries && openPanel === 'episode' && (\n <form\n onSubmit={handleEpisodeSubmit}\n className=\"mb-4 space-y-4 rounded-lg border border-border-seam bg-bg-surface p-4\"\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"episode-season\" className={labelClass}>\n Season\n </label>\n <select\n id=\"episode-season\"\n value={episodeSeasonId}\n onChange={(e) => setEpisodeSeasonId(e.target.value)}\n className={fieldClass}\n >\n <option value=\"\">No season</option>\n {[...title.seasons]\n .sort((a, b) => a.number - b.number)\n .map((s) => (\n <option key={s.id} value={s.id}>\n {s.name ? `${s.number}. ${s.name}` : `Season ${s.number}`}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor=\"episode-number\" className={labelClass}>\n Episode number *\n </label>\n <input\n id=\"episode-number\"\n type=\"number\"\n value={episodeNumber}\n onChange={(e) => setEpisodeNumber(e.target.value)}\n placeholder=\"1\"\n className={fieldClass}\n required\n />\n </div>\n </div>\n <div>\n <label htmlFor=\"episode-name\" className={labelClass}>\n Name *\n </label>\n <input\n id=\"episode-name\"\n type=\"text\"\n value={episodeName}\n onChange={(e) => setEpisodeName(e.target.value)}\n placeholder=\"Pilot\"\n className={fieldClass}\n required\n />\n </div>\n <div>\n <label htmlFor=\"episode-synopsis\" className={labelClass}>\n Synopsis\n </label>\n <textarea\n id=\"episode-synopsis\"\n value={episodeSynopsis}\n onChange={(e) => setEpisodeSynopsis(e.target.value)}\n rows={3}\n className={fieldClass}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"episode-runtime\" className={labelClass}>\n Runtime (minutes)\n </label>\n <input\n id=\"episode-runtime\"\n type=\"number\"\n value={episodeRuntime}\n onChange={(e) => setEpisodeRuntime(e.target.value)}\n placeholder=\"45\"\n className={fieldClass}\n />\n </div>\n <div>\n <label htmlFor=\"episode-airdate\" className={labelClass}>\n Air date\n </label>\n <input\n id=\"episode-airdate\"\n type=\"date\"\n value={episodeAirDate}\n onChange={(e) => setEpisodeAirDate(e.target.value)}\n className={fieldClass}\n />\n </div>\n </div>\n <div className=\"flex items-center gap-3\">\n <button\n type=\"submit\"\n disabled={createEpisode.isPending}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50 transition-opacity\"\n >\n <Plus className=\"h-4 w-4\" />\n {createEpisode.isPending ? 'Adding…' : 'Add episode'}\n </button>\n <button\n type=\"button\"\n onClick={closePanel}\n className=\"rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n Cancel\n </button>\n </div>\n </form>\n )}\n\n {title.episodes.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n {isSeries\n ? 'No episodes yet. Add a season, then episodes, to build out this series.'\n : 'No episodes recorded for this title.'}\n </p>\n ) : (\n <ul className=\"divide-y divide-border-seam\">\n {[...title.episodes]\n .sort((a, b) => a.number - b.number)\n .map((ep) => (\n <li key={ep.id} className=\"flex items-center justify-between py-2\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {ep.number}. {ep.name}\n </p>\n {ep.synopsis && (\n <p className=\"line-clamp-1 text-xs text-text-secondary\">{ep.synopsis}</p>\n )}\n </div>\n {ep.runtimeMinutes && (\n <span className=\"text-xs text-text-secondary\">{ep.runtimeMinutes} min</span>\n )}\n </li>\n ))}\n </ul>\n )}\n </GlassCard>\n\n {/* Reviews */}\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-5\">\n <div className=\"mb-3 flex items-center justify-between\">\n <h2 className=\"flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <Star className=\"h-4 w-4\" />\n Reviews ({reviewItems.length})\n </h2>\n {avgScore !== null && (\n <RatingDisplay value={avgScore / 2} count={reviewItems.length} />\n )}\n </div>\n {reviewItems.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">No reviews yet.</p>\n ) : (\n <ul className=\"space-y-3\">\n {reviewItems.map((review) => (\n <li\n key={review.id}\n className=\"rounded-lg border border-border-seam bg-bg-surface p-3\"\n >\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-semibold text-text-primary\">\n {review.score}/10\n </span>\n <div className=\"flex items-center gap-2\">\n {review.verifiedWatch && (\n <span className=\"rounded-full bg-status-success-bg-subtle px-2 py-0.5 text-[10px] text-status-success-text\">\n Verified\n </span>\n )}\n <StatusBadge status={review.status} />\n </div>\n </div>\n {review.body && (\n <p className=\"mt-1 text-sm text-text-secondary\">{review.body}</p>\n )}\n </li>\n ))}\n </ul>\n )}\n </GlassCard>\n\n {/* Clips */}\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-5\">\n <div className=\"mb-3 flex flex-wrap items-center justify-between gap-3\">\n <h2 className=\"flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <Video className=\"h-4 w-4\" />\n Clips ({clips.data?.totalCount ?? clipItems.length})\n </h2>\n <button\n type=\"button\"\n onClick={() => {\n setClipError(null);\n setShowClipForm((v) => !v);\n }}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-2.5 py-1 text-xs font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <Plus className=\"h-3.5 w-3.5\" />\n Add clip\n </button>\n </div>\n\n {showClipForm && (\n <form\n onSubmit={handleClipSubmit}\n className=\"mb-4 space-y-4 rounded-lg border border-border-seam bg-bg-surface p-4\"\n >\n <div>\n <label htmlFor=\"clip-file\" className={labelClass}>\n Clip video * (short vertical MP4)\n </label>\n <input\n id=\"clip-file\"\n ref={clipInputRef}\n type=\"file\"\n accept=\"video/*\"\n onChange={(e) => setClipFile(e.target.files?.[0] ?? null)}\n className={fieldClass}\n />\n {clipFile && (\n <p className=\"mt-1 truncate text-xs text-text-secondary\">\n Selected: {clipFile.name}\n </p>\n )}\n </div>\n <div>\n <label htmlFor=\"clip-caption\" className={labelClass}>\n Caption\n </label>\n <input\n id=\"clip-caption\"\n type=\"text\"\n value={clipCaption}\n onChange={(e) => setClipCaption(e.target.value)}\n placeholder=\"e.g. Official teaser\"\n className={fieldClass}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"clip-duration\" className={labelClass}>\n Duration (seconds)\n </label>\n <input\n id=\"clip-duration\"\n type=\"number\"\n min={0}\n value={clipDuration}\n onChange={(e) => setClipDuration(e.target.value)}\n placeholder=\"30\"\n className={fieldClass}\n />\n </div>\n <div>\n <label htmlFor=\"clip-language\" className={labelClass}>\n Language\n </label>\n <input\n id=\"clip-language\"\n type=\"text\"\n value={clipLanguage}\n onChange={(e) => setClipLanguage(e.target.value)}\n placeholder=\"en\"\n className={fieldClass}\n />\n </div>\n </div>\n {clipError && <p className=\"text-xs text-status-error-text\">{clipError}</p>}\n <div className=\"flex items-center gap-3\">\n <button\n type=\"submit\"\n disabled={clipUploading}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90 disabled:opacity-50\"\n >\n <Upload className=\"h-4 w-4\" />\n {clipUploading ? 'Uploading…' : 'Add clip'}\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setShowClipForm(false);\n setClipError(null);\n }}\n className=\"rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n </div>\n </form>\n )}\n\n {clips.isLoading ? (\n <LoadingState label=\"Loading clips…\" className=\"min-h-[20vh]\" />\n ) : clips.isError ? (\n <ErrorState\n title=\"Could not load clips\"\n message=\"Something went wrong fetching this title's clips.\"\n onRetry={() => void clips.refetch()}\n className=\"min-h-[20vh]\"\n />\n ) : clipItems.length === 0 ? (\n <EmptyState\n title=\"No clips yet\"\n description=\"Upload a short vertical clip to promote this title.\"\n />\n ) : (\n <>\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4\">\n {clipItems.map((clip) => (\n <ClipCard\n key={clip.id}\n clip={clip}\n deleting={deletingClipId === clip.id}\n onDelete={(c) => void handleClipDelete(c)}\n />\n ))}\n </div>\n <Pagination\n page={clipPage}\n hasMore={clips.data?.pageInfo.hasMore ?? false}\n shownCount={clipItems.length}\n onPageChange={setClipPage}\n />\n </>\n )}\n </GlassCard>\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-6\">\n {/* Watch / entitlement */}\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-5\">\n <h2 className=\"mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <Play className=\"h-4 w-4\" />\n Watch\n </h2>\n {entitlement.isLoading ? (\n <p className=\"text-sm text-text-secondary\">Checking access…</p>\n ) : entitlement.isError ? (\n <div className=\"space-y-2\">\n <p className=\"text-sm text-status-error-text\">Could not check entitlement.</p>\n <button\n type=\"button\"\n onClick={() => void entitlement.refetch()}\n className=\"rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n Retry\n </button>\n </div>\n ) : entitlement.data?.entitled ? (\n <div className=\"space-y-3\">\n {entitlement.data.modes.length > 0 && (\n <div className=\"flex flex-wrap gap-1.5\">\n {entitlement.data.modes.map((mode) => (\n <span\n key={mode}\n className=\"rounded-full bg-status-success-bg-subtle px-2.5 py-0.5 text-xs text-status-success-text\"\n >\n {formatEnumLabel(mode)}\n </span>\n ))}\n </div>\n )}\n {watchedNotice ? (\n <p className=\"flex items-center gap-2 text-sm text-status-success-text\">\n <CheckCircle2 className=\"h-4 w-4\" />\n Marked as watched — see Continue Watching.\n </p>\n ) : (\n <button\n type=\"button\"\n disabled={recordProgress.isPending}\n onClick={() => void handleMarkWatched()}\n className=\"inline-flex w-full items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50 transition-opacity\"\n >\n <Play className=\"h-4 w-4\" />\n {recordProgress.isPending ? 'Saving…' : 'Mark as watched'}\n </button>\n )}\n </div>\n ) : (\n <div className=\"flex items-start gap-2 rounded-lg border border-border-seam bg-bg-sunken px-3 py-2.5\">\n <Lock className=\"mt-0.5 h-4 w-4 shrink-0 text-text-secondary\" />\n <p className=\"text-sm text-text-secondary\">\n You are not entitled to watch this title. Grant an entitlement or add a rights\n window to enable playback.\n </p>\n </div>\n )}\n </GlassCard>\n\n {/* Lifecycle actions */}\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-5\">\n <h2 className=\"mb-3 text-sm font-semibold text-text-primary\">Publication</h2>\n <div className=\"flex flex-wrap gap-2\">\n {NEXT_STATUSES[title.status].map((status) => (\n <button\n key={status}\n type=\"button\"\n disabled={transition.isPending}\n onClick={() => void handleTransition(status)}\n className=\"rounded-lg bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50 transition-opacity\"\n >\n {formatEnumLabel(status)}\n </button>\n ))}\n {NEXT_STATUSES[title.status].length === 0 && (\n <p className=\"text-xs text-text-secondary\">No further transitions available.</p>\n )}\n </div>\n {title.status !== 'ARCHIVED' && (\n <button\n type=\"button\"\n disabled={archive.isPending}\n onClick={() => void handleArchive()}\n className=\"mt-3 inline-flex items-center gap-2 rounded-lg border border-status-error-border px-3 py-1.5 text-xs font-medium text-status-error-text hover:bg-status-error-bg-subtle disabled:opacity-50 transition-colors\"\n >\n <Archive className=\"h-3.5 w-3.5\" />\n Archive Title\n </button>\n )}\n </GlassCard>\n\n {/* Facts */}\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-5\">\n <h2 className=\"mb-3 text-sm font-semibold text-text-primary\">Details</h2>\n <dl className=\"space-y-2\">\n {facts.map((fact) => {\n const Icon = fact.icon;\n return (\n <div key={fact.label} className=\"flex items-center justify-between text-sm\">\n <dt className=\"flex items-center gap-2 text-text-secondary\">\n <Icon className=\"h-3.5 w-3.5\" />\n {fact.label}\n </dt>\n <dd className=\"font-medium text-text-primary\">{fact.value}</dd>\n </div>\n );\n })}\n {title.aiDisclosure && (\n <div className=\"flex items-center gap-2 pt-1 text-sm text-text-secondary\">\n <Sparkles className=\"h-3.5 w-3.5\" />\n Contains AI-generated content\n </div>\n )}\n </dl>\n </GlassCard>\n\n {/* Genres & tags */}\n {(title.genres.length > 0 || title.tags.length > 0) && (\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-5\">\n {title.genres.length > 0 && (\n <>\n <h3 className=\"mb-2 text-xs font-semibold uppercase tracking-wide text-text-secondary\">\n Genres\n </h3>\n <div className=\"mb-3 flex flex-wrap gap-1.5\">\n {title.genres.map((g) => (\n <span\n key={g}\n className=\"rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs text-text-primary\"\n >\n {g}\n </span>\n ))}\n </div>\n </>\n )}\n {title.tags.length > 0 && (\n <>\n <h3 className=\"mb-2 text-xs font-semibold uppercase tracking-wide text-text-secondary\">\n Tags\n </h3>\n <div className=\"flex flex-wrap gap-1.5\">\n {title.tags.map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs text-text-secondary\"\n >\n {tag}\n </span>\n ))}\n </div>\n </>\n )}\n </GlassCard>\n )}\n </div>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
@@ -7,12 +7,12 @@ import { formatEnumLabel as a } from "../../utils/formatters.js";
|
|
|
7
7
|
import { ErrorState as o, LoadingState as s } from "../../components/StateViews.js";
|
|
8
8
|
import { EmptyState as c } from "../../components/EmptyState.js";
|
|
9
9
|
import { Pagination as l } from "../../components/Pagination.js";
|
|
10
|
-
import {
|
|
11
|
-
import { Film as
|
|
12
|
-
import { jsx as
|
|
13
|
-
import { EmphasisPanel as
|
|
10
|
+
import { useEffect as u, useState as d } from "react";
|
|
11
|
+
import { Film as f, Plus as p, Search as m } from "lucide-react";
|
|
12
|
+
import { jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
13
|
+
import { EmphasisPanel as _ } from "@burdenoff/fe-libs/ui";
|
|
14
14
|
//#region src/hookmovies/pages/catalog/TitlesPage.tsx
|
|
15
|
-
var
|
|
15
|
+
var v = [
|
|
16
16
|
"MOVIE",
|
|
17
17
|
"SERIES",
|
|
18
18
|
"SHORT_FILM",
|
|
@@ -24,104 +24,125 @@ var _ = [
|
|
|
24
24
|
"TRAILER",
|
|
25
25
|
"COLLECTION",
|
|
26
26
|
"FAST_CHANNEL"
|
|
27
|
-
],
|
|
27
|
+
], y = [
|
|
28
28
|
"DRAFT",
|
|
29
29
|
"IN_REVIEW",
|
|
30
30
|
"SCHEDULED",
|
|
31
31
|
"PUBLISHED",
|
|
32
32
|
"UNPUBLISHED",
|
|
33
33
|
"ARCHIVED"
|
|
34
|
-
],
|
|
35
|
-
let { navigateTo:
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
], b = 20, x = () => {
|
|
35
|
+
let { navigateTo: x } = e(), [S, C] = d(""), [w, T] = d(""), [E, D] = d(""), [O, k] = d(""), [A, j] = d(0);
|
|
36
|
+
u(() => {
|
|
37
|
+
let e = window.setTimeout(() => T(S.trim()), 300);
|
|
38
|
+
return () => window.clearTimeout(e);
|
|
39
|
+
}, [S]), u(() => {
|
|
40
|
+
j(0);
|
|
41
|
+
}, [w]);
|
|
42
|
+
let { data: M, isLoading: N, isError: P, refetch: F } = i({
|
|
43
|
+
type: E || void 0,
|
|
44
|
+
status: O || void 0
|
|
38
45
|
}, {
|
|
39
|
-
limit:
|
|
40
|
-
offset:
|
|
41
|
-
}),
|
|
42
|
-
return /* @__PURE__ */
|
|
46
|
+
limit: b,
|
|
47
|
+
offset: A * b
|
|
48
|
+
}, w || void 0), I = M?.items ?? [], L = M?.totalCount ?? 0;
|
|
49
|
+
return /* @__PURE__ */ g(n, {
|
|
43
50
|
title: "Catalog",
|
|
44
51
|
description: "Movies, series, anime, documentaries & more",
|
|
45
52
|
purpose: "The single source of truth for everything streamable in this workspace. Filter by type or status, then open a title to manage its credits, episodes, and publication lifecycle.",
|
|
46
|
-
actions: /* @__PURE__ */
|
|
53
|
+
actions: /* @__PURE__ */ g("button", {
|
|
47
54
|
type: "button",
|
|
48
|
-
onClick: () =>
|
|
55
|
+
onClick: () => x("catalog/titles/new"),
|
|
49
56
|
className: "inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 transition-opacity",
|
|
50
|
-
children: [/* @__PURE__ */
|
|
57
|
+
children: [/* @__PURE__ */ h(p, { className: "h-4 w-4" }), "New Title"]
|
|
51
58
|
}),
|
|
52
59
|
children: [
|
|
53
|
-
/* @__PURE__ */
|
|
60
|
+
/* @__PURE__ */ h(_, { children: /* @__PURE__ */ g("div", {
|
|
54
61
|
className: "relative",
|
|
55
|
-
children: [/* @__PURE__ */ m
|
|
56
|
-
type: "
|
|
62
|
+
children: [/* @__PURE__ */ h(m, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary" }), /* @__PURE__ */ h("input", {
|
|
63
|
+
type: "search",
|
|
64
|
+
"aria-label": "Search titles",
|
|
57
65
|
placeholder: "Search titles by name…",
|
|
58
|
-
value:
|
|
59
|
-
onChange: (e) =>
|
|
66
|
+
value: S,
|
|
67
|
+
onChange: (e) => C(e.target.value),
|
|
60
68
|
className: "w-full rounded-lg border border-border-subtle bg-bg-surface py-2.5 pl-10 pr-4 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]"
|
|
61
69
|
})]
|
|
62
70
|
}) }),
|
|
63
|
-
/* @__PURE__ */
|
|
71
|
+
/* @__PURE__ */ g("div", {
|
|
64
72
|
className: "mt-4 flex flex-wrap items-center gap-3",
|
|
65
|
-
children: [
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
children: "
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
73
|
+
children: [
|
|
74
|
+
/* @__PURE__ */ g("select", {
|
|
75
|
+
value: E,
|
|
76
|
+
onChange: (e) => {
|
|
77
|
+
D(e.target.value), j(0);
|
|
78
|
+
},
|
|
79
|
+
className: "rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
|
|
80
|
+
children: [/* @__PURE__ */ h("option", {
|
|
81
|
+
value: "",
|
|
82
|
+
children: "Any Type"
|
|
83
|
+
}), v.map((e) => /* @__PURE__ */ h("option", {
|
|
84
|
+
value: e,
|
|
85
|
+
children: a(e)
|
|
86
|
+
}, e))]
|
|
87
|
+
}),
|
|
88
|
+
/* @__PURE__ */ g("select", {
|
|
89
|
+
value: O,
|
|
90
|
+
onChange: (e) => {
|
|
91
|
+
k(e.target.value), j(0);
|
|
92
|
+
},
|
|
93
|
+
className: "rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
|
|
94
|
+
children: [/* @__PURE__ */ h("option", {
|
|
95
|
+
value: "",
|
|
96
|
+
children: "Any Status"
|
|
97
|
+
}), y.map((e) => /* @__PURE__ */ h("option", {
|
|
98
|
+
value: e,
|
|
99
|
+
children: a(e)
|
|
100
|
+
}, e))]
|
|
101
|
+
}),
|
|
102
|
+
!N && !P && /* @__PURE__ */ g("p", {
|
|
103
|
+
className: "ml-auto text-xs text-text-secondary",
|
|
104
|
+
"aria-live": "polite",
|
|
105
|
+
children: [
|
|
106
|
+
L,
|
|
107
|
+
" ",
|
|
108
|
+
L === 1 ? "title" : "titles",
|
|
109
|
+
w ? ` matching “${w}”` : ""
|
|
110
|
+
]
|
|
111
|
+
})
|
|
112
|
+
]
|
|
92
113
|
}),
|
|
93
|
-
/* @__PURE__ */
|
|
114
|
+
/* @__PURE__ */ g("div", {
|
|
94
115
|
className: "mt-6",
|
|
95
|
-
children: [
|
|
116
|
+
children: [N ? /* @__PURE__ */ h(s, { label: "Loading catalog…" }) : P ? /* @__PURE__ */ h(o, {
|
|
96
117
|
title: "Could not load titles",
|
|
97
118
|
message: "The catalog service did not respond.",
|
|
98
|
-
onRetry: () => void
|
|
99
|
-
}) :
|
|
119
|
+
onRetry: () => void F()
|
|
120
|
+
}) : I.length === 0 ? /* @__PURE__ */ h(c, {
|
|
100
121
|
illustration: "empty-data",
|
|
101
|
-
icon: /* @__PURE__ */
|
|
122
|
+
icon: /* @__PURE__ */ h(f, { className: "h-12 w-12" }),
|
|
102
123
|
title: "No titles found",
|
|
103
|
-
description:
|
|
104
|
-
action: /* @__PURE__ */
|
|
124
|
+
description: w ? `No titles match "${w}".` : "No titles match these filters yet. Create your first title.",
|
|
125
|
+
action: /* @__PURE__ */ h("button", {
|
|
105
126
|
type: "button",
|
|
106
|
-
onClick: () =>
|
|
127
|
+
onClick: () => x("catalog/titles/new"),
|
|
107
128
|
className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 transition-opacity",
|
|
108
129
|
children: "New Title"
|
|
109
130
|
})
|
|
110
|
-
}) : /* @__PURE__ */
|
|
131
|
+
}) : /* @__PURE__ */ h("div", {
|
|
111
132
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
112
|
-
children:
|
|
133
|
+
children: I.map((e) => /* @__PURE__ */ g("button", {
|
|
113
134
|
type: "button",
|
|
114
|
-
onClick: () =>
|
|
135
|
+
onClick: () => x(`catalog/titles/${e.id}`),
|
|
115
136
|
className: t("flex flex-col rounded-lg border border-border-seam bg-bg-surface p-4 text-left", "hover:border-border-strong transition-colors"),
|
|
116
137
|
children: [
|
|
117
|
-
/* @__PURE__ */
|
|
138
|
+
/* @__PURE__ */ g("div", {
|
|
118
139
|
className: "flex items-start justify-between gap-2",
|
|
119
|
-
children: [/* @__PURE__ */
|
|
140
|
+
children: [/* @__PURE__ */ h("h3", {
|
|
120
141
|
className: "line-clamp-2 text-sm font-semibold text-text-primary",
|
|
121
142
|
children: e.title
|
|
122
|
-
}), /* @__PURE__ */
|
|
143
|
+
}), /* @__PURE__ */ h(r, { status: e.status })]
|
|
123
144
|
}),
|
|
124
|
-
/* @__PURE__ */
|
|
145
|
+
/* @__PURE__ */ g("p", {
|
|
125
146
|
className: "mt-1 text-xs text-text-secondary",
|
|
126
147
|
children: [
|
|
127
148
|
a(e.type),
|
|
@@ -129,30 +150,30 @@ var _ = [
|
|
|
129
150
|
e.runtimeMinutes ? ` · ${e.runtimeMinutes} min` : ""
|
|
130
151
|
]
|
|
131
152
|
}),
|
|
132
|
-
e.synopsis && /* @__PURE__ */
|
|
153
|
+
e.synopsis && /* @__PURE__ */ h("p", {
|
|
133
154
|
className: "mt-2 line-clamp-2 text-xs text-text-secondary",
|
|
134
155
|
children: e.synopsis
|
|
135
156
|
}),
|
|
136
|
-
e.genres.length > 0 && /* @__PURE__ */
|
|
157
|
+
e.genres.length > 0 && /* @__PURE__ */ h("div", {
|
|
137
158
|
className: "mt-3 flex flex-wrap gap-1",
|
|
138
|
-
children: e.genres.slice(0, 3).map((e) => /* @__PURE__ */
|
|
159
|
+
children: e.genres.slice(0, 3).map((e) => /* @__PURE__ */ h("span", {
|
|
139
160
|
className: "rounded-full bg-bg-sunken px-2 py-0.5 text-[10px] text-text-secondary",
|
|
140
161
|
children: e
|
|
141
162
|
}, e))
|
|
142
163
|
})
|
|
143
164
|
]
|
|
144
165
|
}, e.id))
|
|
145
|
-
}), !
|
|
146
|
-
page:
|
|
147
|
-
hasMore:
|
|
148
|
-
onPageChange:
|
|
149
|
-
shownCount:
|
|
166
|
+
}), !N && !P && /* @__PURE__ */ h(l, {
|
|
167
|
+
page: A,
|
|
168
|
+
hasMore: M?.pageInfo.hasMore ?? !1,
|
|
169
|
+
onPageChange: j,
|
|
170
|
+
shownCount: I.length
|
|
150
171
|
})]
|
|
151
172
|
})
|
|
152
173
|
]
|
|
153
174
|
});
|
|
154
175
|
};
|
|
155
176
|
//#endregion
|
|
156
|
-
export {
|
|
177
|
+
export { x as TitlesPage };
|
|
157
178
|
|
|
158
179
|
//# sourceMappingURL=TitlesPage.js.map
|