@burdenoff/microfe-hookmovies 2026.705.2 → 2026.706.1
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/hooks/index.js +1 -0
- package/dist/hookmovies/hooks/useFiles.js +66 -0
- package/dist/hookmovies/hooks/useFiles.js.map +1 -0
- package/dist/hookmovies/hooks/useHookMoviesApi.js +118 -32
- 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 +942 -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/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":"RightsWindowsPage.js","names":[],"sources":["../../../../src/hookmovies/pages/distribution/RightsWindowsPage.tsx"],"sourcesContent":["/**\n * Rights Windows (Rights & distribution)\n * @module hookmovies/pages/distribution\n *\n * The availability source of truth. Lists rights windows via\n * `hokListRightsWindows`, creates them with `hokCreateRightsWindow`, and moves\n * them through their lifecycle with `hokTransitionRightsWindow`.\n */\n\nimport { type FC, type FormEvent, useState } from 'react';\nimport { ScrollText, Plus, X, Globe, ShieldCheck } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingState, ErrorState } from '../../components/StateViews';\nimport { Pagination } from '../../components/Pagination';\nimport {\n useRightsWindows,\n useCreateRightsWindow,\n useTransitionRightsWindow,\n useTitles,\n type HokRightsWindowStatus,\n type HokMonetizationMode,\n type CreateRightsWindowInput,\n} from '../../hooks/useHookMoviesApi';\nimport { formatDate, formatEnumLabel } from '../../utils/formatters';\n\nconst STATUS_OPTIONS: HokRightsWindowStatus[] = [\n 'DRAFT',\n 'PENDING',\n 'ACTIVE',\n 'EXPIRED',\n 'REVOKED',\n];\n\nconst MODE_OPTIONS: HokMonetizationMode[] = [\n 'FREE_AVOD',\n 'SVOD',\n 'TVOD_RENT',\n 'TVOD_BUY',\n 'PPV',\n 'FAST',\n];\n\nconst NEXT_STATUSES: Record<HokRightsWindowStatus, HokRightsWindowStatus[]> = {\n DRAFT: ['PENDING', 'ACTIVE'],\n PENDING: ['ACTIVE', 'REVOKED'],\n ACTIVE: ['EXPIRED', 'REVOKED'],\n EXPIRED: [],\n REVOKED: [],\n};\n\nconst PAGE_LIMIT = 20;\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\nexport const RightsWindowsPage: FC = () => {\n const [statusFilter, setStatusFilter] = useState<HokRightsWindowStatus | ''>('');\n const [page, setPage] = useState(0);\n const [showCreate, setShowCreate] = useState(false);\n\n const { data, isLoading, isError, refetch } = useRightsWindows(\n { status: statusFilter || undefined },\n { limit: PAGE_LIMIT, offset: page * PAGE_LIMIT }\n );\n const titles = useTitles({}, { limit: 200, offset: 0 });\n const createWindow = useCreateRightsWindow();\n const transition = useTransitionRightsWindow();\n\n const [titleId, setTitleId] = useState('');\n const [modes, setModes] = useState<HokMonetizationMode[]>(['SVOD']);\n const [territories, setTerritories] = useState('');\n const [exclusive, setExclusive] = useState(false);\n const [currency, setCurrency] = useState('USD');\n const [startsAt, setStartsAt] = useState('');\n const [endsAt, setEndsAt] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [actionError, setActionError] = useState<string | null>(null);\n\n const toggleMode = (mode: HokMonetizationMode) => {\n setModes((prev) => (prev.includes(mode) ? prev.filter((m) => m !== mode) : [...prev, mode]));\n };\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setError(null);\n if (!titleId) {\n setError('Select a title.');\n return;\n }\n if (modes.length === 0) {\n setError('Select at least one monetization mode.');\n return;\n }\n const input: CreateRightsWindowInput = {\n titleId,\n modes,\n territories: territories.trim()\n ? territories\n .split(',')\n .map((t) => t.trim().toUpperCase())\n .filter(Boolean)\n : undefined,\n exclusive,\n currency: currency.trim() || undefined,\n startsAt: startsAt ? new Date(startsAt).toISOString() : undefined,\n endsAt: endsAt ? new Date(endsAt).toISOString() : undefined,\n };\n try {\n await createWindow.mutateAsync(input);\n setTitleId('');\n setModes(['SVOD']);\n setTerritories('');\n setExclusive(false);\n setStartsAt('');\n setEndsAt('');\n setShowCreate(false);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create rights window');\n }\n };\n\n const handleTransition = async (id: string, status: HokRightsWindowStatus) => {\n setActionError(null);\n try {\n await transition.mutateAsync({ id, status });\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Transition failed');\n }\n };\n\n const items = data?.items ?? [];\n\n return (\n <PageLayout\n title=\"Rights & Distribution\"\n description=\"Licensing windows by territory, mode & exclusivity\"\n purpose=\"Rights windows are the source of truth for availability. Define what is licensed where, when, and how it can be monetized — entitlements derive from these windows.\"\n actions={\n <button\n type=\"button\"\n onClick={() => setShowCreate((v) => !v)}\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 transition-opacity\"\n >\n <Plus className=\"h-4 w-4\" />\n New Rights Window\n </button>\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 {showCreate && (\n <GlassCard treatment=\"glass\" className=\"mb-6 border-border-seam p-5\">\n <div className=\"mb-3 flex items-center justify-between\">\n <h2 className=\"text-sm font-semibold text-text-primary\">Create Rights Window</h2>\n <button\n type=\"button\"\n onClick={() => setShowCreate(false)}\n className=\"rounded-md p-1 text-text-secondary hover:text-text-primary\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {error && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {error}\n </div>\n )}\n <div>\n <label htmlFor=\"rights-title\" className={labelClass}>\n Title *\n </label>\n <select\n id=\"rights-title\"\n value={titleId}\n onChange={(e) => setTitleId(e.target.value)}\n className={fieldClass}\n required\n >\n <option value=\"\">Select a title…</option>\n {(titles.data?.items ?? []).map((t) => (\n <option key={t.id} value={t.id}>\n {t.title}\n </option>\n ))}\n </select>\n </div>\n\n <div>\n <span className={labelClass}>Monetization Modes *</span>\n <div className=\"flex flex-wrap gap-2\">\n {MODE_OPTIONS.map((mode) => (\n <button\n key={mode}\n type=\"button\"\n onClick={() => toggleMode(mode)}\n className={\n modes.includes(mode)\n ? 'rounded-full bg-action-primary-bg px-3 py-1 text-xs font-medium text-action-primary-text'\n : 'rounded-full border border-border-seam px-3 py-1 text-xs text-text-secondary hover:bg-bg-sunken transition-colors'\n }\n >\n {formatEnumLabel(mode)}\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"rights-territories\" className={labelClass}>\n Territories (ISO codes, comma-separated)\n </label>\n <input\n id=\"rights-territories\"\n type=\"text\"\n value={territories}\n onChange={(e) => setTerritories(e.target.value)}\n placeholder=\"US, IN, GB\"\n className={fieldClass}\n />\n </div>\n <div>\n <label htmlFor=\"rights-currency\" className={labelClass}>\n Currency\n </label>\n <input\n id=\"rights-currency\"\n type=\"text\"\n value={currency}\n onChange={(e) => setCurrency(e.target.value)}\n placeholder=\"USD\"\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=\"rights-start\" className={labelClass}>\n Starts At\n </label>\n <input\n id=\"rights-start\"\n type=\"date\"\n value={startsAt}\n onChange={(e) => setStartsAt(e.target.value)}\n className={fieldClass}\n />\n </div>\n <div>\n <label htmlFor=\"rights-end\" className={labelClass}>\n Ends At\n </label>\n <input\n id=\"rights-end\"\n type=\"date\"\n value={endsAt}\n onChange={(e) => setEndsAt(e.target.value)}\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={exclusive}\n onChange={(e) => setExclusive(e.target.checked)}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n Exclusive license\n </label>\n\n <button\n type=\"submit\"\n disabled={createWindow.isPending}\n className=\"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 {createWindow.isPending ? 'Creating…' : 'Create Window'}\n </button>\n </form>\n </GlassCard>\n )}\n\n <div className=\"mb-4 flex flex-wrap items-center gap-3\">\n <select\n value={statusFilter}\n onChange={(e) => {\n setStatusFilter(e.target.value as HokRightsWindowStatus | '');\n setPage(0);\n }}\n 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)]\"\n >\n <option value=\"\">Any Status</option>\n {STATUS_OPTIONS.map((s) => (\n <option key={s} value={s}>\n {formatEnumLabel(s)}\n </option>\n ))}\n </select>\n </div>\n\n {isLoading ? (\n <LoadingState label=\"Loading rights windows…\" />\n ) : isError ? (\n <ErrorState onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n <EmptyState\n icon={<ScrollText className=\"h-12 w-12\" />}\n title=\"No rights windows\"\n description=\"Create a rights window to make titles available.\"\n />\n ) : (\n <div className=\"space-y-3\">\n {items.map((win) => (\n <GlassCard key={win.id} treatment=\"metric\" className=\"border-border-seam p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-sm font-semibold text-text-primary\">\n {win.title?.title ?? 'Title'}\n </h3>\n <div className=\"mt-1 flex flex-wrap items-center gap-1.5\">\n {win.modes.map((mode) => (\n <span\n key={mode}\n className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-[10px] text-text-secondary\"\n >\n {formatEnumLabel(mode)}\n </span>\n ))}\n {win.exclusive && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-status-info-bg-subtle px-2 py-0.5 text-[10px] text-status-info-text\">\n <ShieldCheck className=\"h-3 w-3\" />\n Exclusive\n </span>\n )}\n </div>\n </div>\n <StatusBadge status={win.status} />\n </div>\n\n <div className=\"mt-3 flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-text-secondary\">\n {win.territories.length > 0 && (\n <span className=\"inline-flex items-center gap-1\">\n <Globe className=\"h-3 w-3\" />\n {win.territories.join(', ')}\n </span>\n )}\n {win.startsAt && (\n <span>\n {formatDate(win.startsAt, { hour: undefined, minute: undefined })}\n {win.endsAt\n ? ` → ${formatDate(win.endsAt, { hour: undefined, minute: undefined })}`\n : ' onward'}\n </span>\n )}\n </div>\n\n {NEXT_STATUSES[win.status].length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-2\">\n {NEXT_STATUSES[win.status].map((status) => (\n <button\n key={status}\n type=\"button\"\n disabled={transition.isPending}\n onClick={() => void handleTransition(win.id, status)}\n className=\"rounded-lg border border-border-seam px-2.5 py-1 text-xs font-medium text-text-primary hover:bg-bg-sunken disabled:opacity-50 transition-colors\"\n >\n Mark {formatEnumLabel(status)}\n </button>\n ))}\n </div>\n )}\n </GlassCard>\n ))}\n </div>\n )}\n\n {!isLoading && !isError && (\n <Pagination\n page={page}\n hasMore={data?.pageInfo.hasMore ?? false}\n onPageChange={setPage}\n shownCount={items.length}\n />\n )}\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RightsWindowsPage.js","names":[],"sources":["../../../../src/hookmovies/pages/distribution/RightsWindowsPage.tsx"],"sourcesContent":["/**\n * Rights Windows (Rights & distribution)\n * @module hookmovies/pages/distribution\n *\n * The availability source of truth. Lists rights windows via\n * `hokListRightsWindows`, creates them with `hokCreateRightsWindow`, and moves\n * them through their lifecycle with `hokTransitionRightsWindow`.\n */\n\nimport { type FC, type FormEvent, useState } from 'react';\nimport { ScrollText, Plus, X, Globe, ShieldCheck } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingState, ErrorState } from '../../components/StateViews';\nimport { Pagination } from '../../components/Pagination';\nimport {\n useRightsWindows,\n useCreateRightsWindow,\n useTransitionRightsWindow,\n useTitles,\n type HokRightsWindowStatus,\n type HokMonetizationMode,\n type CreateRightsWindowInput,\n} from '../../hooks/useHookMoviesApi';\nimport { formatDate, formatEnumLabel } from '../../utils/formatters';\n\nconst STATUS_OPTIONS: HokRightsWindowStatus[] = [\n 'DRAFT',\n 'PENDING',\n 'ACTIVE',\n 'EXPIRED',\n 'REVOKED',\n];\n\nconst MODE_OPTIONS: HokMonetizationMode[] = [\n 'FREE_AVOD',\n 'SVOD',\n 'TVOD_RENT',\n 'TVOD_BUY',\n 'PPV',\n 'FAST',\n];\n\n// Mirrors the svc RIGHTS_WINDOW_TRANSITIONS state machine\n// (wspace-hookmovies-svc monetization.service.ts). Only valid next statuses\n// for the window's current status are offered as transition actions.\nconst NEXT_STATUSES: Record<HokRightsWindowStatus, HokRightsWindowStatus[]> = {\n DRAFT: ['PENDING', 'REVOKED'],\n PENDING: ['ACTIVE', 'REVOKED'],\n ACTIVE: ['EXPIRED', 'REVOKED'],\n EXPIRED: [],\n REVOKED: [],\n};\n\nconst PAGE_LIMIT = 20;\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\nexport const RightsWindowsPage: FC = () => {\n const [statusFilter, setStatusFilter] = useState<HokRightsWindowStatus | ''>('');\n const [page, setPage] = useState(0);\n const [showCreate, setShowCreate] = useState(false);\n\n const { data, isLoading, isError, refetch } = useRightsWindows(\n { status: statusFilter || undefined },\n { limit: PAGE_LIMIT, offset: page * PAGE_LIMIT }\n );\n const titles = useTitles({}, { limit: 200, offset: 0 });\n const createWindow = useCreateRightsWindow();\n const transition = useTransitionRightsWindow();\n\n const [titleId, setTitleId] = useState('');\n const [modes, setModes] = useState<HokMonetizationMode[]>(['SVOD']);\n const [territories, setTerritories] = useState('');\n const [exclusive, setExclusive] = useState(false);\n const [currency, setCurrency] = useState('USD');\n const [startsAt, setStartsAt] = useState('');\n const [endsAt, setEndsAt] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [actionError, setActionError] = useState<string | null>(null);\n\n const toggleMode = (mode: HokMonetizationMode) => {\n setModes((prev) => (prev.includes(mode) ? prev.filter((m) => m !== mode) : [...prev, mode]));\n };\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setError(null);\n if (!titleId) {\n setError('Select a title.');\n return;\n }\n if (modes.length === 0) {\n setError('Select at least one monetization mode.');\n return;\n }\n const input: CreateRightsWindowInput = {\n titleId,\n modes,\n territories: territories.trim()\n ? territories\n .split(',')\n .map((t) => t.trim().toUpperCase())\n .filter(Boolean)\n : undefined,\n exclusive,\n currency: currency.trim() || undefined,\n startsAt: startsAt ? new Date(startsAt).toISOString() : undefined,\n endsAt: endsAt ? new Date(endsAt).toISOString() : undefined,\n };\n try {\n await createWindow.mutateAsync(input);\n setTitleId('');\n setModes(['SVOD']);\n setTerritories('');\n setExclusive(false);\n setStartsAt('');\n setEndsAt('');\n setShowCreate(false);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create rights window');\n }\n };\n\n const handleTransition = async (id: string, status: HokRightsWindowStatus) => {\n setActionError(null);\n try {\n await transition.mutateAsync({ id, status });\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Transition failed');\n }\n };\n\n const items = data?.items ?? [];\n\n return (\n <PageLayout\n title=\"Rights & Distribution\"\n description=\"Licensing windows by territory, mode & exclusivity\"\n purpose=\"Rights windows are the source of truth for availability. Define what is licensed where, when, and how it can be monetized — entitlements derive from these windows.\"\n actions={\n <button\n type=\"button\"\n onClick={() => setShowCreate((v) => !v)}\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 transition-opacity\"\n >\n <Plus className=\"h-4 w-4\" />\n New Rights Window\n </button>\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 {showCreate && (\n <GlassCard treatment=\"glass\" className=\"mb-6 border-border-seam p-5\">\n <div className=\"mb-3 flex items-center justify-between\">\n <h2 className=\"text-sm font-semibold text-text-primary\">Create Rights Window</h2>\n <button\n type=\"button\"\n onClick={() => setShowCreate(false)}\n className=\"rounded-md p-1 text-text-secondary hover:text-text-primary\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {error && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {error}\n </div>\n )}\n <div>\n <label htmlFor=\"rights-title\" className={labelClass}>\n Title *\n </label>\n <select\n id=\"rights-title\"\n value={titleId}\n onChange={(e) => setTitleId(e.target.value)}\n className={fieldClass}\n required\n >\n <option value=\"\">Select a title…</option>\n {(titles.data?.items ?? []).map((t) => (\n <option key={t.id} value={t.id}>\n {t.title}\n </option>\n ))}\n </select>\n </div>\n\n <div>\n <span className={labelClass}>Monetization Modes *</span>\n <div className=\"flex flex-wrap gap-2\">\n {MODE_OPTIONS.map((mode) => (\n <button\n key={mode}\n type=\"button\"\n onClick={() => toggleMode(mode)}\n className={\n modes.includes(mode)\n ? 'rounded-full bg-action-primary-bg px-3 py-1 text-xs font-medium text-action-primary-text'\n : 'rounded-full border border-border-seam px-3 py-1 text-xs text-text-secondary hover:bg-bg-sunken transition-colors'\n }\n >\n {formatEnumLabel(mode)}\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"rights-territories\" className={labelClass}>\n Territories (ISO codes, comma-separated)\n </label>\n <input\n id=\"rights-territories\"\n type=\"text\"\n value={territories}\n onChange={(e) => setTerritories(e.target.value)}\n placeholder=\"US, IN, GB\"\n className={fieldClass}\n />\n </div>\n <div>\n <label htmlFor=\"rights-currency\" className={labelClass}>\n Currency\n </label>\n <input\n id=\"rights-currency\"\n type=\"text\"\n value={currency}\n onChange={(e) => setCurrency(e.target.value)}\n placeholder=\"USD\"\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=\"rights-start\" className={labelClass}>\n Starts At\n </label>\n <input\n id=\"rights-start\"\n type=\"date\"\n value={startsAt}\n onChange={(e) => setStartsAt(e.target.value)}\n className={fieldClass}\n />\n </div>\n <div>\n <label htmlFor=\"rights-end\" className={labelClass}>\n Ends At\n </label>\n <input\n id=\"rights-end\"\n type=\"date\"\n value={endsAt}\n onChange={(e) => setEndsAt(e.target.value)}\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={exclusive}\n onChange={(e) => setExclusive(e.target.checked)}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n Exclusive license\n </label>\n\n <button\n type=\"submit\"\n disabled={createWindow.isPending}\n className=\"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 {createWindow.isPending ? 'Creating…' : 'Create Window'}\n </button>\n </form>\n </GlassCard>\n )}\n\n <div className=\"mb-4 flex flex-wrap items-center gap-3\">\n <select\n value={statusFilter}\n onChange={(e) => {\n setStatusFilter(e.target.value as HokRightsWindowStatus | '');\n setPage(0);\n }}\n 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)]\"\n >\n <option value=\"\">Any Status</option>\n {STATUS_OPTIONS.map((s) => (\n <option key={s} value={s}>\n {formatEnumLabel(s)}\n </option>\n ))}\n </select>\n </div>\n\n {isLoading ? (\n <LoadingState label=\"Loading rights windows…\" />\n ) : isError ? (\n <ErrorState onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n <EmptyState\n icon={<ScrollText className=\"h-12 w-12\" />}\n title=\"No rights windows\"\n description=\"Create a rights window to make titles available.\"\n />\n ) : (\n <div className=\"space-y-3\">\n {items.map((win) => (\n <GlassCard key={win.id} treatment=\"metric\" className=\"border-border-seam p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-sm font-semibold text-text-primary\">\n {win.title?.title ?? 'Title'}\n </h3>\n <div className=\"mt-1 flex flex-wrap items-center gap-1.5\">\n {win.modes.map((mode) => (\n <span\n key={mode}\n className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-[10px] text-text-secondary\"\n >\n {formatEnumLabel(mode)}\n </span>\n ))}\n {win.exclusive && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-status-info-bg-subtle px-2 py-0.5 text-[10px] text-status-info-text\">\n <ShieldCheck className=\"h-3 w-3\" />\n Exclusive\n </span>\n )}\n </div>\n </div>\n <StatusBadge status={win.status} />\n </div>\n\n <div className=\"mt-3 flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-text-secondary\">\n {win.territories.length > 0 && (\n <span className=\"inline-flex items-center gap-1\">\n <Globe className=\"h-3 w-3\" />\n {win.territories.join(', ')}\n </span>\n )}\n {win.startsAt && (\n <span>\n {formatDate(win.startsAt, { hour: undefined, minute: undefined })}\n {win.endsAt\n ? ` → ${formatDate(win.endsAt, { hour: undefined, minute: undefined })}`\n : ' onward'}\n </span>\n )}\n </div>\n\n {NEXT_STATUSES[win.status].length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-2\">\n {NEXT_STATUSES[win.status].map((status) => (\n <button\n key={status}\n type=\"button\"\n disabled={transition.isPending}\n onClick={() => void handleTransition(win.id, status)}\n className=\"rounded-lg border border-border-seam px-2.5 py-1 text-xs font-medium text-text-primary hover:bg-bg-sunken disabled:opacity-50 transition-colors\"\n >\n Mark {formatEnumLabel(status)}\n </button>\n ))}\n </div>\n )}\n </GlassCard>\n ))}\n </div>\n )}\n\n {!isLoading && !isError && (\n <Pagination\n page={page}\n hasMore={data?.pageInfo.hasMore ?? false}\n onPageChange={setPage}\n shownCount={items.length}\n />\n )}\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1,90 +1,288 @@
|
|
|
1
1
|
import { useHookMovies as e } from "../../context/HookMoviesContext.js";
|
|
2
2
|
import { PageLayout as t } from "../../components/PageLayout.js";
|
|
3
3
|
import { StatusBadge as n } from "../../components/StatusBadge.js";
|
|
4
|
-
import {
|
|
5
|
-
import { formatDate as
|
|
6
|
-
import { ErrorState as
|
|
7
|
-
import { EmptyState as
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
4
|
+
import { useGrantEntitlement as r, useMyEntitlements as i, useRevokeEntitlement as a, useTitles as o } from "../../hooks/useHookMoviesApi.js";
|
|
5
|
+
import { formatDate as s, formatEnumLabel as c } from "../../utils/formatters.js";
|
|
6
|
+
import { ErrorState as l, LoadingState as ee } from "../../components/StateViews.js";
|
|
7
|
+
import { EmptyState as u } from "../../components/EmptyState.js";
|
|
8
|
+
import { useState as d } from "react";
|
|
9
|
+
import { PlayCircle as f, Plus as p, Ticket as m, X as h, XCircle as g } from "lucide-react";
|
|
10
|
+
import { jsx as _, jsxs as v } from "react/jsx-runtime";
|
|
11
|
+
import { GlassCard as y } from "@burdenoff/fe-libs/ui";
|
|
11
12
|
//#region src/hookmovies/pages/library/EntitlementsPage.tsx
|
|
12
|
-
var
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
var b = [
|
|
14
|
+
"FREE_AVOD",
|
|
15
|
+
"SVOD",
|
|
16
|
+
"TVOD_RENT",
|
|
17
|
+
"TVOD_BUY",
|
|
18
|
+
"PPV",
|
|
19
|
+
"FAST"
|
|
20
|
+
], x = [
|
|
21
|
+
"SUBSCRIPTION",
|
|
22
|
+
"PURCHASE",
|
|
23
|
+
"RENTAL",
|
|
24
|
+
"PROMO",
|
|
25
|
+
"FREE"
|
|
26
|
+
], S = "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)]", C = "mb-1.5 block text-xs font-medium text-text-secondary", w = () => {
|
|
27
|
+
let { navigateTo: w, currentUser: T } = e(), { data: E, isLoading: D, isError: O, refetch: k } = i(), A = a(), j = r(), [M, N] = d(!1), [P, F] = d(""), I = o({}, {
|
|
28
|
+
limit: 200,
|
|
29
|
+
offset: 0
|
|
30
|
+
}, P), [L, R] = d(""), [z, B] = d(T?.id ?? ""), [V, H] = d("SVOD"), [U, W] = d("SUBSCRIPTION"), [G, K] = d(""), [q, J] = d(""), [Y, X] = d(null), [Z, Q] = d(null), $ = E ?? [], te = () => {
|
|
31
|
+
R(""), B(T?.id ?? ""), H("SVOD"), W("SUBSCRIPTION"), K(""), J(""), F(""), X(null);
|
|
32
|
+
}, ne = async (e) => {
|
|
33
|
+
if (e.preventDefault(), X(null), !L) {
|
|
34
|
+
X("Select a title.");
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (!z.trim()) {
|
|
38
|
+
X("A user id is required.");
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
let t = {
|
|
42
|
+
titleId: L,
|
|
43
|
+
userId: z.trim(),
|
|
44
|
+
mode: V,
|
|
45
|
+
source: U,
|
|
46
|
+
territory: G.trim() ? G.trim().toUpperCase() : void 0,
|
|
47
|
+
expiresAt: q ? new Date(q).toISOString() : void 0
|
|
48
|
+
};
|
|
49
|
+
try {
|
|
50
|
+
await j.mutateAsync(t), te(), N(!1);
|
|
51
|
+
} catch (e) {
|
|
52
|
+
X(e instanceof Error ? e.message : "Failed to grant entitlement");
|
|
53
|
+
}
|
|
54
|
+
}, re = async (e) => {
|
|
55
|
+
Q(null);
|
|
56
|
+
try {
|
|
57
|
+
await A.mutateAsync(e);
|
|
58
|
+
} catch (e) {
|
|
59
|
+
Q(e instanceof Error ? e.message : "Failed to revoke entitlement");
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
return /* @__PURE__ */ v(t, {
|
|
15
63
|
title: "My Entitlements",
|
|
16
64
|
description: "What you can watch right now",
|
|
17
65
|
purpose: "Your computed right to watch each title — from subscriptions, purchases, rentals, or promos. Entitlements derive from plan and rights windows.",
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
/* @__PURE__ */
|
|
35
|
-
className: "
|
|
36
|
-
children:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
66
|
+
actions: /* @__PURE__ */ v("button", {
|
|
67
|
+
type: "button",
|
|
68
|
+
onClick: () => N((e) => !e),
|
|
69
|
+
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",
|
|
70
|
+
children: [/* @__PURE__ */ _(p, { className: "h-4 w-4" }), "Grant Entitlement"]
|
|
71
|
+
}),
|
|
72
|
+
children: [
|
|
73
|
+
Z && /* @__PURE__ */ _("div", {
|
|
74
|
+
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",
|
|
75
|
+
children: Z
|
|
76
|
+
}),
|
|
77
|
+
M && /* @__PURE__ */ v(y, {
|
|
78
|
+
treatment: "glass",
|
|
79
|
+
className: "mb-6 border-border-seam p-5",
|
|
80
|
+
children: [/* @__PURE__ */ v("div", {
|
|
81
|
+
className: "mb-3 flex items-center justify-between",
|
|
82
|
+
children: [/* @__PURE__ */ _("h2", {
|
|
83
|
+
className: "text-sm font-semibold text-text-primary",
|
|
84
|
+
children: "Grant Entitlement"
|
|
85
|
+
}), /* @__PURE__ */ _("button", {
|
|
86
|
+
type: "button",
|
|
87
|
+
onClick: () => N(!1),
|
|
88
|
+
"aria-label": "Close grant form",
|
|
89
|
+
className: "rounded-md p-1 text-text-secondary hover:text-text-primary",
|
|
90
|
+
children: /* @__PURE__ */ _(h, { className: "h-4 w-4" })
|
|
91
|
+
})]
|
|
92
|
+
}), /* @__PURE__ */ v("form", {
|
|
93
|
+
onSubmit: ne,
|
|
94
|
+
className: "space-y-4",
|
|
95
|
+
children: [
|
|
96
|
+
Y && /* @__PURE__ */ _("div", {
|
|
97
|
+
className: "rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
|
|
98
|
+
children: Y
|
|
99
|
+
}),
|
|
100
|
+
/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
|
|
101
|
+
htmlFor: "grant-title-search",
|
|
102
|
+
className: C,
|
|
103
|
+
children: "Search titles"
|
|
104
|
+
}), /* @__PURE__ */ _("input", {
|
|
105
|
+
id: "grant-title-search",
|
|
106
|
+
type: "text",
|
|
107
|
+
value: P,
|
|
108
|
+
onChange: (e) => F(e.target.value),
|
|
109
|
+
placeholder: "Filter titles by name…",
|
|
110
|
+
className: S
|
|
111
|
+
})] }),
|
|
112
|
+
/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
|
|
113
|
+
htmlFor: "grant-title",
|
|
114
|
+
className: C,
|
|
115
|
+
children: "Title *"
|
|
116
|
+
}), /* @__PURE__ */ v("select", {
|
|
117
|
+
id: "grant-title",
|
|
118
|
+
value: L,
|
|
119
|
+
onChange: (e) => R(e.target.value),
|
|
120
|
+
className: S,
|
|
121
|
+
required: !0,
|
|
122
|
+
children: [/* @__PURE__ */ _("option", {
|
|
123
|
+
value: "",
|
|
124
|
+
children: I.isLoading ? "Loading titles…" : "Select a title…"
|
|
125
|
+
}), (I.data?.items ?? []).map((e) => /* @__PURE__ */ _("option", {
|
|
126
|
+
value: e.id,
|
|
127
|
+
children: e.title
|
|
128
|
+
}, e.id))]
|
|
129
|
+
})] }),
|
|
130
|
+
/* @__PURE__ */ v("div", { children: [
|
|
131
|
+
/* @__PURE__ */ _("label", {
|
|
132
|
+
htmlFor: "grant-user",
|
|
133
|
+
className: C,
|
|
134
|
+
children: "User ID *"
|
|
49
135
|
}),
|
|
50
|
-
/* @__PURE__ */
|
|
51
|
-
|
|
52
|
-
|
|
136
|
+
/* @__PURE__ */ _("input", {
|
|
137
|
+
id: "grant-user",
|
|
138
|
+
type: "text",
|
|
139
|
+
value: z,
|
|
140
|
+
onChange: (e) => B(e.target.value),
|
|
141
|
+
placeholder: "User id to grant access to",
|
|
142
|
+
className: S,
|
|
143
|
+
required: !0
|
|
53
144
|
}),
|
|
54
|
-
|
|
55
|
-
className: "
|
|
56
|
-
children:
|
|
145
|
+
/* @__PURE__ */ _("p", {
|
|
146
|
+
className: "mt-1 text-[11px] text-text-secondary",
|
|
147
|
+
children: "Defaults to your own id. Change it to grant access to another user."
|
|
57
148
|
})
|
|
58
|
-
]
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
149
|
+
] }),
|
|
150
|
+
/* @__PURE__ */ v("div", {
|
|
151
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
152
|
+
children: [/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
|
|
153
|
+
htmlFor: "grant-mode",
|
|
154
|
+
className: C,
|
|
155
|
+
children: "Monetization Mode *"
|
|
156
|
+
}), /* @__PURE__ */ _("select", {
|
|
157
|
+
id: "grant-mode",
|
|
158
|
+
value: V,
|
|
159
|
+
onChange: (e) => H(e.target.value),
|
|
160
|
+
className: S,
|
|
161
|
+
required: !0,
|
|
162
|
+
children: b.map((e) => /* @__PURE__ */ _("option", {
|
|
163
|
+
value: e,
|
|
164
|
+
children: c(e)
|
|
165
|
+
}, e))
|
|
166
|
+
})] }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
|
|
167
|
+
htmlFor: "grant-source",
|
|
168
|
+
className: C,
|
|
169
|
+
children: "Source"
|
|
170
|
+
}), /* @__PURE__ */ _("select", {
|
|
171
|
+
id: "grant-source",
|
|
172
|
+
value: U,
|
|
173
|
+
onChange: (e) => W(e.target.value),
|
|
174
|
+
className: S,
|
|
175
|
+
children: x.map((e) => /* @__PURE__ */ _("option", {
|
|
176
|
+
value: e,
|
|
177
|
+
children: c(e)
|
|
178
|
+
}, e))
|
|
179
|
+
})] })]
|
|
180
|
+
}),
|
|
181
|
+
/* @__PURE__ */ v("div", {
|
|
182
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
183
|
+
children: [/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
|
|
184
|
+
htmlFor: "grant-territory",
|
|
185
|
+
className: C,
|
|
186
|
+
children: "Territory (ISO code, optional)"
|
|
187
|
+
}), /* @__PURE__ */ _("input", {
|
|
188
|
+
id: "grant-territory",
|
|
189
|
+
type: "text",
|
|
190
|
+
value: G,
|
|
191
|
+
onChange: (e) => K(e.target.value),
|
|
192
|
+
placeholder: "US",
|
|
193
|
+
className: S
|
|
194
|
+
})] }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
|
|
195
|
+
htmlFor: "grant-expires",
|
|
196
|
+
className: C,
|
|
197
|
+
children: "Expires At (optional)"
|
|
198
|
+
}), /* @__PURE__ */ _("input", {
|
|
199
|
+
id: "grant-expires",
|
|
200
|
+
type: "date",
|
|
201
|
+
value: q,
|
|
202
|
+
onChange: (e) => J(e.target.value),
|
|
203
|
+
className: S
|
|
204
|
+
})] })]
|
|
205
|
+
}),
|
|
206
|
+
/* @__PURE__ */ _("button", {
|
|
207
|
+
type: "submit",
|
|
208
|
+
disabled: j.isPending,
|
|
209
|
+
className: "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",
|
|
210
|
+
children: j.isPending ? "Granting…" : "Grant Entitlement"
|
|
211
|
+
})
|
|
212
|
+
]
|
|
213
|
+
})]
|
|
214
|
+
}),
|
|
215
|
+
D ? /* @__PURE__ */ _(ee, { label: "Loading entitlements…" }) : O ? /* @__PURE__ */ _(l, { onRetry: () => void k() }) : $.length === 0 ? /* @__PURE__ */ _(u, {
|
|
216
|
+
icon: /* @__PURE__ */ _(m, { className: "h-12 w-12" }),
|
|
217
|
+
title: "No entitlements yet",
|
|
218
|
+
description: "You don't have any active watch rights. Grant an entitlement above, or browse the catalog to find something to watch.",
|
|
219
|
+
action: /* @__PURE__ */ _("button", {
|
|
220
|
+
type: "button",
|
|
221
|
+
onClick: () => w("catalog/titles"),
|
|
222
|
+
className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 transition-opacity",
|
|
223
|
+
children: "Browse Catalog"
|
|
224
|
+
})
|
|
225
|
+
}) : /* @__PURE__ */ _("div", {
|
|
226
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
227
|
+
children: $.map((e) => /* @__PURE__ */ v(y, {
|
|
228
|
+
treatment: "metric",
|
|
229
|
+
className: "flex flex-col border-border-seam p-4",
|
|
230
|
+
children: [
|
|
231
|
+
/* @__PURE__ */ v("div", {
|
|
232
|
+
className: "flex items-start justify-between gap-2",
|
|
233
|
+
children: [/* @__PURE__ */ _("button", {
|
|
234
|
+
type: "button",
|
|
235
|
+
onClick: () => w(`catalog/titles/${e.titleId}`),
|
|
236
|
+
className: "line-clamp-2 text-left text-sm font-semibold text-text-primary hover:text-action-primary-bg transition-colors",
|
|
237
|
+
children: e.title?.title ?? "Title"
|
|
238
|
+
}), /* @__PURE__ */ _(n, { status: e.status })]
|
|
239
|
+
}),
|
|
240
|
+
/* @__PURE__ */ v("div", {
|
|
241
|
+
className: "mt-2 flex flex-wrap items-center gap-1.5 text-xs text-text-secondary",
|
|
242
|
+
children: [
|
|
243
|
+
/* @__PURE__ */ _("span", {
|
|
244
|
+
className: "rounded-full bg-bg-sunken px-2 py-0.5",
|
|
245
|
+
children: c(e.mode)
|
|
246
|
+
}),
|
|
247
|
+
/* @__PURE__ */ _("span", {
|
|
248
|
+
className: "rounded-full bg-bg-sunken px-2 py-0.5",
|
|
249
|
+
children: c(e.source)
|
|
250
|
+
}),
|
|
251
|
+
e.territory && /* @__PURE__ */ _("span", {
|
|
252
|
+
className: "rounded-full bg-bg-sunken px-2 py-0.5",
|
|
253
|
+
children: e.territory
|
|
254
|
+
})
|
|
255
|
+
]
|
|
256
|
+
}),
|
|
257
|
+
e.expiresAt && /* @__PURE__ */ v("p", {
|
|
258
|
+
className: "mt-2 text-xs text-text-secondary",
|
|
259
|
+
children: ["Expires ", s(e.expiresAt, {
|
|
260
|
+
hour: void 0,
|
|
261
|
+
minute: void 0
|
|
262
|
+
})]
|
|
263
|
+
}),
|
|
264
|
+
/* @__PURE__ */ v("div", {
|
|
265
|
+
className: "mt-auto flex items-center gap-2 pt-3",
|
|
266
|
+
children: [/* @__PURE__ */ v("button", {
|
|
267
|
+
type: "button",
|
|
268
|
+
onClick: () => w(`catalog/titles/${e.titleId}`),
|
|
269
|
+
className: "inline-flex flex-1 items-center justify-center gap-1 rounded-lg bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text hover:opacity-90 transition-opacity",
|
|
270
|
+
children: [/* @__PURE__ */ _(f, { className: "h-3.5 w-3.5" }), "Watch"]
|
|
271
|
+
}), e.status === "ACTIVE" && /* @__PURE__ */ v("button", {
|
|
272
|
+
type: "button",
|
|
273
|
+
disabled: A.isPending,
|
|
274
|
+
onClick: () => void re(e.id),
|
|
275
|
+
className: "inline-flex items-center gap-1 rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-secondary hover:text-status-error-text disabled:opacity-50 transition-colors",
|
|
276
|
+
children: [/* @__PURE__ */ _(g, { className: "h-3.5 w-3.5" }), "Revoke"]
|
|
277
|
+
})]
|
|
278
|
+
})
|
|
279
|
+
]
|
|
280
|
+
}, e.id))
|
|
281
|
+
})
|
|
282
|
+
]
|
|
85
283
|
});
|
|
86
284
|
};
|
|
87
285
|
//#endregion
|
|
88
|
-
export {
|
|
286
|
+
export { w as EntitlementsPage };
|
|
89
287
|
|
|
90
288
|
//# sourceMappingURL=EntitlementsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntitlementsPage.js","names":[],"sources":["../../../../src/hookmovies/pages/library/EntitlementsPage.tsx"],"sourcesContent":["/**\n * My Entitlements (Library — self-scoped)\n * @module hookmovies/pages/library\n *\n * The caller's own watch entitlements via `hokMyEntitlements` (self-scoped to\n * the user). Active entitlements can be revoked with `hokRevokeEntitlement`.\n */\n\nimport { type FC } from 'react';\nimport { Ticket, PlayCircle, XCircle } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingState, ErrorState } from '../../components/StateViews';\nimport { useHookMovies } from '../../context/HookMoviesContext';\nimport { useMyEntitlements, useRevokeEntitlement } from '../../hooks/useHookMoviesApi';\nimport { formatDate, formatEnumLabel } from '../../utils/formatters';\n\nexport const EntitlementsPage: FC = () => {\n const { navigateTo } = useHookMovies();\n const { data, isLoading, isError, refetch } = useMyEntitlements();\n const revoke = useRevokeEntitlement();\n\n const items = data ?? [];\n\n return (\n <PageLayout\n title=\"My Entitlements\"\n description=\"What you can watch right now\"\n purpose=\"Your computed right to watch each title — from subscriptions, purchases, rentals, or promos. Entitlements derive from plan and rights windows.\"\n >\n {isLoading ? (\n <LoadingState label=\"Loading entitlements…\" />\n ) : isError ? (\n <ErrorState onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n <EmptyState\n icon={<Ticket className=\"h-12 w-12\" />}\n title=\"No entitlements yet\"\n description=\"You don't have any active watch rights. Browse the catalog to find something to watch.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('catalog/titles')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 transition-opacity\"\n >\n Browse Catalog\n </button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {items.map((ent) => (\n <GlassCard\n key={ent.id}\n treatment=\"metric\"\n className=\"flex flex-col border-border-seam p-4\"\n >\n <div className=\"flex items-start justify-between gap-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo(`catalog/titles/${ent.titleId}`)}\n className=\"line-clamp-2 text-left text-sm font-semibold text-text-primary hover:text-action-primary-bg transition-colors\"\n >\n {ent.title?.title ?? 'Title'}\n </button>\n <StatusBadge status={ent.status} />\n </div>\n <div className=\"mt-2 flex flex-wrap items-center gap-1.5 text-xs text-text-secondary\">\n <span className=\"rounded-full bg-bg-sunken px-2 py-0.5\">\n {formatEnumLabel(ent.mode)}\n </span>\n <span className=\"rounded-full bg-bg-sunken px-2 py-0.5\">\n {formatEnumLabel(ent.source)}\n </span>\n {ent.territory && (\n <span className=\"rounded-full bg-bg-sunken px-2 py-0.5\">{ent.territory}</span>\n )}\n </div>\n {ent.expiresAt && (\n <p className=\"mt-2 text-xs text-text-secondary\">\n Expires {formatDate(ent.expiresAt, { hour: undefined, minute: undefined })}\n </p>\n )}\n <div className=\"mt-auto flex items-center gap-2 pt-3\">\n <button\n type=\"button\"\n onClick={() => navigateTo(`catalog/titles/${ent.titleId}`)}\n className=\"inline-flex flex-1 items-center justify-center gap-1 rounded-lg bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text hover:opacity-90 transition-opacity\"\n >\n <PlayCircle className=\"h-3.5 w-3.5\" />\n Watch\n </button>\n {ent.status === 'ACTIVE' && (\n <button\n type=\"button\"\n disabled={revoke.isPending}\n onClick={() => void revoke.mutateAsync(ent.id)}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-secondary hover:text-status-error-text disabled:opacity-50 transition-colors\"\n >\n <XCircle className=\"h-3.5 w-3.5\" />\n Revoke\n </button>\n )}\n </div>\n </GlassCard>\n ))}\n </div>\n )}\n </PageLayout>\n );\n};\n"],"mappings":";;;;;;;;;;;AAmBA,IAAa,UAA6B;CACxC,IAAM,EAAE,kBAAe,GAAe,EAChC,EAAE,SAAM,cAAW,YAAS,eAAY,GAAmB,EAC3D,IAAS,GAAsB,EAE/B,IAAQ,KAAQ,EAAE;AAExB,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SAAQ;YAEP,IACC,kBAAC,GAAD,EAAc,OAAM,yBAA0B,CAAA,GAC5C,IACF,kBAAC,GAAD,EAAY,eAAe,KAAK,GAAS,EAAI,CAAA,GAC3C,EAAM,WAAW,IACnB,kBAAC,GAAD;GACE,MAAM,kBAAC,GAAD,EAAQ,WAAU,aAAc,CAAA;GACtC,OAAM;GACN,aAAY;GACZ,QACE,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAW,iBAAiB;IAC3C,WAAU;cACX;IAEQ,CAAA;GAEX,CAAA,GAEF,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAM,KAAK,MACV,kBAAC,GAAD;IAEE,WAAU;IACV,WAAU;cAHZ;KAKE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAW,kBAAkB,EAAI,UAAU;OAC1D,WAAU;iBAET,EAAI,OAAO,SAAS;OACd,CAAA,EACT,kBAAC,GAAD,EAAa,QAAQ,EAAI,QAAU,CAAA,CAC/B;;KACN,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAgB,EAAI,KAAK;QACrB,CAAA;OACP,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAgB,EAAI,OAAO;QACvB,CAAA;OACN,EAAI,aACH,kBAAC,QAAD;QAAM,WAAU;kBAAyC,EAAI;QAAiB,CAAA;OAE5E;;KACL,EAAI,aACH,kBAAC,KAAD;MAAG,WAAU;gBAAb,CAAgD,YACrC,EAAW,EAAI,WAAW;OAAE,MAAM,KAAA;OAAW,QAAQ,KAAA;OAAW,CAAC,CACxE;;KAEN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAW,kBAAkB,EAAI,UAAU;OAC1D,WAAU;iBAHZ,CAKE,kBAAC,GAAD,EAAY,WAAU,eAAgB,CAAA,EAAA,QAE/B;UACR,EAAI,WAAW,YACd,kBAAC,UAAD;OACE,MAAK;OACL,UAAU,EAAO;OACjB,eAAe,KAAK,EAAO,YAAY,EAAI,GAAG;OAC9C,WAAU;iBAJZ,CAME,kBAAC,GAAD,EAAS,WAAU,eAAgB,CAAA,EAAA,SAE5B;SAEP;;KACI;MAnDL,EAAI,GAmDC,CACZ;GACE,CAAA;EAEG,CAAA"}
|
|
1
|
+
{"version":3,"file":"EntitlementsPage.js","names":[],"sources":["../../../../src/hookmovies/pages/library/EntitlementsPage.tsx"],"sourcesContent":["/**\n * My Entitlements (Library — self-scoped)\n * @module hookmovies/pages/library\n *\n * The caller's own watch entitlements via `hokMyEntitlements` (self-scoped to\n * the user). Active entitlements can be revoked with `hokRevokeEntitlement`, and\n * new entitlements granted (to self or another user) with `hokGrantEntitlement`.\n */\n\nimport { type FC, type FormEvent, useState } from 'react';\nimport { Ticket, PlayCircle, XCircle, Plus, X } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingState, ErrorState } from '../../components/StateViews';\nimport { useHookMovies } from '../../context/HookMoviesContext';\nimport {\n useMyEntitlements,\n useRevokeEntitlement,\n useGrantEntitlement,\n useTitles,\n type HokMonetizationMode,\n type HokEntitlementSource,\n type HokGrantEntitlementInput,\n} from '../../hooks/useHookMoviesApi';\nimport { formatDate, formatEnumLabel } from '../../utils/formatters';\n\nconst MODE_OPTIONS: HokMonetizationMode[] = [\n 'FREE_AVOD',\n 'SVOD',\n 'TVOD_RENT',\n 'TVOD_BUY',\n 'PPV',\n 'FAST',\n];\n\nconst SOURCE_OPTIONS: HokEntitlementSource[] = [\n 'SUBSCRIPTION',\n 'PURCHASE',\n 'RENTAL',\n 'PROMO',\n 'FREE',\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\nexport const EntitlementsPage: FC = () => {\n const { navigateTo, currentUser } = useHookMovies();\n const { data, isLoading, isError, refetch } = useMyEntitlements();\n const revoke = useRevokeEntitlement();\n const grant = useGrantEntitlement();\n\n const [showGrant, setShowGrant] = useState(false);\n const [titleSearch, setTitleSearch] = useState('');\n const titles = useTitles({}, { limit: 200, offset: 0 }, titleSearch);\n\n const [titleId, setTitleId] = useState('');\n const [userId, setUserId] = useState(currentUser?.id ?? '');\n const [mode, setMode] = useState<HokMonetizationMode>('SVOD');\n const [source, setSource] = useState<HokEntitlementSource>('SUBSCRIPTION');\n const [territory, setTerritory] = useState('');\n const [expiresAt, setExpiresAt] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [actionError, setActionError] = useState<string | null>(null);\n\n const items = data ?? [];\n\n const resetForm = () => {\n setTitleId('');\n setUserId(currentUser?.id ?? '');\n setMode('SVOD');\n setSource('SUBSCRIPTION');\n setTerritory('');\n setExpiresAt('');\n setTitleSearch('');\n setError(null);\n };\n\n const handleGrant = async (e: FormEvent) => {\n e.preventDefault();\n setError(null);\n if (!titleId) {\n setError('Select a title.');\n return;\n }\n if (!userId.trim()) {\n setError('A user id is required.');\n return;\n }\n const input: HokGrantEntitlementInput = {\n titleId,\n userId: userId.trim(),\n mode,\n source,\n territory: territory.trim() ? territory.trim().toUpperCase() : undefined,\n expiresAt: expiresAt ? new Date(expiresAt).toISOString() : undefined,\n };\n try {\n await grant.mutateAsync(input);\n resetForm();\n setShowGrant(false);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to grant entitlement');\n }\n };\n\n const handleRevoke = async (id: string) => {\n setActionError(null);\n try {\n await revoke.mutateAsync(id);\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to revoke entitlement');\n }\n };\n\n return (\n <PageLayout\n title=\"My Entitlements\"\n description=\"What you can watch right now\"\n purpose=\"Your computed right to watch each title — from subscriptions, purchases, rentals, or promos. Entitlements derive from plan and rights windows.\"\n actions={\n <button\n type=\"button\"\n onClick={() => setShowGrant((v) => !v)}\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 transition-opacity\"\n >\n <Plus className=\"h-4 w-4\" />\n Grant Entitlement\n </button>\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 {showGrant && (\n <GlassCard treatment=\"glass\" className=\"mb-6 border-border-seam p-5\">\n <div className=\"mb-3 flex items-center justify-between\">\n <h2 className=\"text-sm font-semibold text-text-primary\">Grant Entitlement</h2>\n <button\n type=\"button\"\n onClick={() => setShowGrant(false)}\n aria-label=\"Close grant form\"\n className=\"rounded-md p-1 text-text-secondary hover:text-text-primary\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n <form onSubmit={handleGrant} className=\"space-y-4\">\n {error && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {error}\n </div>\n )}\n\n <div>\n <label htmlFor=\"grant-title-search\" className={labelClass}>\n Search titles\n </label>\n <input\n id=\"grant-title-search\"\n type=\"text\"\n value={titleSearch}\n onChange={(e) => setTitleSearch(e.target.value)}\n placeholder=\"Filter titles by name…\"\n className={fieldClass}\n />\n </div>\n\n <div>\n <label htmlFor=\"grant-title\" className={labelClass}>\n Title *\n </label>\n <select\n id=\"grant-title\"\n value={titleId}\n onChange={(e) => setTitleId(e.target.value)}\n className={fieldClass}\n required\n >\n <option value=\"\">{titles.isLoading ? 'Loading titles…' : 'Select a title…'}</option>\n {(titles.data?.items ?? []).map((t) => (\n <option key={t.id} value={t.id}>\n {t.title}\n </option>\n ))}\n </select>\n </div>\n\n <div>\n <label htmlFor=\"grant-user\" className={labelClass}>\n User ID *\n </label>\n <input\n id=\"grant-user\"\n type=\"text\"\n value={userId}\n onChange={(e) => setUserId(e.target.value)}\n placeholder=\"User id to grant access to\"\n className={fieldClass}\n required\n />\n <p className=\"mt-1 text-[11px] text-text-secondary\">\n Defaults to your own id. Change it to grant access to another user.\n </p>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"grant-mode\" className={labelClass}>\n Monetization Mode *\n </label>\n <select\n id=\"grant-mode\"\n value={mode}\n onChange={(e) => setMode(e.target.value as HokMonetizationMode)}\n className={fieldClass}\n required\n >\n {MODE_OPTIONS.map((m) => (\n <option key={m} value={m}>\n {formatEnumLabel(m)}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor=\"grant-source\" className={labelClass}>\n Source\n </label>\n <select\n id=\"grant-source\"\n value={source}\n onChange={(e) => setSource(e.target.value as HokEntitlementSource)}\n className={fieldClass}\n >\n {SOURCE_OPTIONS.map((s) => (\n <option key={s} value={s}>\n {formatEnumLabel(s)}\n </option>\n ))}\n </select>\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"grant-territory\" className={labelClass}>\n Territory (ISO code, optional)\n </label>\n <input\n id=\"grant-territory\"\n type=\"text\"\n value={territory}\n onChange={(e) => setTerritory(e.target.value)}\n placeholder=\"US\"\n className={fieldClass}\n />\n </div>\n <div>\n <label htmlFor=\"grant-expires\" className={labelClass}>\n Expires At (optional)\n </label>\n <input\n id=\"grant-expires\"\n type=\"date\"\n value={expiresAt}\n onChange={(e) => setExpiresAt(e.target.value)}\n className={fieldClass}\n />\n </div>\n </div>\n\n <button\n type=\"submit\"\n disabled={grant.isPending}\n className=\"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 {grant.isPending ? 'Granting…' : 'Grant Entitlement'}\n </button>\n </form>\n </GlassCard>\n )}\n\n {isLoading ? (\n <LoadingState label=\"Loading entitlements…\" />\n ) : isError ? (\n <ErrorState onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n <EmptyState\n icon={<Ticket className=\"h-12 w-12\" />}\n title=\"No entitlements yet\"\n description=\"You don't have any active watch rights. Grant an entitlement above, or browse the catalog to find something to watch.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('catalog/titles')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 transition-opacity\"\n >\n Browse Catalog\n </button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {items.map((ent) => (\n <GlassCard\n key={ent.id}\n treatment=\"metric\"\n className=\"flex flex-col border-border-seam p-4\"\n >\n <div className=\"flex items-start justify-between gap-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo(`catalog/titles/${ent.titleId}`)}\n className=\"line-clamp-2 text-left text-sm font-semibold text-text-primary hover:text-action-primary-bg transition-colors\"\n >\n {ent.title?.title ?? 'Title'}\n </button>\n <StatusBadge status={ent.status} />\n </div>\n <div className=\"mt-2 flex flex-wrap items-center gap-1.5 text-xs text-text-secondary\">\n <span className=\"rounded-full bg-bg-sunken px-2 py-0.5\">\n {formatEnumLabel(ent.mode)}\n </span>\n <span className=\"rounded-full bg-bg-sunken px-2 py-0.5\">\n {formatEnumLabel(ent.source)}\n </span>\n {ent.territory && (\n <span className=\"rounded-full bg-bg-sunken px-2 py-0.5\">{ent.territory}</span>\n )}\n </div>\n {ent.expiresAt && (\n <p className=\"mt-2 text-xs text-text-secondary\">\n Expires {formatDate(ent.expiresAt, { hour: undefined, minute: undefined })}\n </p>\n )}\n <div className=\"mt-auto flex items-center gap-2 pt-3\">\n <button\n type=\"button\"\n onClick={() => navigateTo(`catalog/titles/${ent.titleId}`)}\n className=\"inline-flex flex-1 items-center justify-center gap-1 rounded-lg bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text hover:opacity-90 transition-opacity\"\n >\n <PlayCircle className=\"h-3.5 w-3.5\" />\n Watch\n </button>\n {ent.status === 'ACTIVE' && (\n <button\n type=\"button\"\n disabled={revoke.isPending}\n onClick={() => void handleRevoke(ent.id)}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-secondary hover:text-status-error-text disabled:opacity-50 transition-colors\"\n >\n <XCircle className=\"h-3.5 w-3.5\" />\n Revoke\n </button>\n )}\n </div>\n </GlassCard>\n ))}\n </div>\n )}\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|