@burdenoff/microfe-hookmovies 2026.628.1 → 2026.629.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/useHookMoviesApi.js.map +1 -1
- package/dist/hookmovies/pages/HomePage.js.map +1 -1
- package/dist/hookmovies/pages/auditions/AuditionDetailPage.js.map +1 -1
- package/dist/hookmovies/pages/auditions/AuditionsPage.js.map +1 -1
- package/dist/hookmovies/pages/catalog/PeoplePage.js.map +1 -1
- package/dist/hookmovies/pages/catalog/StudiosPage.js.map +1 -1
- package/dist/hookmovies/pages/catalog/TitleDetailPage.js.map +1 -1
- package/dist/hookmovies/pages/catalog/TitlesPage.js.map +1 -1
- package/dist/hookmovies/pages/distribution/RightsWindowsPage.js.map +1 -1
- package/dist/hookmovies/pages/engagement/ReviewsPage.js.map +1 -1
- package/dist/hookmovies/pages/library/EntitlementsPage.js.map +1 -1
- package/dist/hookmovies/pages/productions/ProductionDetailPage.js.map +1 -1
- package/dist/hookmovies/pages/productions/ProductionsPage.js.map +1 -1
- package/dist/hookmovies/utils/formatters.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useHookMoviesApi.js","names":[],"sources":["../../../src/hookmovies/hooks/useHookMoviesApi.ts"],"sourcesContent":["/**\n * HookMovies API hooks\n * @module hookmovies/hooks\n *\n * Real data layer for the HookMovies workspace subgraph (`hookmovies` Hive\n * service / `wspace-hookmovies-svc`). Every hook talks to the live workspace\n * GraphQL gateway via {@link graphqlFetch} with inline operations whose fields\n * mirror `.hive-schema.graphql` exactly. No mocks.\n *\n * Conventions (mirrors microfe-kadaikodi):\n * - `hookmoviesQuery` wraps graphqlFetch({ gateway: 'workspace', workspaceToken: true })\n * and throws on GraphQL errors / missing data.\n * - One react-query `useQuery` per read op and `useMutation` per write op.\n * - Strict hand-written result interfaces — no `any`.\n * - Pagination uses the subgraph's `PaginationInput` (limit/offset/cursor) and\n * `Hok*Connection { items, pageInfo { cursor hasMore } }` shapes verbatim.\n */\n\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\n\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\n\nimport { useHookMovies } from '../context';\n\n// ============================================================================\n// Low-level query runner\n// ============================================================================\n\nconst hookmoviesQuery = async <TData>(\n query: string,\n variables: Record<string, unknown>,\n baseUrl?: string\n): Promise<TData> => {\n const response = await graphqlFetch<TData>({\n gateway: 'workspace',\n baseUrl,\n query,\n variables,\n workspaceToken: true,\n });\n\n if (response.errors?.length) {\n throw new Error(response.errors[0]?.message ?? 'GraphQL request failed');\n }\n\n if (!response.data) {\n throw new Error('GraphQL response did not include data');\n }\n\n return response.data;\n};\n\nconst QK = 'hookmovies' as const;\n\n// ============================================================================\n// Shared scalar / enum types (mirror .hive-schema.graphql)\n// ============================================================================\n\nexport interface HokPaginationInput {\n cursor?: string | null;\n limit?: number | null;\n offset?: number | null;\n}\n\nexport interface HokPageInfo {\n cursor: string | null;\n hasMore: boolean;\n}\n\nexport type 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\nexport type HokTitleStatus =\n | 'DRAFT'\n | 'IN_REVIEW'\n | 'SCHEDULED'\n | 'PUBLISHED'\n | 'UNPUBLISHED'\n | 'ARCHIVED';\n\nexport type HokProductionStatus =\n | 'DEVELOPMENT'\n | 'GREENLIT'\n | 'IN_PRODUCTION'\n | 'POST_PRODUCTION'\n | 'RELEASED'\n | 'SHELVED';\n\nexport type HokAuditionStatus = 'OPEN' | 'REVIEWING' | 'CALLBACKS' | 'CLOSED' | 'CANCELLED';\n\nexport type HokApplicationStatus =\n | 'SUBMITTED'\n | 'SHORTLISTED'\n | 'CALLBACK'\n | 'OFFERED'\n | 'REJECTED'\n | 'WITHDRAWN';\n\nexport type HokCreditRole =\n | 'ACTOR'\n | 'DIRECTOR'\n | 'WRITER'\n | 'PRODUCER'\n | 'CINEMATOGRAPHER'\n | 'EDITOR'\n | 'COMPOSER'\n | 'VOICE_ACTOR'\n | 'CREW';\n\nexport type HokReviewStatus = 'PENDING' | 'PUBLISHED' | 'HIDDEN' | 'REMOVED';\n\nexport type HokRightsWindowStatus = 'DRAFT' | 'PENDING' | 'ACTIVE' | 'EXPIRED' | 'REVOKED';\n\nexport type HokMonetizationMode =\n | 'FREE_AVOD'\n | 'SVOD'\n | 'TVOD_RENT'\n | 'TVOD_BUY'\n | 'PPV'\n | 'FAST';\n\nexport type HokEntitlementSource = 'SUBSCRIPTION' | 'PURCHASE' | 'RENTAL' | 'PROMO' | 'FREE';\n\nexport type HokEntitlementStatus = 'ACTIVE' | 'EXPIRED' | 'REVOKED';\n\nexport type HokListKind = 'WATCHLIST' | 'PLAYLIST';\n\nexport type HokListVisibility = 'PRIVATE' | 'UNLISTED' | 'PUBLIC';\n\n// ============================================================================\n// Domain object types\n// ============================================================================\n\nexport interface HokStudio {\n id: string;\n name: string;\n slug: string | null;\n description: string | null;\n country: string | null;\n websiteUrl: string | null;\n isVerified: boolean;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface HokPerson {\n id: string;\n name: string;\n biography: string | null;\n knownFor: string | null;\n primaryLanguage: string | null;\n portfolioRef: string | null;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface HokCredit {\n id: string;\n personId: string;\n titleId: string;\n role: HokCreditRole;\n characterName: string | null;\n billingOrder: number;\n person: Pick<HokPerson, 'id' | 'name' | 'knownFor'> | null;\n}\n\nexport interface HokEpisodeSummary {\n id: string;\n name: string;\n number: number;\n seasonId: string | null;\n synopsis: string | null;\n runtimeMinutes: number | null;\n airDate: string | null;\n}\n\nexport interface HokSeasonSummary {\n id: string;\n name: string | null;\n number: number;\n synopsis: string | null;\n}\n\nexport interface HokTitle {\n id: string;\n title: string;\n slug: string | null;\n type: HokTitleType;\n status: HokTitleStatus;\n synopsis: string | null;\n genres: string[];\n tags: string[];\n originalLanguage: string | null;\n originCountry: string | null;\n releaseYear: number | null;\n releaseDate: string | null;\n runtimeMinutes: number | null;\n certification: string | null;\n aiDisclosure: boolean;\n studioId: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface HokTitleDetail extends HokTitle {\n studio: Pick<HokStudio, 'id' | 'name'> | null;\n credits: HokCredit[];\n seasons: HokSeasonSummary[];\n episodes: HokEpisodeSummary[];\n}\n\nexport interface HokProduction {\n id: string;\n title: string;\n logline: string | null;\n synopsis: string | null;\n status: HokProductionStatus;\n currency: string;\n budgetMinorUnits: string;\n studioId: string | null;\n producerId: string | null;\n resultingTitleId: string | null;\n artistrybaseProjectRef: string | null;\n tags: string[];\n startDate: string | null;\n endDate: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface HokAuditionSummary {\n id: string;\n roleName: string;\n status: HokAuditionStatus;\n isMinorRole: boolean;\n payMinorUnits: string;\n currency: string;\n location: string | null;\n language: string | null;\n submissionDeadline: string | null;\n}\n\nexport interface HokProductionDetail extends HokProduction {\n auditions: HokAuditionSummary[];\n}\n\nexport interface HokAudition {\n id: string;\n productionId: string;\n roleName: string;\n description: string | null;\n status: HokAuditionStatus;\n isMinorRole: boolean;\n payMinorUnits: string;\n currency: string;\n location: string | null;\n language: string | null;\n submissionDeadline: string | null;\n createdAt: string;\n updatedAt: string;\n production: Pick<HokProduction, 'id' | 'title'> | null;\n}\n\nexport interface HokApplication {\n id: string;\n auditionId: string;\n applicantId: string;\n coverNote: string | null;\n score: number | null;\n status: HokApplicationStatus;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface HokReview {\n id: string;\n titleId: string;\n authorId: string;\n score: number;\n body: string | null;\n hasSpoilers: boolean;\n helpfulCount: number;\n verifiedWatch: boolean;\n status: HokReviewStatus;\n createdAt: string;\n updatedAt: string;\n title: Pick<HokTitle, 'id' | 'title'> | null;\n}\n\nexport interface HokRightsWindow {\n id: string;\n titleId: string;\n status: HokRightsWindowStatus;\n modes: HokMonetizationMode[];\n territories: string[];\n exclusive: boolean;\n currency: string;\n licenseeId: string | null;\n startsAt: string | null;\n endsAt: string | null;\n createdAt: string;\n updatedAt: string;\n title: Pick<HokTitle, 'id' | 'title'> | null;\n}\n\nexport interface HokEntitlement {\n id: string;\n titleId: string;\n userId: string;\n mode: HokMonetizationMode;\n source: HokEntitlementSource;\n status: HokEntitlementStatus;\n territory: string | null;\n expiresAt: string | null;\n createdAt: string;\n updatedAt: string;\n title: Pick<HokTitle, 'id' | 'title'> | null;\n}\n\nexport interface HokList {\n id: string;\n name: string;\n description: string | null;\n kind: HokListKind;\n visibility: HokListVisibility;\n ownerId: string;\n itemCount: number;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface HokWatchProgress {\n id: string;\n titleId: string;\n episodeId: string | null;\n positionSeconds: number;\n completed: boolean;\n lastDevice: string | null;\n updatedAt: string;\n title: Pick<HokTitle, 'id' | 'title' | 'type'> | null;\n}\n\n// ============================================================================\n// Connection wrappers\n// ============================================================================\n\ninterface HokConnection<TItem> {\n items: TItem[];\n pageInfo: HokPageInfo;\n}\n\n// ============================================================================\n// Inline field selections (reused across ops)\n// ============================================================================\n\nconst TITLE_FIELDS = `\n id title slug type status synopsis genres tags originalLanguage originCountry\n releaseYear releaseDate runtimeMinutes certification aiDisclosure studioId\n createdAt updatedAt\n`;\n\nconst STUDIO_FIELDS = `\n id name slug description country websiteUrl isVerified tags createdAt updatedAt\n`;\n\nconst PERSON_FIELDS = `\n id name biography knownFor primaryLanguage portfolioRef tags createdAt updatedAt\n`;\n\nconst PRODUCTION_FIELDS = `\n id title logline synopsis status currency budgetMinorUnits studioId producerId\n resultingTitleId artistrybaseProjectRef tags startDate endDate createdAt updatedAt\n`;\n\nconst AUDITION_FIELDS = `\n id productionId roleName description status isMinorRole payMinorUnits currency\n location language submissionDeadline createdAt updatedAt\n`;\n\nconst REVIEW_FIELDS = `\n id titleId authorId score body hasSpoilers helpfulCount verifiedWatch status\n createdAt updatedAt\n`;\n\nconst RIGHTS_FIELDS = `\n id titleId status modes territories exclusive currency licenseeId startsAt endsAt\n createdAt updatedAt\n`;\n\nconst LIST_FIELDS = `\n id name description kind visibility ownerId itemCount createdAt updatedAt\n`;\n\nconst PAGE_INFO = `pageInfo { cursor hasMore }`;\n\n// ============================================================================\n// Titles\n// ============================================================================\n\nexport interface HokTitleFilters {\n status?: HokTitleStatus;\n type?: HokTitleType;\n studioId?: string;\n}\n\nexport const useTitles = (\n filters: HokTitleFilters = {},\n pagination: HokPaginationInput = { limit: 50, offset: 0 }\n) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'titles', filters, pagination],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokListTitles: HokConnection<HokTitle> }>(\n `query HokTitles($pagination: PaginationInput, $status: HokTitleStatus, $type: HokTitleType, $studioId: ID) {\n hokListTitles(pagination: $pagination, status: $status, type: $type, studioId: $studioId) {\n items { ${TITLE_FIELDS} }\n ${PAGE_INFO}\n }\n }`,\n {\n pagination,\n status: filters.status,\n type: filters.type,\n studioId: filters.studioId,\n },\n apiGatewayUrl\n );\n return result.hokListTitles;\n },\n });\n};\n\nexport const useTitle = (id: string | undefined) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'title', id],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokTitle: HokTitleDetail | null }>(\n `query HokTitle($id: ID!) {\n hokTitle(id: $id) {\n ${TITLE_FIELDS}\n studio { id name }\n credits {\n id personId titleId role characterName billingOrder\n person { id name knownFor }\n }\n seasons { id name number synopsis }\n episodes { id name number seasonId synopsis runtimeMinutes airDate }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.hokTitle;\n },\n enabled: Boolean(id),\n });\n};\n\nexport interface CreateTitleInput {\n title: string;\n type?: HokTitleType;\n synopsis?: string;\n genres?: string[];\n tags?: string[];\n originalLanguage?: string;\n originCountry?: string;\n releaseYear?: number;\n runtimeMinutes?: number;\n certification?: string;\n slug?: string;\n studioId?: string;\n aiDisclosure?: boolean;\n}\n\nexport const useCreateTitle = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (input: CreateTitleInput) => {\n const result = await hookmoviesQuery<{ hokCreateTitle: HokTitle }>(\n `mutation HokCreateTitle($input: HokCreateTitleInput!) {\n hokCreateTitle(input: $input) { ${TITLE_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.hokCreateTitle;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'titles'] });\n },\n });\n};\n\nexport interface UpdateTitleInput {\n title?: string;\n type?: HokTitleType;\n synopsis?: string;\n genres?: string[];\n tags?: string[];\n originalLanguage?: string;\n originCountry?: string;\n releaseYear?: number;\n runtimeMinutes?: number;\n certification?: string;\n slug?: string;\n studioId?: string;\n aiDisclosure?: boolean;\n}\n\nexport const useUpdateTitle = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input: UpdateTitleInput }) => {\n const result = await hookmoviesQuery<{ hokUpdateTitle: HokTitle }>(\n `mutation HokUpdateTitle($id: ID!, $input: HokUpdateTitleInput!) {\n hokUpdateTitle(id: $id, input: $input) { ${TITLE_FIELDS} }\n }`,\n { id, input },\n apiGatewayUrl\n );\n return result.hokUpdateTitle;\n },\n onSuccess: (data) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'titles'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'title', data.id] });\n },\n });\n};\n\nexport const useTransitionTitle = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async ({ id, status }: { id: string; status: HokTitleStatus }) => {\n const result = await hookmoviesQuery<{ hokTransitionTitle: HokTitle }>(\n `mutation HokTransitionTitle($id: ID!, $status: HokTitleStatus!) {\n hokTransitionTitle(id: $id, status: $status) { ${TITLE_FIELDS} }\n }`,\n { id, status },\n apiGatewayUrl\n );\n return result.hokTransitionTitle;\n },\n onSuccess: (data) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'titles'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'title', data.id] });\n },\n });\n};\n\nexport const useArchiveTitle = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await hookmoviesQuery<{ hokArchiveTitle: boolean }>(\n `mutation HokArchiveTitle($id: ID!) { hokArchiveTitle(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return result.hokArchiveTitle;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'titles'] });\n },\n });\n};\n\n// ============================================================================\n// Studios\n// ============================================================================\n\nexport const useStudios = (pagination: HokPaginationInput = { limit: 50, offset: 0 }) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'studios', pagination],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokListStudios: HokConnection<HokStudio> }>(\n `query HokStudios($pagination: PaginationInput) {\n hokListStudios(pagination: $pagination) {\n items { ${STUDIO_FIELDS} }\n ${PAGE_INFO}\n }\n }`,\n { pagination },\n apiGatewayUrl\n );\n return result.hokListStudios;\n },\n });\n};\n\nexport interface CreateStudioInput {\n name: string;\n slug?: string;\n description?: string;\n country?: string;\n websiteUrl?: string;\n tags?: string[];\n}\n\nexport const useCreateStudio = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (input: CreateStudioInput) => {\n const result = await hookmoviesQuery<{ hokCreateStudio: HokStudio }>(\n `mutation HokCreateStudio($input: HokCreateStudioInput!) {\n hokCreateStudio(input: $input) { ${STUDIO_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.hokCreateStudio;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'studios'] });\n },\n });\n};\n\n// ============================================================================\n// People\n// ============================================================================\n\nexport const usePeople = (pagination: HokPaginationInput = { limit: 50, offset: 0 }) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'people', pagination],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokListPeople: HokConnection<HokPerson> }>(\n `query HokPeople($pagination: PaginationInput) {\n hokListPeople(pagination: $pagination) {\n items { ${PERSON_FIELDS} }\n ${PAGE_INFO}\n }\n }`,\n { pagination },\n apiGatewayUrl\n );\n return result.hokListPeople;\n },\n });\n};\n\nexport interface CreatePersonInput {\n name: string;\n biography?: string;\n knownFor?: string;\n primaryLanguage?: string;\n portfolioRef?: string;\n tags?: string[];\n}\n\nexport const useCreatePerson = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (input: CreatePersonInput) => {\n const result = await hookmoviesQuery<{ hokCreatePerson: HokPerson }>(\n `mutation HokCreatePerson($input: HokCreatePersonInput!) {\n hokCreatePerson(input: $input) { ${PERSON_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.hokCreatePerson;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'people'] });\n },\n });\n};\n\n// ============================================================================\n// Productions\n// ============================================================================\n\nexport interface HokProductionFilters {\n status?: HokProductionStatus;\n studioId?: string;\n}\n\nexport const useProductions = (\n filters: HokProductionFilters = {},\n pagination: HokPaginationInput = { limit: 50, offset: 0 }\n) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'productions', filters, pagination],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokListProductions: HokConnection<HokProduction> }>(\n `query HokProductions($pagination: PaginationInput, $status: HokProductionStatus, $studioId: ID) {\n hokListProductions(pagination: $pagination, status: $status, studioId: $studioId) {\n items { ${PRODUCTION_FIELDS} }\n ${PAGE_INFO}\n }\n }`,\n { pagination, status: filters.status, studioId: filters.studioId },\n apiGatewayUrl\n );\n return result.hokListProductions;\n },\n });\n};\n\nexport const useProduction = (id: string | undefined) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'production', id],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokProduction: HokProductionDetail | null }>(\n `query HokProduction($id: ID!) {\n hokProduction(id: $id) {\n ${PRODUCTION_FIELDS}\n auditions {\n id roleName status isMinorRole payMinorUnits currency location language submissionDeadline\n }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.hokProduction;\n },\n enabled: Boolean(id),\n });\n};\n\nexport interface CreateProductionInput {\n title: string;\n logline?: string;\n synopsis?: string;\n currency?: string;\n budgetMinorUnits?: string;\n studioId?: string;\n producerId?: string;\n artistrybaseProjectRef?: string;\n tags?: string[];\n startDate?: string;\n endDate?: string;\n}\n\nexport const useCreateProduction = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (input: CreateProductionInput) => {\n const result = await hookmoviesQuery<{ hokCreateProduction: HokProduction }>(\n `mutation HokCreateProduction($input: HokCreateProductionInput!) {\n hokCreateProduction(input: $input) { ${PRODUCTION_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.hokCreateProduction;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'productions'] });\n },\n });\n};\n\nexport const useTransitionProduction = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async ({ id, status }: { id: string; status: HokProductionStatus }) => {\n const result = await hookmoviesQuery<{ hokTransitionProduction: HokProduction }>(\n `mutation HokTransitionProduction($id: ID!, $status: HokProductionStatus!) {\n hokTransitionProduction(id: $id, status: $status) { ${PRODUCTION_FIELDS} }\n }`,\n { id, status },\n apiGatewayUrl\n );\n return result.hokTransitionProduction;\n },\n onSuccess: (data) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'productions'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'production', data.id] });\n },\n });\n};\n\n// ============================================================================\n// Auditions & Applications\n// ============================================================================\n\nexport interface HokAuditionFilters {\n status?: HokAuditionStatus;\n productionId?: string;\n}\n\nexport const useAuditions = (\n filters: HokAuditionFilters = {},\n pagination: HokPaginationInput = { limit: 50, offset: 0 }\n) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'auditions', filters, pagination],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokListAuditions: HokConnection<HokAudition> }>(\n `query HokAuditions($pagination: PaginationInput, $status: HokAuditionStatus, $productionId: ID) {\n hokListAuditions(pagination: $pagination, status: $status, productionId: $productionId) {\n items {\n ${AUDITION_FIELDS}\n production { id title }\n }\n ${PAGE_INFO}\n }\n }`,\n { pagination, status: filters.status, productionId: filters.productionId },\n apiGatewayUrl\n );\n return result.hokListAuditions;\n },\n });\n};\n\n/**\n * Resolve a single audition by id. The subgraph exposes no `hokAudition(id)`\n * root field, so we read the (workspace-scoped) audition list and pick the row.\n * Fetched with a generous page size since auditions are workspace-bounded.\n */\nexport const useAuditionById = (id: string | undefined) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'audition-by-id', id],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokListAuditions: HokConnection<HokAudition> }>(\n `query HokAuditionLookup($pagination: PaginationInput) {\n hokListAuditions(pagination: $pagination) {\n items {\n ${AUDITION_FIELDS}\n production { id title }\n }\n ${PAGE_INFO}\n }\n }`,\n { pagination: { limit: 200, offset: 0 } },\n apiGatewayUrl\n );\n return result.hokListAuditions.items.find((a) => a.id === id) ?? null;\n },\n enabled: Boolean(id),\n });\n};\n\nexport const useAuditionApplications = (auditionId: string | undefined) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'applications', auditionId],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokListApplications: HokApplication[] }>(\n `query HokApplications($auditionId: ID!) {\n hokListApplications(auditionId: $auditionId) {\n id auditionId applicantId coverNote score status createdAt updatedAt\n }\n }`,\n { auditionId },\n apiGatewayUrl\n );\n return result.hokListApplications;\n },\n enabled: Boolean(auditionId),\n });\n};\n\nexport interface CreateAuditionInput {\n productionId: string;\n roleName: string;\n description?: string;\n payMinorUnits?: string;\n currency?: string;\n location?: string;\n language?: string;\n isMinorRole?: boolean;\n submissionDeadline?: string;\n}\n\nexport const useCreateAudition = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (input: CreateAuditionInput) => {\n const result = await hookmoviesQuery<{ hokCreateAudition: HokAudition }>(\n `mutation HokCreateAudition($input: HokCreateAuditionInput!) {\n hokCreateAudition(input: $input) { ${AUDITION_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.hokCreateAudition;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'auditions'] });\n },\n });\n};\n\nexport const useCloseAudition = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await hookmoviesQuery<{ hokCloseAudition: HokAudition }>(\n `mutation HokCloseAudition($id: ID!) {\n hokCloseAudition(id: $id) { ${AUDITION_FIELDS} }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.hokCloseAudition;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'auditions'] });\n },\n });\n};\n\nexport interface ApplyToAuditionInput {\n auditionId: string;\n coverNote?: string;\n selfTapeFileId?: string;\n}\n\nexport const useApplyToAudition = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (input: ApplyToAuditionInput) => {\n const result = await hookmoviesQuery<{ hokApplyToAudition: HokApplication }>(\n `mutation HokApplyToAudition($input: HokApplyToAuditionInput!) {\n hokApplyToAudition(input: $input) {\n id auditionId applicantId coverNote score status createdAt updatedAt\n }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.hokApplyToAudition;\n },\n onSuccess: (data) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'applications', data.auditionId] });\n },\n });\n};\n\nexport const useScoreApplication = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async ({ id, score }: { id: string; score: number }) => {\n const result = await hookmoviesQuery<{ hokScoreApplication: HokApplication }>(\n `mutation HokScoreApplication($id: ID!, $score: Int!) {\n hokScoreApplication(id: $id, score: $score) {\n id auditionId applicantId coverNote score status createdAt updatedAt\n }\n }`,\n { id, score },\n apiGatewayUrl\n );\n return result.hokScoreApplication;\n },\n onSuccess: (data) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'applications', data.auditionId] });\n },\n });\n};\n\n// ============================================================================\n// Reviews\n// ============================================================================\n\nexport interface HokReviewFilters {\n status?: HokReviewStatus;\n titleId?: string;\n}\n\nexport const useReviews = (\n filters: HokReviewFilters = {},\n pagination: HokPaginationInput = { limit: 50, offset: 0 }\n) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'reviews', filters, pagination],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokListReviews: HokConnection<HokReview> }>(\n `query HokReviews($pagination: PaginationInput, $status: HokReviewStatus, $titleId: ID) {\n hokListReviews(pagination: $pagination, status: $status, titleId: $titleId) {\n items {\n ${REVIEW_FIELDS}\n title { id title }\n }\n ${PAGE_INFO}\n }\n }`,\n { pagination, status: filters.status, titleId: filters.titleId },\n apiGatewayUrl\n );\n return result.hokListReviews;\n },\n });\n};\n\nexport interface CreateReviewInput {\n titleId: string;\n score: number;\n body?: string;\n hasSpoilers?: boolean;\n aspectScores?: Record<string, unknown>;\n}\n\nexport const useCreateReview = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (input: CreateReviewInput) => {\n const result = await hookmoviesQuery<{ hokCreateReview: HokReview }>(\n `mutation HokCreateReview($input: HokCreateReviewInput!) {\n hokCreateReview(input: $input) { ${REVIEW_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.hokCreateReview;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'reviews'] });\n },\n });\n};\n\nexport const useDeleteReview = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await hookmoviesQuery<{ hokDeleteReview: boolean }>(\n `mutation HokDeleteReview($id: ID!) { hokDeleteReview(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return result.hokDeleteReview;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'reviews'] });\n },\n });\n};\n\n// ============================================================================\n// Rights Windows\n// ============================================================================\n\nexport interface HokRightsFilters {\n status?: HokRightsWindowStatus;\n titleId?: string;\n}\n\nexport const useRightsWindows = (\n filters: HokRightsFilters = {},\n pagination: HokPaginationInput = { limit: 50, offset: 0 }\n) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'rights', filters, pagination],\n queryFn: async () => {\n const result = await hookmoviesQuery<{\n hokListRightsWindows: HokConnection<HokRightsWindow>;\n }>(\n `query HokRightsWindows($pagination: PaginationInput, $status: HokRightsWindowStatus, $titleId: ID) {\n hokListRightsWindows(pagination: $pagination, status: $status, titleId: $titleId) {\n items {\n ${RIGHTS_FIELDS}\n title { id title }\n }\n ${PAGE_INFO}\n }\n }`,\n { pagination, status: filters.status, titleId: filters.titleId },\n apiGatewayUrl\n );\n return result.hokListRightsWindows;\n },\n });\n};\n\nexport interface CreateRightsWindowInput {\n titleId: string;\n modes: HokMonetizationMode[];\n territories?: string[];\n exclusive?: boolean;\n currency?: string;\n licenseeId?: string;\n startsAt?: string;\n endsAt?: string;\n contractFileId?: string;\n priceMinorUnits?: Record<string, unknown>;\n}\n\nexport const useCreateRightsWindow = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (input: CreateRightsWindowInput) => {\n const result = await hookmoviesQuery<{ hokCreateRightsWindow: HokRightsWindow }>(\n `mutation HokCreateRightsWindow($input: HokCreateRightsWindowInput!) {\n hokCreateRightsWindow(input: $input) { ${RIGHTS_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.hokCreateRightsWindow;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'rights'] });\n },\n });\n};\n\nexport const useTransitionRightsWindow = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async ({ id, status }: { id: string; status: HokRightsWindowStatus }) => {\n const result = await hookmoviesQuery<{ hokTransitionRightsWindow: HokRightsWindow }>(\n `mutation HokTransitionRightsWindow($id: ID!, $status: HokRightsWindowStatus!) {\n hokTransitionRightsWindow(id: $id, status: $status) { ${RIGHTS_FIELDS} }\n }`,\n { id, status },\n apiGatewayUrl\n );\n return result.hokTransitionRightsWindow;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'rights'] });\n },\n });\n};\n\nexport const useRevokeEntitlement = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await hookmoviesQuery<{ hokRevokeEntitlement: boolean }>(\n `mutation HokRevokeEntitlement($id: ID!) { hokRevokeEntitlement(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return result.hokRevokeEntitlement;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'my-entitlements'] });\n },\n });\n};\n\n// ============================================================================\n// Entitlements (self-scoped)\n// ============================================================================\n\nexport const useMyEntitlements = (titleId?: string) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'my-entitlements', titleId],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokMyEntitlements: HokEntitlement[] }>(\n `query HokMyEntitlements($titleId: ID) {\n hokMyEntitlements(titleId: $titleId) {\n id titleId userId mode source status territory expiresAt createdAt updatedAt\n title { id title }\n }\n }`,\n { titleId },\n apiGatewayUrl\n );\n return result.hokMyEntitlements;\n },\n });\n};\n\n// ============================================================================\n// Lists / Watchlists (self-scoped)\n// ============================================================================\n\nexport const useMyLists = (\n kind?: HokListKind,\n pagination: HokPaginationInput = { limit: 50, offset: 0 }\n) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'my-lists', kind, pagination],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokMyLists: HokConnection<HokList> }>(\n `query HokMyLists($kind: HokListKind, $pagination: PaginationInput) {\n hokMyLists(kind: $kind, pagination: $pagination) {\n items { ${LIST_FIELDS} }\n ${PAGE_INFO}\n }\n }`,\n { kind, pagination },\n apiGatewayUrl\n );\n return result.hokMyLists;\n },\n });\n};\n\nexport interface CreateListInput {\n name: string;\n description?: string;\n kind?: HokListKind;\n visibility?: HokListVisibility;\n items?: Record<string, unknown>;\n}\n\nexport const useCreateList = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (input: CreateListInput) => {\n const result = await hookmoviesQuery<{ hokCreateList: HokList }>(\n `mutation HokCreateList($input: HokCreateListInput!) {\n hokCreateList(input: $input) { ${LIST_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.hokCreateList;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'my-lists'] });\n },\n });\n};\n\nexport const useDeleteList = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await hookmoviesQuery<{ hokDeleteList: boolean }>(\n `mutation HokDeleteList($id: ID!) { hokDeleteList(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return result.hokDeleteList;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'my-lists'] });\n },\n });\n};\n\n// ============================================================================\n// Watch progress / Continue Watching (self-scoped)\n// ============================================================================\n\nexport const useMyWatchProgress = (titleId?: string) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'my-watch-progress', titleId],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokMyWatchProgress: HokWatchProgress[] }>(\n `query HokMyWatchProgress($titleId: ID) {\n hokMyWatchProgress(titleId: $titleId) {\n id titleId episodeId positionSeconds completed lastDevice updatedAt\n title { id title type }\n }\n }`,\n { titleId },\n apiGatewayUrl\n );\n return result.hokMyWatchProgress;\n },\n });\n};\n\n// ============================================================================\n// Health probe (used by the dashboard service-status card)\n// ============================================================================\n\nexport const useHealth = () => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'health'],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokHealth: string }>(\n `query HokHealth { hokHealth }`,\n {},\n apiGatewayUrl\n );\n return result.hokHealth;\n },\n staleTime: 60 * 1000,\n });\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useHookMoviesApi.js","names":[],"sources":["../../../src/hookmovies/hooks/useHookMoviesApi.ts"],"sourcesContent":["/**\n * HookMovies API hooks\n * @module hookmovies/hooks\n *\n * Real data layer for the HookMovies workspace subgraph (`hookmovies` Hive\n * service / `wspace-hookmovies-svc`). Every hook talks to the live workspace\n * GraphQL gateway via {@link graphqlFetch} with inline operations whose fields\n * mirror `.hive-schema.graphql` exactly. No mocks.\n *\n * Conventions (mirrors microfe-kadaikodi):\n * - `hookmoviesQuery` wraps graphqlFetch({ gateway: 'workspace', workspaceToken: true })\n * and throws on GraphQL errors / missing data.\n * - One react-query `useQuery` per read op and `useMutation` per write op.\n * - Strict hand-written result interfaces — no `any`.\n * - Pagination uses the subgraph's `PaginationInput` (limit/offset/cursor) and\n * `Hok*Connection { items, pageInfo { cursor hasMore } }` shapes verbatim.\n */\n\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\n\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\n\nimport { useHookMovies } from '../context';\n\n// ============================================================================\n// Low-level query runner\n// ============================================================================\n\nconst hookmoviesQuery = async <TData>(\n query: string,\n variables: Record<string, unknown>,\n baseUrl?: string\n): Promise<TData> => {\n const response = await graphqlFetch<TData>({\n gateway: 'workspace',\n baseUrl,\n query,\n variables,\n workspaceToken: true,\n });\n\n if (response.errors?.length) {\n throw new Error(response.errors[0]?.message ?? 'GraphQL request failed');\n }\n\n if (!response.data) {\n throw new Error('GraphQL response did not include data');\n }\n\n return response.data;\n};\n\nconst QK = 'hookmovies' as const;\n\n// ============================================================================\n// Shared scalar / enum types (mirror .hive-schema.graphql)\n// ============================================================================\n\nexport interface HokPaginationInput {\n cursor?: string | null;\n limit?: number | null;\n offset?: number | null;\n}\n\nexport interface HokPageInfo {\n cursor: string | null;\n hasMore: boolean;\n}\n\nexport type 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\nexport type HokTitleStatus =\n | 'DRAFT'\n | 'IN_REVIEW'\n | 'SCHEDULED'\n | 'PUBLISHED'\n | 'UNPUBLISHED'\n | 'ARCHIVED';\n\nexport type HokProductionStatus =\n | 'DEVELOPMENT'\n | 'GREENLIT'\n | 'IN_PRODUCTION'\n | 'POST_PRODUCTION'\n | 'RELEASED'\n | 'SHELVED';\n\nexport type HokAuditionStatus = 'OPEN' | 'REVIEWING' | 'CALLBACKS' | 'CLOSED' | 'CANCELLED';\n\nexport type HokApplicationStatus =\n | 'SUBMITTED'\n | 'SHORTLISTED'\n | 'CALLBACK'\n | 'OFFERED'\n | 'REJECTED'\n | 'WITHDRAWN';\n\nexport type HokCreditRole =\n | 'ACTOR'\n | 'DIRECTOR'\n | 'WRITER'\n | 'PRODUCER'\n | 'CINEMATOGRAPHER'\n | 'EDITOR'\n | 'COMPOSER'\n | 'VOICE_ACTOR'\n | 'CREW';\n\nexport type HokReviewStatus = 'PENDING' | 'PUBLISHED' | 'HIDDEN' | 'REMOVED';\n\nexport type HokRightsWindowStatus = 'DRAFT' | 'PENDING' | 'ACTIVE' | 'EXPIRED' | 'REVOKED';\n\nexport type HokMonetizationMode = 'FREE_AVOD' | 'SVOD' | 'TVOD_RENT' | 'TVOD_BUY' | 'PPV' | 'FAST';\n\nexport type HokEntitlementSource = 'SUBSCRIPTION' | 'PURCHASE' | 'RENTAL' | 'PROMO' | 'FREE';\n\nexport type HokEntitlementStatus = 'ACTIVE' | 'EXPIRED' | 'REVOKED';\n\nexport type HokListKind = 'WATCHLIST' | 'PLAYLIST';\n\nexport type HokListVisibility = 'PRIVATE' | 'UNLISTED' | 'PUBLIC';\n\n// ============================================================================\n// Domain object types\n// ============================================================================\n\nexport interface HokStudio {\n id: string;\n name: string;\n slug: string | null;\n description: string | null;\n country: string | null;\n websiteUrl: string | null;\n isVerified: boolean;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface HokPerson {\n id: string;\n name: string;\n biography: string | null;\n knownFor: string | null;\n primaryLanguage: string | null;\n portfolioRef: string | null;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface HokCredit {\n id: string;\n personId: string;\n titleId: string;\n role: HokCreditRole;\n characterName: string | null;\n billingOrder: number;\n person: Pick<HokPerson, 'id' | 'name' | 'knownFor'> | null;\n}\n\nexport interface HokEpisodeSummary {\n id: string;\n name: string;\n number: number;\n seasonId: string | null;\n synopsis: string | null;\n runtimeMinutes: number | null;\n airDate: string | null;\n}\n\nexport interface HokSeasonSummary {\n id: string;\n name: string | null;\n number: number;\n synopsis: string | null;\n}\n\nexport interface HokTitle {\n id: string;\n title: string;\n slug: string | null;\n type: HokTitleType;\n status: HokTitleStatus;\n synopsis: string | null;\n genres: string[];\n tags: string[];\n originalLanguage: string | null;\n originCountry: string | null;\n releaseYear: number | null;\n releaseDate: string | null;\n runtimeMinutes: number | null;\n certification: string | null;\n aiDisclosure: boolean;\n studioId: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface HokTitleDetail extends HokTitle {\n studio: Pick<HokStudio, 'id' | 'name'> | null;\n credits: HokCredit[];\n seasons: HokSeasonSummary[];\n episodes: HokEpisodeSummary[];\n}\n\nexport interface HokProduction {\n id: string;\n title: string;\n logline: string | null;\n synopsis: string | null;\n status: HokProductionStatus;\n currency: string;\n budgetMinorUnits: string;\n studioId: string | null;\n producerId: string | null;\n resultingTitleId: string | null;\n artistrybaseProjectRef: string | null;\n tags: string[];\n startDate: string | null;\n endDate: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface HokAuditionSummary {\n id: string;\n roleName: string;\n status: HokAuditionStatus;\n isMinorRole: boolean;\n payMinorUnits: string;\n currency: string;\n location: string | null;\n language: string | null;\n submissionDeadline: string | null;\n}\n\nexport interface HokProductionDetail extends HokProduction {\n auditions: HokAuditionSummary[];\n}\n\nexport interface HokAudition {\n id: string;\n productionId: string;\n roleName: string;\n description: string | null;\n status: HokAuditionStatus;\n isMinorRole: boolean;\n payMinorUnits: string;\n currency: string;\n location: string | null;\n language: string | null;\n submissionDeadline: string | null;\n createdAt: string;\n updatedAt: string;\n production: Pick<HokProduction, 'id' | 'title'> | null;\n}\n\nexport interface HokApplication {\n id: string;\n auditionId: string;\n applicantId: string;\n coverNote: string | null;\n score: number | null;\n status: HokApplicationStatus;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface HokReview {\n id: string;\n titleId: string;\n authorId: string;\n score: number;\n body: string | null;\n hasSpoilers: boolean;\n helpfulCount: number;\n verifiedWatch: boolean;\n status: HokReviewStatus;\n createdAt: string;\n updatedAt: string;\n title: Pick<HokTitle, 'id' | 'title'> | null;\n}\n\nexport interface HokRightsWindow {\n id: string;\n titleId: string;\n status: HokRightsWindowStatus;\n modes: HokMonetizationMode[];\n territories: string[];\n exclusive: boolean;\n currency: string;\n licenseeId: string | null;\n startsAt: string | null;\n endsAt: string | null;\n createdAt: string;\n updatedAt: string;\n title: Pick<HokTitle, 'id' | 'title'> | null;\n}\n\nexport interface HokEntitlement {\n id: string;\n titleId: string;\n userId: string;\n mode: HokMonetizationMode;\n source: HokEntitlementSource;\n status: HokEntitlementStatus;\n territory: string | null;\n expiresAt: string | null;\n createdAt: string;\n updatedAt: string;\n title: Pick<HokTitle, 'id' | 'title'> | null;\n}\n\nexport interface HokList {\n id: string;\n name: string;\n description: string | null;\n kind: HokListKind;\n visibility: HokListVisibility;\n ownerId: string;\n itemCount: number;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface HokWatchProgress {\n id: string;\n titleId: string;\n episodeId: string | null;\n positionSeconds: number;\n completed: boolean;\n lastDevice: string | null;\n updatedAt: string;\n title: Pick<HokTitle, 'id' | 'title' | 'type'> | null;\n}\n\n// ============================================================================\n// Connection wrappers\n// ============================================================================\n\ninterface HokConnection<TItem> {\n items: TItem[];\n pageInfo: HokPageInfo;\n}\n\n// ============================================================================\n// Inline field selections (reused across ops)\n// ============================================================================\n\nconst TITLE_FIELDS = `\n id title slug type status synopsis genres tags originalLanguage originCountry\n releaseYear releaseDate runtimeMinutes certification aiDisclosure studioId\n createdAt updatedAt\n`;\n\nconst STUDIO_FIELDS = `\n id name slug description country websiteUrl isVerified tags createdAt updatedAt\n`;\n\nconst PERSON_FIELDS = `\n id name biography knownFor primaryLanguage portfolioRef tags createdAt updatedAt\n`;\n\nconst PRODUCTION_FIELDS = `\n id title logline synopsis status currency budgetMinorUnits studioId producerId\n resultingTitleId artistrybaseProjectRef tags startDate endDate createdAt updatedAt\n`;\n\nconst AUDITION_FIELDS = `\n id productionId roleName description status isMinorRole payMinorUnits currency\n location language submissionDeadline createdAt updatedAt\n`;\n\nconst REVIEW_FIELDS = `\n id titleId authorId score body hasSpoilers helpfulCount verifiedWatch status\n createdAt updatedAt\n`;\n\nconst RIGHTS_FIELDS = `\n id titleId status modes territories exclusive currency licenseeId startsAt endsAt\n createdAt updatedAt\n`;\n\nconst LIST_FIELDS = `\n id name description kind visibility ownerId itemCount createdAt updatedAt\n`;\n\nconst PAGE_INFO = `pageInfo { cursor hasMore }`;\n\n// ============================================================================\n// Titles\n// ============================================================================\n\nexport interface HokTitleFilters {\n status?: HokTitleStatus;\n type?: HokTitleType;\n studioId?: string;\n}\n\nexport const useTitles = (\n filters: HokTitleFilters = {},\n pagination: HokPaginationInput = { limit: 50, offset: 0 }\n) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'titles', filters, pagination],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokListTitles: HokConnection<HokTitle> }>(\n `query HokTitles($pagination: PaginationInput, $status: HokTitleStatus, $type: HokTitleType, $studioId: ID) {\n hokListTitles(pagination: $pagination, status: $status, type: $type, studioId: $studioId) {\n items { ${TITLE_FIELDS} }\n ${PAGE_INFO}\n }\n }`,\n {\n pagination,\n status: filters.status,\n type: filters.type,\n studioId: filters.studioId,\n },\n apiGatewayUrl\n );\n return result.hokListTitles;\n },\n });\n};\n\nexport const useTitle = (id: string | undefined) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'title', id],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokTitle: HokTitleDetail | null }>(\n `query HokTitle($id: ID!) {\n hokTitle(id: $id) {\n ${TITLE_FIELDS}\n studio { id name }\n credits {\n id personId titleId role characterName billingOrder\n person { id name knownFor }\n }\n seasons { id name number synopsis }\n episodes { id name number seasonId synopsis runtimeMinutes airDate }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.hokTitle;\n },\n enabled: Boolean(id),\n });\n};\n\nexport interface CreateTitleInput {\n title: string;\n type?: HokTitleType;\n synopsis?: string;\n genres?: string[];\n tags?: string[];\n originalLanguage?: string;\n originCountry?: string;\n releaseYear?: number;\n runtimeMinutes?: number;\n certification?: string;\n slug?: string;\n studioId?: string;\n aiDisclosure?: boolean;\n}\n\nexport const useCreateTitle = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (input: CreateTitleInput) => {\n const result = await hookmoviesQuery<{ hokCreateTitle: HokTitle }>(\n `mutation HokCreateTitle($input: HokCreateTitleInput!) {\n hokCreateTitle(input: $input) { ${TITLE_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.hokCreateTitle;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'titles'] });\n },\n });\n};\n\nexport interface UpdateTitleInput {\n title?: string;\n type?: HokTitleType;\n synopsis?: string;\n genres?: string[];\n tags?: string[];\n originalLanguage?: string;\n originCountry?: string;\n releaseYear?: number;\n runtimeMinutes?: number;\n certification?: string;\n slug?: string;\n studioId?: string;\n aiDisclosure?: boolean;\n}\n\nexport const useUpdateTitle = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input: UpdateTitleInput }) => {\n const result = await hookmoviesQuery<{ hokUpdateTitle: HokTitle }>(\n `mutation HokUpdateTitle($id: ID!, $input: HokUpdateTitleInput!) {\n hokUpdateTitle(id: $id, input: $input) { ${TITLE_FIELDS} }\n }`,\n { id, input },\n apiGatewayUrl\n );\n return result.hokUpdateTitle;\n },\n onSuccess: (data) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'titles'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'title', data.id] });\n },\n });\n};\n\nexport const useTransitionTitle = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async ({ id, status }: { id: string; status: HokTitleStatus }) => {\n const result = await hookmoviesQuery<{ hokTransitionTitle: HokTitle }>(\n `mutation HokTransitionTitle($id: ID!, $status: HokTitleStatus!) {\n hokTransitionTitle(id: $id, status: $status) { ${TITLE_FIELDS} }\n }`,\n { id, status },\n apiGatewayUrl\n );\n return result.hokTransitionTitle;\n },\n onSuccess: (data) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'titles'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'title', data.id] });\n },\n });\n};\n\nexport const useArchiveTitle = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await hookmoviesQuery<{ hokArchiveTitle: boolean }>(\n `mutation HokArchiveTitle($id: ID!) { hokArchiveTitle(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return result.hokArchiveTitle;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'titles'] });\n },\n });\n};\n\n// ============================================================================\n// Studios\n// ============================================================================\n\nexport const useStudios = (pagination: HokPaginationInput = { limit: 50, offset: 0 }) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'studios', pagination],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokListStudios: HokConnection<HokStudio> }>(\n `query HokStudios($pagination: PaginationInput) {\n hokListStudios(pagination: $pagination) {\n items { ${STUDIO_FIELDS} }\n ${PAGE_INFO}\n }\n }`,\n { pagination },\n apiGatewayUrl\n );\n return result.hokListStudios;\n },\n });\n};\n\nexport interface CreateStudioInput {\n name: string;\n slug?: string;\n description?: string;\n country?: string;\n websiteUrl?: string;\n tags?: string[];\n}\n\nexport const useCreateStudio = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (input: CreateStudioInput) => {\n const result = await hookmoviesQuery<{ hokCreateStudio: HokStudio }>(\n `mutation HokCreateStudio($input: HokCreateStudioInput!) {\n hokCreateStudio(input: $input) { ${STUDIO_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.hokCreateStudio;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'studios'] });\n },\n });\n};\n\n// ============================================================================\n// People\n// ============================================================================\n\nexport const usePeople = (pagination: HokPaginationInput = { limit: 50, offset: 0 }) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'people', pagination],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokListPeople: HokConnection<HokPerson> }>(\n `query HokPeople($pagination: PaginationInput) {\n hokListPeople(pagination: $pagination) {\n items { ${PERSON_FIELDS} }\n ${PAGE_INFO}\n }\n }`,\n { pagination },\n apiGatewayUrl\n );\n return result.hokListPeople;\n },\n });\n};\n\nexport interface CreatePersonInput {\n name: string;\n biography?: string;\n knownFor?: string;\n primaryLanguage?: string;\n portfolioRef?: string;\n tags?: string[];\n}\n\nexport const useCreatePerson = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (input: CreatePersonInput) => {\n const result = await hookmoviesQuery<{ hokCreatePerson: HokPerson }>(\n `mutation HokCreatePerson($input: HokCreatePersonInput!) {\n hokCreatePerson(input: $input) { ${PERSON_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.hokCreatePerson;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'people'] });\n },\n });\n};\n\n// ============================================================================\n// Productions\n// ============================================================================\n\nexport interface HokProductionFilters {\n status?: HokProductionStatus;\n studioId?: string;\n}\n\nexport const useProductions = (\n filters: HokProductionFilters = {},\n pagination: HokPaginationInput = { limit: 50, offset: 0 }\n) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'productions', filters, pagination],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokListProductions: HokConnection<HokProduction> }>(\n `query HokProductions($pagination: PaginationInput, $status: HokProductionStatus, $studioId: ID) {\n hokListProductions(pagination: $pagination, status: $status, studioId: $studioId) {\n items { ${PRODUCTION_FIELDS} }\n ${PAGE_INFO}\n }\n }`,\n { pagination, status: filters.status, studioId: filters.studioId },\n apiGatewayUrl\n );\n return result.hokListProductions;\n },\n });\n};\n\nexport const useProduction = (id: string | undefined) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'production', id],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokProduction: HokProductionDetail | null }>(\n `query HokProduction($id: ID!) {\n hokProduction(id: $id) {\n ${PRODUCTION_FIELDS}\n auditions {\n id roleName status isMinorRole payMinorUnits currency location language submissionDeadline\n }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.hokProduction;\n },\n enabled: Boolean(id),\n });\n};\n\nexport interface CreateProductionInput {\n title: string;\n logline?: string;\n synopsis?: string;\n currency?: string;\n budgetMinorUnits?: string;\n studioId?: string;\n producerId?: string;\n artistrybaseProjectRef?: string;\n tags?: string[];\n startDate?: string;\n endDate?: string;\n}\n\nexport const useCreateProduction = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (input: CreateProductionInput) => {\n const result = await hookmoviesQuery<{ hokCreateProduction: HokProduction }>(\n `mutation HokCreateProduction($input: HokCreateProductionInput!) {\n hokCreateProduction(input: $input) { ${PRODUCTION_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.hokCreateProduction;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'productions'] });\n },\n });\n};\n\nexport const useTransitionProduction = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async ({ id, status }: { id: string; status: HokProductionStatus }) => {\n const result = await hookmoviesQuery<{ hokTransitionProduction: HokProduction }>(\n `mutation HokTransitionProduction($id: ID!, $status: HokProductionStatus!) {\n hokTransitionProduction(id: $id, status: $status) { ${PRODUCTION_FIELDS} }\n }`,\n { id, status },\n apiGatewayUrl\n );\n return result.hokTransitionProduction;\n },\n onSuccess: (data) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'productions'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'production', data.id] });\n },\n });\n};\n\n// ============================================================================\n// Auditions & Applications\n// ============================================================================\n\nexport interface HokAuditionFilters {\n status?: HokAuditionStatus;\n productionId?: string;\n}\n\nexport const useAuditions = (\n filters: HokAuditionFilters = {},\n pagination: HokPaginationInput = { limit: 50, offset: 0 }\n) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'auditions', filters, pagination],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokListAuditions: HokConnection<HokAudition> }>(\n `query HokAuditions($pagination: PaginationInput, $status: HokAuditionStatus, $productionId: ID) {\n hokListAuditions(pagination: $pagination, status: $status, productionId: $productionId) {\n items {\n ${AUDITION_FIELDS}\n production { id title }\n }\n ${PAGE_INFO}\n }\n }`,\n { pagination, status: filters.status, productionId: filters.productionId },\n apiGatewayUrl\n );\n return result.hokListAuditions;\n },\n });\n};\n\n/**\n * Resolve a single audition by id. The subgraph exposes no `hokAudition(id)`\n * root field, so we read the (workspace-scoped) audition list and pick the row.\n * Fetched with a generous page size since auditions are workspace-bounded.\n */\nexport const useAuditionById = (id: string | undefined) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'audition-by-id', id],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokListAuditions: HokConnection<HokAudition> }>(\n `query HokAuditionLookup($pagination: PaginationInput) {\n hokListAuditions(pagination: $pagination) {\n items {\n ${AUDITION_FIELDS}\n production { id title }\n }\n ${PAGE_INFO}\n }\n }`,\n { pagination: { limit: 200, offset: 0 } },\n apiGatewayUrl\n );\n return result.hokListAuditions.items.find((a) => a.id === id) ?? null;\n },\n enabled: Boolean(id),\n });\n};\n\nexport const useAuditionApplications = (auditionId: string | undefined) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'applications', auditionId],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokListApplications: HokApplication[] }>(\n `query HokApplications($auditionId: ID!) {\n hokListApplications(auditionId: $auditionId) {\n id auditionId applicantId coverNote score status createdAt updatedAt\n }\n }`,\n { auditionId },\n apiGatewayUrl\n );\n return result.hokListApplications;\n },\n enabled: Boolean(auditionId),\n });\n};\n\nexport interface CreateAuditionInput {\n productionId: string;\n roleName: string;\n description?: string;\n payMinorUnits?: string;\n currency?: string;\n location?: string;\n language?: string;\n isMinorRole?: boolean;\n submissionDeadline?: string;\n}\n\nexport const useCreateAudition = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (input: CreateAuditionInput) => {\n const result = await hookmoviesQuery<{ hokCreateAudition: HokAudition }>(\n `mutation HokCreateAudition($input: HokCreateAuditionInput!) {\n hokCreateAudition(input: $input) { ${AUDITION_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.hokCreateAudition;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'auditions'] });\n },\n });\n};\n\nexport const useCloseAudition = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await hookmoviesQuery<{ hokCloseAudition: HokAudition }>(\n `mutation HokCloseAudition($id: ID!) {\n hokCloseAudition(id: $id) { ${AUDITION_FIELDS} }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.hokCloseAudition;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'auditions'] });\n },\n });\n};\n\nexport interface ApplyToAuditionInput {\n auditionId: string;\n coverNote?: string;\n selfTapeFileId?: string;\n}\n\nexport const useApplyToAudition = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (input: ApplyToAuditionInput) => {\n const result = await hookmoviesQuery<{ hokApplyToAudition: HokApplication }>(\n `mutation HokApplyToAudition($input: HokApplyToAuditionInput!) {\n hokApplyToAudition(input: $input) {\n id auditionId applicantId coverNote score status createdAt updatedAt\n }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.hokApplyToAudition;\n },\n onSuccess: (data) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'applications', data.auditionId] });\n },\n });\n};\n\nexport const useScoreApplication = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async ({ id, score }: { id: string; score: number }) => {\n const result = await hookmoviesQuery<{ hokScoreApplication: HokApplication }>(\n `mutation HokScoreApplication($id: ID!, $score: Int!) {\n hokScoreApplication(id: $id, score: $score) {\n id auditionId applicantId coverNote score status createdAt updatedAt\n }\n }`,\n { id, score },\n apiGatewayUrl\n );\n return result.hokScoreApplication;\n },\n onSuccess: (data) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'applications', data.auditionId] });\n },\n });\n};\n\n// ============================================================================\n// Reviews\n// ============================================================================\n\nexport interface HokReviewFilters {\n status?: HokReviewStatus;\n titleId?: string;\n}\n\nexport const useReviews = (\n filters: HokReviewFilters = {},\n pagination: HokPaginationInput = { limit: 50, offset: 0 }\n) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'reviews', filters, pagination],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokListReviews: HokConnection<HokReview> }>(\n `query HokReviews($pagination: PaginationInput, $status: HokReviewStatus, $titleId: ID) {\n hokListReviews(pagination: $pagination, status: $status, titleId: $titleId) {\n items {\n ${REVIEW_FIELDS}\n title { id title }\n }\n ${PAGE_INFO}\n }\n }`,\n { pagination, status: filters.status, titleId: filters.titleId },\n apiGatewayUrl\n );\n return result.hokListReviews;\n },\n });\n};\n\nexport interface CreateReviewInput {\n titleId: string;\n score: number;\n body?: string;\n hasSpoilers?: boolean;\n aspectScores?: Record<string, unknown>;\n}\n\nexport const useCreateReview = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (input: CreateReviewInput) => {\n const result = await hookmoviesQuery<{ hokCreateReview: HokReview }>(\n `mutation HokCreateReview($input: HokCreateReviewInput!) {\n hokCreateReview(input: $input) { ${REVIEW_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.hokCreateReview;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'reviews'] });\n },\n });\n};\n\nexport const useDeleteReview = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await hookmoviesQuery<{ hokDeleteReview: boolean }>(\n `mutation HokDeleteReview($id: ID!) { hokDeleteReview(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return result.hokDeleteReview;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'reviews'] });\n },\n });\n};\n\n// ============================================================================\n// Rights Windows\n// ============================================================================\n\nexport interface HokRightsFilters {\n status?: HokRightsWindowStatus;\n titleId?: string;\n}\n\nexport const useRightsWindows = (\n filters: HokRightsFilters = {},\n pagination: HokPaginationInput = { limit: 50, offset: 0 }\n) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'rights', filters, pagination],\n queryFn: async () => {\n const result = await hookmoviesQuery<{\n hokListRightsWindows: HokConnection<HokRightsWindow>;\n }>(\n `query HokRightsWindows($pagination: PaginationInput, $status: HokRightsWindowStatus, $titleId: ID) {\n hokListRightsWindows(pagination: $pagination, status: $status, titleId: $titleId) {\n items {\n ${RIGHTS_FIELDS}\n title { id title }\n }\n ${PAGE_INFO}\n }\n }`,\n { pagination, status: filters.status, titleId: filters.titleId },\n apiGatewayUrl\n );\n return result.hokListRightsWindows;\n },\n });\n};\n\nexport interface CreateRightsWindowInput {\n titleId: string;\n modes: HokMonetizationMode[];\n territories?: string[];\n exclusive?: boolean;\n currency?: string;\n licenseeId?: string;\n startsAt?: string;\n endsAt?: string;\n contractFileId?: string;\n priceMinorUnits?: Record<string, unknown>;\n}\n\nexport const useCreateRightsWindow = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (input: CreateRightsWindowInput) => {\n const result = await hookmoviesQuery<{ hokCreateRightsWindow: HokRightsWindow }>(\n `mutation HokCreateRightsWindow($input: HokCreateRightsWindowInput!) {\n hokCreateRightsWindow(input: $input) { ${RIGHTS_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.hokCreateRightsWindow;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'rights'] });\n },\n });\n};\n\nexport const useTransitionRightsWindow = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async ({ id, status }: { id: string; status: HokRightsWindowStatus }) => {\n const result = await hookmoviesQuery<{ hokTransitionRightsWindow: HokRightsWindow }>(\n `mutation HokTransitionRightsWindow($id: ID!, $status: HokRightsWindowStatus!) {\n hokTransitionRightsWindow(id: $id, status: $status) { ${RIGHTS_FIELDS} }\n }`,\n { id, status },\n apiGatewayUrl\n );\n return result.hokTransitionRightsWindow;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'rights'] });\n },\n });\n};\n\nexport const useRevokeEntitlement = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await hookmoviesQuery<{ hokRevokeEntitlement: boolean }>(\n `mutation HokRevokeEntitlement($id: ID!) { hokRevokeEntitlement(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return result.hokRevokeEntitlement;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'my-entitlements'] });\n },\n });\n};\n\n// ============================================================================\n// Entitlements (self-scoped)\n// ============================================================================\n\nexport const useMyEntitlements = (titleId?: string) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'my-entitlements', titleId],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokMyEntitlements: HokEntitlement[] }>(\n `query HokMyEntitlements($titleId: ID) {\n hokMyEntitlements(titleId: $titleId) {\n id titleId userId mode source status territory expiresAt createdAt updatedAt\n title { id title }\n }\n }`,\n { titleId },\n apiGatewayUrl\n );\n return result.hokMyEntitlements;\n },\n });\n};\n\n// ============================================================================\n// Lists / Watchlists (self-scoped)\n// ============================================================================\n\nexport const useMyLists = (\n kind?: HokListKind,\n pagination: HokPaginationInput = { limit: 50, offset: 0 }\n) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'my-lists', kind, pagination],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokMyLists: HokConnection<HokList> }>(\n `query HokMyLists($kind: HokListKind, $pagination: PaginationInput) {\n hokMyLists(kind: $kind, pagination: $pagination) {\n items { ${LIST_FIELDS} }\n ${PAGE_INFO}\n }\n }`,\n { kind, pagination },\n apiGatewayUrl\n );\n return result.hokMyLists;\n },\n });\n};\n\nexport interface CreateListInput {\n name: string;\n description?: string;\n kind?: HokListKind;\n visibility?: HokListVisibility;\n items?: Record<string, unknown>;\n}\n\nexport const useCreateList = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (input: CreateListInput) => {\n const result = await hookmoviesQuery<{ hokCreateList: HokList }>(\n `mutation HokCreateList($input: HokCreateListInput!) {\n hokCreateList(input: $input) { ${LIST_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.hokCreateList;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'my-lists'] });\n },\n });\n};\n\nexport const useDeleteList = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useHookMovies();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await hookmoviesQuery<{ hokDeleteList: boolean }>(\n `mutation HokDeleteList($id: ID!) { hokDeleteList(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return result.hokDeleteList;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'my-lists'] });\n },\n });\n};\n\n// ============================================================================\n// Watch progress / Continue Watching (self-scoped)\n// ============================================================================\n\nexport const useMyWatchProgress = (titleId?: string) => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'my-watch-progress', titleId],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokMyWatchProgress: HokWatchProgress[] }>(\n `query HokMyWatchProgress($titleId: ID) {\n hokMyWatchProgress(titleId: $titleId) {\n id titleId episodeId positionSeconds completed lastDevice updatedAt\n title { id title type }\n }\n }`,\n { titleId },\n apiGatewayUrl\n );\n return result.hokMyWatchProgress;\n },\n });\n};\n\n// ============================================================================\n// Health probe (used by the dashboard service-status card)\n// ============================================================================\n\nexport const useHealth = () => {\n const { apiGatewayUrl } = useHookMovies();\n return useQuery({\n queryKey: [QK, 'health'],\n queryFn: async () => {\n const result = await hookmoviesQuery<{ hokHealth: string }>(\n `query HokHealth { hokHealth }`,\n {},\n apiGatewayUrl\n );\n return result.hokHealth;\n },\n staleTime: 60 * 1000,\n });\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HomePage.js","names":[],"sources":["../../../src/hookmovies/pages/HomePage.tsx"],"sourcesContent":["/**\n * HookMovies Overview / Dashboard\n * @module hookmovies/pages\n *\n * The MFE's primary landing surface. Summarizes the workspace's catalog,\n * production pipeline, casting activity, and audience engagement using the live\n * HookMovies subgraph (counts derived from the real list ops), plus quick\n * links into each module. No mocks.\n */\n\nimport { type FC, useMemo } from 'react';\nimport {\n Film,\n Clapperboard,\n Megaphone,\n Star,\n Building2,\n Users,\n ScrollText,\n PlayCircle,\n ArrowRight,\n Plus,\n} from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { useHookMovies } from '../context/HookMoviesContext';\nimport {\n useTitles,\n useProductions,\n useAuditions,\n useReviews,\n useStudios,\n usePeople,\n useMyWatchProgress,\n} from '../hooks/useHookMoviesApi';\nimport { cn } from '../utils/cn';\nimport { formatEnumLabel, formatRelativeTime } from '../utils/formatters';\n\nconst SMALL_PAGE = { limit: 5, offset: 0 };\nconst COUNT_PAGE = { limit: 50, offset: 0 };\n\nexport const HomePage: FC = () => {\n const { navigateTo, currentUser } = useHookMovies();\n\n const titles = useTitles({}, COUNT_PAGE);\n const productions = useProductions({}, COUNT_PAGE);\n const openAuditions = useAuditions({ status: 'OPEN' }, COUNT_PAGE);\n const reviews = useReviews({}, COUNT_PAGE);\n const studios = useStudios(COUNT_PAGE);\n const people = usePeople(COUNT_PAGE);\n const recentTitles = useTitles({}, SMALL_PAGE);\n const watchProgress = useMyWatchProgress();\n\n const titleCount = titles.data?.items.length ?? 0;\n const productionCount = productions.data?.items.length ?? 0;\n const auditionCount = openAuditions.data?.items.length ?? 0;\n const reviewCount = reviews.data?.items.length ?? 0;\n const studioCount = studios.data?.items.length ?? 0;\n const personCount = people.data?.items.length ?? 0;\n\n const continueWatching = useMemo(\n () => (watchProgress.data ?? []).filter((p) => !p.completed).slice(0, 4),\n [watchProgress.data]\n );\n\n const modules: Array<{\n id: string;\n label: string;\n description: string;\n icon: typeof Film;\n path: string;\n }> = [\n {\n id: 'catalog',\n label: 'Catalog',\n description: 'Movies, series, anime & more',\n icon: Film,\n path: 'catalog/titles',\n },\n {\n id: 'studios',\n label: 'Studios',\n description: 'Studios, channels & networks',\n icon: Building2,\n path: 'catalog/studios',\n },\n {\n id: 'people',\n label: 'People',\n description: 'Cast & crew directory',\n icon: Users,\n path: 'catalog/people',\n },\n {\n id: 'productions',\n label: 'Productions',\n description: 'Development → release pipeline',\n icon: Clapperboard,\n path: 'productions',\n },\n {\n id: 'auditions',\n label: 'Auditions',\n description: 'Casting & talent marketplace',\n icon: Megaphone,\n path: 'auditions',\n },\n {\n id: 'reviews',\n label: 'Reviews',\n description: 'Ratings & audience reviews',\n icon: Star,\n path: 'engagement/reviews',\n },\n {\n id: 'rights',\n label: 'Rights & Distribution',\n description: 'Licensing windows & territories',\n icon: ScrollText,\n path: 'distribution/rights',\n },\n {\n id: 'library',\n label: 'My Library',\n description: 'Watchlists, playlists & history',\n icon: PlayCircle,\n path: 'library/lists',\n },\n ];\n\n return (\n <PageLayout\n hero\n title=\"HookMovies\"\n description={\n currentUser?.name\n ? `Welcome back, ${currentUser.name}`\n : 'The global creative-media network'\n }\n purpose=\"Discover and stream titles, run productions and casting, and manage rights & distribution — your workspace's whole creative-media operation in one place.\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('catalog/titles/new')}\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 Title\n </button>\n }\n >\n {/* Key metrics */}\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-3 xl:grid-cols-6\">\n <MetricsCard\n featured\n icon={<Film className=\"h-5 w-5\" />}\n value={titleCount}\n label=\"Titles\"\n />\n <MetricsCard\n icon={<Clapperboard className=\"h-5 w-5\" />}\n value={productionCount}\n label=\"Productions\"\n />\n <MetricsCard\n icon={<Megaphone className=\"h-5 w-5\" />}\n value={auditionCount}\n label=\"Open Auditions\"\n />\n <MetricsCard icon={<Star className=\"h-5 w-5\" />} value={reviewCount} label=\"Reviews\" />\n <MetricsCard\n icon={<Building2 className=\"h-5 w-5\" />}\n value={studioCount}\n label=\"Studios\"\n />\n <MetricsCard icon={<Users className=\"h-5 w-5\" />} value={personCount} label=\"People\" />\n </div>\n\n {/* Continue watching */}\n {continueWatching.length > 0 && (\n <section className=\"mt-8\">\n <h2 className=\"mb-3 text-lg font-semibold text-text-primary\">Continue Watching</h2>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n {continueWatching.map((entry) => (\n <button\n key={entry.id}\n type=\"button\"\n onClick={() => entry.title && navigateTo(`catalog/titles/${entry.title.id}`)}\n className=\"text-left\"\n >\n <GlassCard treatment=\"metric\" className=\"h-full border-border-seam p-4\">\n <div className=\"flex items-center gap-2 text-action-primary-bg\">\n <PlayCircle className=\"h-5 w-5\" />\n {entry.title && (\n <span className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">\n {formatEnumLabel(entry.title.type)}\n </span>\n )}\n </div>\n <p className=\"mt-2 line-clamp-1 text-sm font-semibold text-text-primary\">\n {entry.title?.title ?? 'Untitled'}\n </p>\n <p className=\"mt-1 text-xs text-text-secondary\">\n {Math.round(entry.positionSeconds / 60)} min in ·{' '}\n {formatRelativeTime(entry.updatedAt)}\n </p>\n </GlassCard>\n </button>\n ))}\n </div>\n </section>\n )}\n\n {/* Modules */}\n <section className=\"mt-8\">\n <h2 className=\"mb-3 text-lg font-semibold text-text-primary\">Modules</h2>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n {modules.map((mod) => {\n const Icon = mod.icon;\n return (\n <button\n key={mod.id}\n type=\"button\"\n onClick={() => navigateTo(mod.path)}\n className=\"group text-left\"\n >\n <GlassCard\n treatment=\"metric\"\n className=\"flex h-full flex-col border-border-seam p-5\"\n >\n <div className=\"flex items-center justify-between\">\n <span className=\"inline-flex h-10 w-10 items-center justify-center rounded-lg bg-bg-sunken text-action-primary-bg\">\n <Icon className=\"h-5 w-5\" />\n </span>\n <ArrowRight className=\"h-4 w-4 text-text-secondary transition-colors group-hover:text-text-primary\" />\n </div>\n <p className=\"mt-3 text-sm font-semibold text-text-primary\">{mod.label}</p>\n <p className=\"mt-1 text-xs text-text-secondary\">{mod.description}</p>\n </GlassCard>\n </button>\n );\n })}\n </div>\n </section>\n\n {/* Recently added titles */}\n <section className=\"mt-8\">\n <div className=\"mb-3 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Recently Added</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('catalog/titles')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-action-primary-bg hover:opacity-80\"\n >\n View all\n <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <ul className=\"divide-y divide-border-seam\">\n {(recentTitles.data?.items ?? []).map((title) => (\n <li key={title.id}>\n <button\n type=\"button\"\n onClick={() => navigateTo(`catalog/titles/${title.id}`)}\n className={cn(\n 'flex w-full items-center justify-between gap-3 px-4 py-3 text-left',\n 'hover:bg-bg-sunken transition-colors'\n )}\n >\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{title.title}</p>\n <p className=\"text-xs text-text-secondary\">\n {formatEnumLabel(title.type)}\n {title.releaseYear ? ` · ${title.releaseYear}` : ''}\n {title.originalLanguage ? ` · ${title.originalLanguage}` : ''}\n </p>\n </div>\n <StatusBadge status={title.status} />\n </button>\n </li>\n ))}\n {recentTitles.data && recentTitles.data.items.length === 0 && (\n <li className=\"px-4 py-6 text-center text-sm text-text-secondary\">\n No titles yet — create your first title to get started.\n </li>\n )}\n </ul>\n </GlassCard>\n </section>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"HomePage.js","names":[],"sources":["../../../src/hookmovies/pages/HomePage.tsx"],"sourcesContent":["/**\n * HookMovies Overview / Dashboard\n * @module hookmovies/pages\n *\n * The MFE's primary landing surface. Summarizes the workspace's catalog,\n * production pipeline, casting activity, and audience engagement using the live\n * HookMovies subgraph (counts derived from the real list ops), plus quick\n * links into each module. No mocks.\n */\n\nimport { type FC, useMemo } from 'react';\nimport {\n Film,\n Clapperboard,\n Megaphone,\n Star,\n Building2,\n Users,\n ScrollText,\n PlayCircle,\n ArrowRight,\n Plus,\n} from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { useHookMovies } from '../context/HookMoviesContext';\nimport {\n useTitles,\n useProductions,\n useAuditions,\n useReviews,\n useStudios,\n usePeople,\n useMyWatchProgress,\n} from '../hooks/useHookMoviesApi';\nimport { cn } from '../utils/cn';\nimport { formatEnumLabel, formatRelativeTime } from '../utils/formatters';\n\nconst SMALL_PAGE = { limit: 5, offset: 0 };\nconst COUNT_PAGE = { limit: 50, offset: 0 };\n\nexport const HomePage: FC = () => {\n const { navigateTo, currentUser } = useHookMovies();\n\n const titles = useTitles({}, COUNT_PAGE);\n const productions = useProductions({}, COUNT_PAGE);\n const openAuditions = useAuditions({ status: 'OPEN' }, COUNT_PAGE);\n const reviews = useReviews({}, COUNT_PAGE);\n const studios = useStudios(COUNT_PAGE);\n const people = usePeople(COUNT_PAGE);\n const recentTitles = useTitles({}, SMALL_PAGE);\n const watchProgress = useMyWatchProgress();\n\n const titleCount = titles.data?.items.length ?? 0;\n const productionCount = productions.data?.items.length ?? 0;\n const auditionCount = openAuditions.data?.items.length ?? 0;\n const reviewCount = reviews.data?.items.length ?? 0;\n const studioCount = studios.data?.items.length ?? 0;\n const personCount = people.data?.items.length ?? 0;\n\n const continueWatching = useMemo(\n () => (watchProgress.data ?? []).filter((p) => !p.completed).slice(0, 4),\n [watchProgress.data]\n );\n\n const modules: Array<{\n id: string;\n label: string;\n description: string;\n icon: typeof Film;\n path: string;\n }> = [\n {\n id: 'catalog',\n label: 'Catalog',\n description: 'Movies, series, anime & more',\n icon: Film,\n path: 'catalog/titles',\n },\n {\n id: 'studios',\n label: 'Studios',\n description: 'Studios, channels & networks',\n icon: Building2,\n path: 'catalog/studios',\n },\n {\n id: 'people',\n label: 'People',\n description: 'Cast & crew directory',\n icon: Users,\n path: 'catalog/people',\n },\n {\n id: 'productions',\n label: 'Productions',\n description: 'Development → release pipeline',\n icon: Clapperboard,\n path: 'productions',\n },\n {\n id: 'auditions',\n label: 'Auditions',\n description: 'Casting & talent marketplace',\n icon: Megaphone,\n path: 'auditions',\n },\n {\n id: 'reviews',\n label: 'Reviews',\n description: 'Ratings & audience reviews',\n icon: Star,\n path: 'engagement/reviews',\n },\n {\n id: 'rights',\n label: 'Rights & Distribution',\n description: 'Licensing windows & territories',\n icon: ScrollText,\n path: 'distribution/rights',\n },\n {\n id: 'library',\n label: 'My Library',\n description: 'Watchlists, playlists & history',\n icon: PlayCircle,\n path: 'library/lists',\n },\n ];\n\n return (\n <PageLayout\n hero\n title=\"HookMovies\"\n description={\n currentUser?.name\n ? `Welcome back, ${currentUser.name}`\n : 'The global creative-media network'\n }\n purpose=\"Discover and stream titles, run productions and casting, and manage rights & distribution — your workspace's whole creative-media operation in one place.\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('catalog/titles/new')}\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 Title\n </button>\n }\n >\n {/* Key metrics */}\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-3 xl:grid-cols-6\">\n <MetricsCard\n featured\n icon={<Film className=\"h-5 w-5\" />}\n value={titleCount}\n label=\"Titles\"\n />\n <MetricsCard\n icon={<Clapperboard className=\"h-5 w-5\" />}\n value={productionCount}\n label=\"Productions\"\n />\n <MetricsCard\n icon={<Megaphone className=\"h-5 w-5\" />}\n value={auditionCount}\n label=\"Open Auditions\"\n />\n <MetricsCard icon={<Star className=\"h-5 w-5\" />} value={reviewCount} label=\"Reviews\" />\n <MetricsCard icon={<Building2 className=\"h-5 w-5\" />} value={studioCount} label=\"Studios\" />\n <MetricsCard icon={<Users className=\"h-5 w-5\" />} value={personCount} label=\"People\" />\n </div>\n\n {/* Continue watching */}\n {continueWatching.length > 0 && (\n <section className=\"mt-8\">\n <h2 className=\"mb-3 text-lg font-semibold text-text-primary\">Continue Watching</h2>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n {continueWatching.map((entry) => (\n <button\n key={entry.id}\n type=\"button\"\n onClick={() => entry.title && navigateTo(`catalog/titles/${entry.title.id}`)}\n className=\"text-left\"\n >\n <GlassCard treatment=\"metric\" className=\"h-full border-border-seam p-4\">\n <div className=\"flex items-center gap-2 text-action-primary-bg\">\n <PlayCircle className=\"h-5 w-5\" />\n {entry.title && (\n <span className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">\n {formatEnumLabel(entry.title.type)}\n </span>\n )}\n </div>\n <p className=\"mt-2 line-clamp-1 text-sm font-semibold text-text-primary\">\n {entry.title?.title ?? 'Untitled'}\n </p>\n <p className=\"mt-1 text-xs text-text-secondary\">\n {Math.round(entry.positionSeconds / 60)} min in ·{' '}\n {formatRelativeTime(entry.updatedAt)}\n </p>\n </GlassCard>\n </button>\n ))}\n </div>\n </section>\n )}\n\n {/* Modules */}\n <section className=\"mt-8\">\n <h2 className=\"mb-3 text-lg font-semibold text-text-primary\">Modules</h2>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n {modules.map((mod) => {\n const Icon = mod.icon;\n return (\n <button\n key={mod.id}\n type=\"button\"\n onClick={() => navigateTo(mod.path)}\n className=\"group text-left\"\n >\n <GlassCard\n treatment=\"metric\"\n className=\"flex h-full flex-col border-border-seam p-5\"\n >\n <div className=\"flex items-center justify-between\">\n <span className=\"inline-flex h-10 w-10 items-center justify-center rounded-lg bg-bg-sunken text-action-primary-bg\">\n <Icon className=\"h-5 w-5\" />\n </span>\n <ArrowRight className=\"h-4 w-4 text-text-secondary transition-colors group-hover:text-text-primary\" />\n </div>\n <p className=\"mt-3 text-sm font-semibold text-text-primary\">{mod.label}</p>\n <p className=\"mt-1 text-xs text-text-secondary\">{mod.description}</p>\n </GlassCard>\n </button>\n );\n })}\n </div>\n </section>\n\n {/* Recently added titles */}\n <section className=\"mt-8\">\n <div className=\"mb-3 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Recently Added</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('catalog/titles')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-action-primary-bg hover:opacity-80\"\n >\n View all\n <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <ul className=\"divide-y divide-border-seam\">\n {(recentTitles.data?.items ?? []).map((title) => (\n <li key={title.id}>\n <button\n type=\"button\"\n onClick={() => navigateTo(`catalog/titles/${title.id}`)}\n className={cn(\n 'flex w-full items-center justify-between gap-3 px-4 py-3 text-left',\n 'hover:bg-bg-sunken transition-colors'\n )}\n >\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{title.title}</p>\n <p className=\"text-xs text-text-secondary\">\n {formatEnumLabel(title.type)}\n {title.releaseYear ? ` · ${title.releaseYear}` : ''}\n {title.originalLanguage ? ` · ${title.originalLanguage}` : ''}\n </p>\n </div>\n <StatusBadge status={title.status} />\n </button>\n </li>\n ))}\n {recentTitles.data && recentTitles.data.items.length === 0 && (\n <li className=\"px-4 py-6 text-center text-sm text-text-secondary\">\n No titles yet — create your first title to get started.\n </li>\n )}\n </ul>\n </GlassCard>\n </section>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuditionDetailPage.js","names":[],"sources":["../../../../src/hookmovies/pages/auditions/AuditionDetailPage.tsx"],"sourcesContent":["/**\n * Audition detail (Casting marketplace)\n * @module hookmovies/pages/auditions\n *\n * Two-sided surface:\n * - Talent can apply via `hokApplyToAudition` (self-scoped).\n * - Casting teams screen applicants from `hokListApplications`, score them via\n * `hokScoreApplication`, and close the call with `hokCloseAudition`.\n */\n\nimport { type FC, type FormEvent, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n MapPin,\n Languages,\n Clock,\n Coins,\n Send,\n Lock,\n Users,\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 { useHookMovies } from '../../context/HookMoviesContext';\nimport {\n useAuditionById,\n useAuditionApplications,\n useApplyToAudition,\n useScoreApplication,\n useCloseAudition,\n} from '../../hooks/useHookMoviesApi';\nimport { formatDate, formatMinorUnits, formatRelativeTime } from '../../utils/formatters';\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)]';\n\nexport const AuditionDetailPage: FC = () => {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useHookMovies();\n const { data: audition, isLoading, isError, refetch } = useAuditionById(id);\n const applications = useAuditionApplications(id);\n const apply = useApplyToAudition();\n const score = useScoreApplication();\n const closeAudition = useCloseAudition();\n\n const [coverNote, setCoverNote] = useState('');\n const [selfTapeFileId, setSelfTapeFileId] = useState('');\n const [applyError, setApplyError] = useState<string | null>(null);\n const [applied, setApplied] = useState(false);\n const [scoreError, setScoreError] = useState<string | null>(null);\n\n if (isLoading) {\n return (\n <PageLayout title=\"Audition\">\n <LoadingState label=\"Loading audition…\" />\n </PageLayout>\n );\n }\n\n if (isError || !audition) {\n return (\n <PageLayout title=\"Audition\">\n <ErrorState title=\"Audition not found\" onRetry={() => void refetch()} />\n </PageLayout>\n );\n }\n\n const handleApply = async (e: FormEvent) => {\n e.preventDefault();\n setApplyError(null);\n try {\n await apply.mutateAsync({\n auditionId: audition.id,\n coverNote: coverNote.trim() || undefined,\n selfTapeFileId: selfTapeFileId.trim() || undefined,\n });\n setApplied(true);\n setCoverNote('');\n setSelfTapeFileId('');\n } catch (err) {\n setApplyError(err instanceof Error ? err.message : 'Failed to apply');\n }\n };\n\n const handleScore = async (applicationId: string, value: number) => {\n setScoreError(null);\n try {\n await score.mutateAsync({ id: applicationId, score: value });\n } catch (err) {\n setScoreError(err instanceof Error ? err.message : 'Failed to score');\n }\n };\n\n const isOpen = audition.status === 'OPEN';\n const applicationItems = applications.data ?? [];\n\n return (\n <PageLayout\n title={\n <span className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigateTo('auditions')}\n className=\"rounded-md p-1 text-text-secondary hover:text-text-primary\"\n aria-label=\"Back to auditions\"\n >\n <ArrowLeft className=\"h-5 w-5\" />\n </button>\n {audition.roleName}\n </span>\n }\n description={audition.production?.title ?? 'Casting call'}\n actions={\n <div className=\"flex items-center gap-2\">\n <StatusBadge status={audition.status} />\n {isOpen && (\n <button\n type=\"button\"\n disabled={closeAudition.isPending}\n onClick={() => void closeAudition.mutateAsync(audition.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-primary hover:bg-bg-sunken disabled:opacity-50 transition-colors\"\n >\n <Lock className=\"h-3.5 w-3.5\" />\n Close\n </button>\n )}\n </div>\n }\n >\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n <div className=\"space-y-6 lg:col-span-2\">\n {audition.description && (\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-5\">\n <h2 className=\"mb-2 text-sm font-semibold text-text-primary\">Role Description</h2>\n <p className=\"text-sm leading-relaxed text-text-secondary\">{audition.description}</p>\n </GlassCard>\n )}\n\n {/* Applicants (casting view) */}\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 Applicants ({applicationItems.length})\n </h2>\n {scoreError && (\n <div className=\"mb-3 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-xs text-status-error-text\">\n {scoreError}\n </div>\n )}\n {applications.isLoading ? (\n <LoadingState label=\"Loading applicants…\" className=\"min-h-[120px]\" />\n ) : applicationItems.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">No applications yet.</p>\n ) : (\n <ul className=\"space-y-3\">\n {applicationItems.map((appn) => (\n <li\n key={appn.id}\n className=\"rounded-lg border border-border-seam bg-bg-surface p-3\"\n >\n <div className=\"flex items-center justify-between gap-2\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n Applicant {appn.applicantId.slice(0, 8)}\n </p>\n <p className=\"text-xs text-text-secondary\">\n Applied {formatRelativeTime(appn.createdAt)}\n </p>\n </div>\n <StatusBadge status={appn.status} />\n </div>\n {appn.coverNote && (\n <p className=\"mt-2 text-sm text-text-secondary\">{appn.coverNote}</p>\n )}\n <div className=\"mt-3 flex items-center gap-2\">\n <span className=\"text-xs text-text-secondary\">Score:</span>\n {[1, 2, 3, 4, 5].map((value) => (\n <button\n key={value}\n type=\"button\"\n disabled={score.isPending}\n onClick={() => void handleScore(appn.id, value)}\n className={\n appn.score === value\n ? 'h-7 w-7 rounded-md bg-action-primary-bg text-xs font-semibold text-action-primary-text'\n : 'h-7 w-7 rounded-md border border-border-seam text-xs text-text-primary hover:bg-bg-sunken transition-colors'\n }\n >\n {value}\n </button>\n ))}\n </div>\n </li>\n ))}\n </ul>\n )}\n </GlassCard>\n </div>\n\n <div className=\"space-y-6\">\n {/* Details */}\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 text-sm\">\n <div className=\"flex items-center justify-between\">\n <dt className=\"flex items-center gap-2 text-text-secondary\">\n <Coins className=\"h-3.5 w-3.5\" />\n Pay\n </dt>\n <dd className=\"font-medium text-text-primary\">\n {formatMinorUnits(audition.payMinorUnits, audition.currency)}\n </dd>\n </div>\n {audition.location && (\n <div className=\"flex items-center justify-between\">\n <dt className=\"flex items-center gap-2 text-text-secondary\">\n <MapPin className=\"h-3.5 w-3.5\" />\n Location\n </dt>\n <dd className=\"font-medium text-text-primary\">{audition.location}</dd>\n </div>\n )}\n {audition.language && (\n <div className=\"flex items-center justify-between\">\n <dt className=\"flex items-center gap-2 text-text-secondary\">\n <Languages className=\"h-3.5 w-3.5\" />\n Language\n </dt>\n <dd className=\"font-medium text-text-primary\">{audition.language}</dd>\n </div>\n )}\n {audition.submissionDeadline && (\n <div className=\"flex items-center justify-between\">\n <dt className=\"flex items-center gap-2 text-text-secondary\">\n <Clock className=\"h-3.5 w-3.5\" />\n Deadline\n </dt>\n <dd className=\"font-medium text-text-primary\">\n {formatDate(audition.submissionDeadline, { hour: undefined, minute: undefined })}\n </dd>\n </div>\n )}\n </dl>\n {audition.isMinorRole && (\n <p className=\"mt-3 rounded-lg bg-status-warning-bg-subtle px-3 py-2 text-xs text-status-warning-text\">\n This is a minor role. Guardian consent and restricted communications apply.\n </p>\n )}\n </GlassCard>\n\n {/* Apply (talent view) */}\n <GlassCard treatment=\"glass\" className=\"border-border-seam p-5\">\n <h2 className=\"mb-3 text-sm font-semibold text-text-primary\">Apply</h2>\n {applied ? (\n <p className=\"rounded-lg bg-status-success-bg-subtle px-3 py-2 text-sm text-status-success-text\">\n Your application has been submitted.\n </p>\n ) : !isOpen ? (\n <p className=\"text-sm text-text-secondary\">This audition is no longer open.</p>\n ) : (\n <form onSubmit={handleApply} className=\"space-y-3\">\n {applyError && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-xs text-status-error-text\">\n {applyError}\n </div>\n )}\n <textarea\n value={coverNote}\n onChange={(e) => setCoverNote(e.target.value)}\n rows={3}\n placeholder=\"Cover note (why you're right for this role)\"\n className={fieldClass}\n />\n <input\n type=\"text\"\n value={selfTapeFileId}\n onChange={(e) => setSelfTapeFileId(e.target.value)}\n placeholder=\"Self-tape file ID (optional)\"\n className={fieldClass}\n />\n <button\n type=\"submit\"\n disabled={apply.isPending}\n className=\"inline-flex w-full items-center justify-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 <Send className=\"h-4 w-4\" />\n {apply.isPending ? 'Submitting…' : 'Submit Application'}\n </button>\n </form>\n )}\n </GlassCard>\n </div>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AuditionDetailPage.js","names":[],"sources":["../../../../src/hookmovies/pages/auditions/AuditionDetailPage.tsx"],"sourcesContent":["/**\n * Audition detail (Casting marketplace)\n * @module hookmovies/pages/auditions\n *\n * Two-sided surface:\n * - Talent can apply via `hokApplyToAudition` (self-scoped).\n * - Casting teams screen applicants from `hokListApplications`, score them via\n * `hokScoreApplication`, and close the call with `hokCloseAudition`.\n */\n\nimport { type FC, type FormEvent, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { ArrowLeft, MapPin, Languages, Clock, Coins, Send, Lock, Users } 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 { useHookMovies } from '../../context/HookMoviesContext';\nimport {\n useAuditionById,\n useAuditionApplications,\n useApplyToAudition,\n useScoreApplication,\n useCloseAudition,\n} from '../../hooks/useHookMoviesApi';\nimport { formatDate, formatMinorUnits, formatRelativeTime } from '../../utils/formatters';\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)]';\n\nexport const AuditionDetailPage: FC = () => {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useHookMovies();\n const { data: audition, isLoading, isError, refetch } = useAuditionById(id);\n const applications = useAuditionApplications(id);\n const apply = useApplyToAudition();\n const score = useScoreApplication();\n const closeAudition = useCloseAudition();\n\n const [coverNote, setCoverNote] = useState('');\n const [selfTapeFileId, setSelfTapeFileId] = useState('');\n const [applyError, setApplyError] = useState<string | null>(null);\n const [applied, setApplied] = useState(false);\n const [scoreError, setScoreError] = useState<string | null>(null);\n\n if (isLoading) {\n return (\n <PageLayout title=\"Audition\">\n <LoadingState label=\"Loading audition…\" />\n </PageLayout>\n );\n }\n\n if (isError || !audition) {\n return (\n <PageLayout title=\"Audition\">\n <ErrorState title=\"Audition not found\" onRetry={() => void refetch()} />\n </PageLayout>\n );\n }\n\n const handleApply = async (e: FormEvent) => {\n e.preventDefault();\n setApplyError(null);\n try {\n await apply.mutateAsync({\n auditionId: audition.id,\n coverNote: coverNote.trim() || undefined,\n selfTapeFileId: selfTapeFileId.trim() || undefined,\n });\n setApplied(true);\n setCoverNote('');\n setSelfTapeFileId('');\n } catch (err) {\n setApplyError(err instanceof Error ? err.message : 'Failed to apply');\n }\n };\n\n const handleScore = async (applicationId: string, value: number) => {\n setScoreError(null);\n try {\n await score.mutateAsync({ id: applicationId, score: value });\n } catch (err) {\n setScoreError(err instanceof Error ? err.message : 'Failed to score');\n }\n };\n\n const isOpen = audition.status === 'OPEN';\n const applicationItems = applications.data ?? [];\n\n return (\n <PageLayout\n title={\n <span className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigateTo('auditions')}\n className=\"rounded-md p-1 text-text-secondary hover:text-text-primary\"\n aria-label=\"Back to auditions\"\n >\n <ArrowLeft className=\"h-5 w-5\" />\n </button>\n {audition.roleName}\n </span>\n }\n description={audition.production?.title ?? 'Casting call'}\n actions={\n <div className=\"flex items-center gap-2\">\n <StatusBadge status={audition.status} />\n {isOpen && (\n <button\n type=\"button\"\n disabled={closeAudition.isPending}\n onClick={() => void closeAudition.mutateAsync(audition.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-primary hover:bg-bg-sunken disabled:opacity-50 transition-colors\"\n >\n <Lock className=\"h-3.5 w-3.5\" />\n Close\n </button>\n )}\n </div>\n }\n >\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n <div className=\"space-y-6 lg:col-span-2\">\n {audition.description && (\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-5\">\n <h2 className=\"mb-2 text-sm font-semibold text-text-primary\">Role Description</h2>\n <p className=\"text-sm leading-relaxed text-text-secondary\">{audition.description}</p>\n </GlassCard>\n )}\n\n {/* Applicants (casting view) */}\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 Applicants ({applicationItems.length})\n </h2>\n {scoreError && (\n <div className=\"mb-3 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-xs text-status-error-text\">\n {scoreError}\n </div>\n )}\n {applications.isLoading ? (\n <LoadingState label=\"Loading applicants…\" className=\"min-h-[120px]\" />\n ) : applicationItems.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">No applications yet.</p>\n ) : (\n <ul className=\"space-y-3\">\n {applicationItems.map((appn) => (\n <li\n key={appn.id}\n className=\"rounded-lg border border-border-seam bg-bg-surface p-3\"\n >\n <div className=\"flex items-center justify-between gap-2\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n Applicant {appn.applicantId.slice(0, 8)}\n </p>\n <p className=\"text-xs text-text-secondary\">\n Applied {formatRelativeTime(appn.createdAt)}\n </p>\n </div>\n <StatusBadge status={appn.status} />\n </div>\n {appn.coverNote && (\n <p className=\"mt-2 text-sm text-text-secondary\">{appn.coverNote}</p>\n )}\n <div className=\"mt-3 flex items-center gap-2\">\n <span className=\"text-xs text-text-secondary\">Score:</span>\n {[1, 2, 3, 4, 5].map((value) => (\n <button\n key={value}\n type=\"button\"\n disabled={score.isPending}\n onClick={() => void handleScore(appn.id, value)}\n className={\n appn.score === value\n ? 'h-7 w-7 rounded-md bg-action-primary-bg text-xs font-semibold text-action-primary-text'\n : 'h-7 w-7 rounded-md border border-border-seam text-xs text-text-primary hover:bg-bg-sunken transition-colors'\n }\n >\n {value}\n </button>\n ))}\n </div>\n </li>\n ))}\n </ul>\n )}\n </GlassCard>\n </div>\n\n <div className=\"space-y-6\">\n {/* Details */}\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 text-sm\">\n <div className=\"flex items-center justify-between\">\n <dt className=\"flex items-center gap-2 text-text-secondary\">\n <Coins className=\"h-3.5 w-3.5\" />\n Pay\n </dt>\n <dd className=\"font-medium text-text-primary\">\n {formatMinorUnits(audition.payMinorUnits, audition.currency)}\n </dd>\n </div>\n {audition.location && (\n <div className=\"flex items-center justify-between\">\n <dt className=\"flex items-center gap-2 text-text-secondary\">\n <MapPin className=\"h-3.5 w-3.5\" />\n Location\n </dt>\n <dd className=\"font-medium text-text-primary\">{audition.location}</dd>\n </div>\n )}\n {audition.language && (\n <div className=\"flex items-center justify-between\">\n <dt className=\"flex items-center gap-2 text-text-secondary\">\n <Languages className=\"h-3.5 w-3.5\" />\n Language\n </dt>\n <dd className=\"font-medium text-text-primary\">{audition.language}</dd>\n </div>\n )}\n {audition.submissionDeadline && (\n <div className=\"flex items-center justify-between\">\n <dt className=\"flex items-center gap-2 text-text-secondary\">\n <Clock className=\"h-3.5 w-3.5\" />\n Deadline\n </dt>\n <dd className=\"font-medium text-text-primary\">\n {formatDate(audition.submissionDeadline, {\n hour: undefined,\n minute: undefined,\n })}\n </dd>\n </div>\n )}\n </dl>\n {audition.isMinorRole && (\n <p className=\"mt-3 rounded-lg bg-status-warning-bg-subtle px-3 py-2 text-xs text-status-warning-text\">\n This is a minor role. Guardian consent and restricted communications apply.\n </p>\n )}\n </GlassCard>\n\n {/* Apply (talent view) */}\n <GlassCard treatment=\"glass\" className=\"border-border-seam p-5\">\n <h2 className=\"mb-3 text-sm font-semibold text-text-primary\">Apply</h2>\n {applied ? (\n <p className=\"rounded-lg bg-status-success-bg-subtle px-3 py-2 text-sm text-status-success-text\">\n Your application has been submitted.\n </p>\n ) : !isOpen ? (\n <p className=\"text-sm text-text-secondary\">This audition is no longer open.</p>\n ) : (\n <form onSubmit={handleApply} className=\"space-y-3\">\n {applyError && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-xs text-status-error-text\">\n {applyError}\n </div>\n )}\n <textarea\n value={coverNote}\n onChange={(e) => setCoverNote(e.target.value)}\n rows={3}\n placeholder=\"Cover note (why you're right for this role)\"\n className={fieldClass}\n />\n <input\n type=\"text\"\n value={selfTapeFileId}\n onChange={(e) => setSelfTapeFileId(e.target.value)}\n placeholder=\"Self-tape file ID (optional)\"\n className={fieldClass}\n />\n <button\n type=\"submit\"\n disabled={apply.isPending}\n className=\"inline-flex w-full items-center justify-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 <Send className=\"h-4 w-4\" />\n {apply.isPending ? 'Submitting…' : 'Submit Application'}\n </button>\n </form>\n )}\n </GlassCard>\n </div>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuditionsPage.js","names":[],"sources":["../../../../src/hookmovies/pages/auditions/AuditionsPage.tsx"],"sourcesContent":["/**\n * Auditions list (Casting marketplace)\n * @module hookmovies/pages/auditions\n *\n * Lists casting role postings via `hokListAuditions` with a status filter.\n * Talent open a posting to apply; casting teams open it to screen applicants.\n */\n\nimport { type FC, useState } from 'react';\nimport { Megaphone, Plus, MapPin, Languages, Clock } 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 { useHookMovies } from '../../context/HookMoviesContext';\nimport { useAuditions, type HokAuditionStatus } from '../../hooks/useHookMoviesApi';\nimport { formatEnumLabel, formatMinorUnits, formatDate } from '../../utils/formatters';\n\nconst STATUS_OPTIONS: HokAuditionStatus[] = [\n 'OPEN',\n 'REVIEWING',\n 'CALLBACKS',\n 'CLOSED',\n 'CANCELLED',\n];\n\nconst PAGE_LIMIT = 20;\n\nexport const AuditionsPage: FC = () => {\n const { navigateTo } = useHookMovies();\n const [statusFilter, setStatusFilter] = useState<HokAuditionStatus | ''>('OPEN');\n const [page, setPage] = useState(0);\n\n const { data, isLoading, isError, refetch } = useAuditions(\n { status: statusFilter || undefined },\n { limit: PAGE_LIMIT, offset: page * PAGE_LIMIT }\n );\n\n const items = data?.items ?? [];\n\n return (\n <PageLayout\n title=\"Auditions\"\n description=\"Casting & talent marketplace\"\n purpose=\"Open casting calls across the network. Talent discover and apply to roles with a self-tape; casting teams screen, score, and shortlist applicants.\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('auditions/new')}\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 Post Role\n </button>\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 HokAuditionStatus | '');\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 auditions…\" />\n ) : isError ? (\n <ErrorState onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n <EmptyState\n icon={<Megaphone className=\"h-12 w-12\" />}\n title=\"No auditions\"\n description=\"No casting calls match this filter.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('auditions/new')}\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 Post a Role\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((aud) => (\n <button\n key={aud.id}\n type=\"button\"\n onClick={() => navigateTo(`auditions/${aud.id}`)}\n className=\"text-left\"\n >\n <GlassCard
|
|
1
|
+
{"version":3,"file":"AuditionsPage.js","names":[],"sources":["../../../../src/hookmovies/pages/auditions/AuditionsPage.tsx"],"sourcesContent":["/**\n * Auditions list (Casting marketplace)\n * @module hookmovies/pages/auditions\n *\n * Lists casting role postings via `hokListAuditions` with a status filter.\n * Talent open a posting to apply; casting teams open it to screen applicants.\n */\n\nimport { type FC, useState } from 'react';\nimport { Megaphone, Plus, MapPin, Languages, Clock } 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 { useHookMovies } from '../../context/HookMoviesContext';\nimport { useAuditions, type HokAuditionStatus } from '../../hooks/useHookMoviesApi';\nimport { formatEnumLabel, formatMinorUnits, formatDate } from '../../utils/formatters';\n\nconst STATUS_OPTIONS: HokAuditionStatus[] = [\n 'OPEN',\n 'REVIEWING',\n 'CALLBACKS',\n 'CLOSED',\n 'CANCELLED',\n];\n\nconst PAGE_LIMIT = 20;\n\nexport const AuditionsPage: FC = () => {\n const { navigateTo } = useHookMovies();\n const [statusFilter, setStatusFilter] = useState<HokAuditionStatus | ''>('OPEN');\n const [page, setPage] = useState(0);\n\n const { data, isLoading, isError, refetch } = useAuditions(\n { status: statusFilter || undefined },\n { limit: PAGE_LIMIT, offset: page * PAGE_LIMIT }\n );\n\n const items = data?.items ?? [];\n\n return (\n <PageLayout\n title=\"Auditions\"\n description=\"Casting & talent marketplace\"\n purpose=\"Open casting calls across the network. Talent discover and apply to roles with a self-tape; casting teams screen, score, and shortlist applicants.\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('auditions/new')}\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 Post Role\n </button>\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 HokAuditionStatus | '');\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 auditions…\" />\n ) : isError ? (\n <ErrorState onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n <EmptyState\n icon={<Megaphone className=\"h-12 w-12\" />}\n title=\"No auditions\"\n description=\"No casting calls match this filter.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('auditions/new')}\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 Post a Role\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((aud) => (\n <button\n key={aud.id}\n type=\"button\"\n onClick={() => navigateTo(`auditions/${aud.id}`)}\n className=\"text-left\"\n >\n <GlassCard treatment=\"metric\" className=\"flex h-full flex-col border-border-seam p-4\">\n <div className=\"flex items-start justify-between gap-2\">\n <h3 className=\"line-clamp-2 text-sm font-semibold text-text-primary\">\n {aud.roleName}\n </h3>\n <StatusBadge status={aud.status} />\n </div>\n {aud.production && (\n <p className=\"mt-1 text-xs text-text-secondary\">{aud.production.title}</p>\n )}\n {aud.isMinorRole && (\n <span className=\"mt-2 inline-block w-fit rounded-full bg-status-warning-bg-subtle px-2 py-0.5 text-[10px] text-status-warning-text\">\n Minor role — guardian consent required\n </span>\n )}\n {aud.description && (\n <p className=\"mt-2 line-clamp-2 text-xs text-text-secondary\">{aud.description}</p>\n )}\n <div className=\"mt-3 space-y-1 text-xs text-text-secondary\">\n <p className=\"font-medium text-text-primary\">\n {formatMinorUnits(aud.payMinorUnits, aud.currency)}\n </p>\n {aud.location && (\n <p className=\"flex items-center gap-1\">\n <MapPin className=\"h-3 w-3\" />\n {aud.location}\n </p>\n )}\n {aud.language && (\n <p className=\"flex items-center gap-1\">\n <Languages className=\"h-3 w-3\" />\n {aud.language}\n </p>\n )}\n {aud.submissionDeadline && (\n <p className=\"flex items-center gap-1\">\n <Clock className=\"h-3 w-3\" />\n Deadline{' '}\n {formatDate(aud.submissionDeadline, { hour: undefined, minute: undefined })}\n </p>\n )}\n </div>\n </GlassCard>\n </button>\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":";;;;;;;;;;;;;AAoBA,IAAM,IAAsC;CAC1C;CACA;CACA;CACA;CACA;CACD,EAEK,IAAa,IAEN,UAA0B;CACrC,IAAM,EAAE,kBAAe,GAAe,EAChC,CAAC,GAAc,KAAmB,EAAiC,OAAO,EAC1E,CAAC,GAAM,KAAW,EAAS,EAAE,EAE7B,EAAE,SAAM,cAAW,YAAS,eAAY,EAC5C,EAAE,QAAQ,KAAgB,KAAA,GAAW,EACrC;EAAE,OAAO;EAAY,QAAQ,IAAO;EAAY,CACjD,EAEK,IAAQ,GAAM,SAAS,EAAE;AAE/B,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SAAQ;EACR,SACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,EAAW,gBAAgB;GAC1C,WAAU;aAHZ,CAKE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,YAErB;;YAZb;GAeE,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,UAAD;KACE,OAAO;KACP,WAAW,MAAM;AAEf,MADA,EAAgB,EAAE,OAAO,MAAgC,EACzD,EAAQ,EAAE;;KAEZ,WAAU;eANZ,CAQE,kBAAC,UAAD;MAAQ,OAAM;gBAAG;MAAmB,CAAA,EACnC,EAAe,KAAK,MACnB,kBAAC,UAAD;MAAgB,OAAO;gBACpB,EAAgB,EAAE;MACZ,EAFI,EAEJ,CACT,CACK;;IACL,CAAA;GAEL,IACC,kBAAC,GAAD,EAAc,OAAM,sBAAuB,CAAA,GACzC,IACF,kBAAC,GAAD,EAAY,eAAe,KAAK,GAAS,EAAI,CAAA,GAC3C,EAAM,WAAW,IACnB,kBAAC,GAAD;IACE,MAAM,kBAAC,GAAD,EAAW,WAAU,aAAc,CAAA;IACzC,OAAM;IACN,aAAY;IACZ,QACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAW,gBAAgB;KAC1C,WAAU;eACX;KAEQ,CAAA;IAEX,CAAA,GAEF,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAM,KAAK,MACV,kBAAC,UAAD;KAEE,MAAK;KACL,eAAe,EAAW,aAAa,EAAI,KAAK;KAChD,WAAU;eAEV,kBAAC,GAAD;MAAW,WAAU;MAAS,WAAU;gBAAxC;OACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAI;SACF,CAAA,EACL,kBAAC,GAAD,EAAa,QAAQ,EAAI,QAAU,CAAA,CAC/B;;OACL,EAAI,cACH,kBAAC,KAAD;QAAG,WAAU;kBAAoC,EAAI,WAAW;QAAU,CAAA;OAE3E,EAAI,eACH,kBAAC,QAAD;QAAM,WAAU;kBAAoH;QAE7H,CAAA;OAER,EAAI,eACH,kBAAC,KAAD;QAAG,WAAU;kBAAiD,EAAI;QAAgB,CAAA;OAEpF,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,KAAD;UAAG,WAAU;oBACV,EAAiB,EAAI,eAAe,EAAI,SAAS;UAChD,CAAA;SACH,EAAI,YACH,kBAAC,KAAD;UAAG,WAAU;oBAAb,CACE,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA,EAC7B,EAAI,SACH;;SAEL,EAAI,YACH,kBAAC,KAAD;UAAG,WAAU;oBAAb,CACE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAChC,EAAI,SACH;;SAEL,EAAI,sBACH,kBAAC,KAAD;UAAG,WAAU;oBAAb;WACE,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;;WACpB;WACR,EAAW,EAAI,oBAAoB;YAAE,MAAM,KAAA;YAAW,QAAQ,KAAA;YAAW,CAAC;WACzE;;SAEF;;OACI;;KACL,EAhDF,EAAI,GAgDF,CACT;IACE,CAAA;GAGP,CAAC,KAAa,CAAC,KACd,kBAAC,GAAD;IACQ;IACN,SAAS,GAAM,SAAS,WAAW;IACnC,cAAc;IACd,YAAY,EAAM;IAClB,CAAA;GAEO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PeoplePage.js","names":[],"sources":["../../../../src/hookmovies/pages/catalog/PeoplePage.tsx"],"sourcesContent":["/**\n * People list (Catalog)\n * @module hookmovies/pages/catalog\n *\n * Cast & crew directory via `hokListPeople` with inline creation through\n * `hokCreatePerson`. People are linked to titles through credits.\n */\n\nimport { type FC, type FormEvent, useState } from 'react';\nimport { Users, Plus, X, Languages } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingState, ErrorState } from '../../components/StateViews';\nimport { Pagination } from '../../components/Pagination';\nimport {\n usePeople,\n useCreatePerson,\n type CreatePersonInput,\n} from '../../hooks/useHookMoviesApi';\n\nconst PAGE_LIMIT = 24;\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 PeoplePage: FC = () => {\n const [page, setPage] = useState(0);\n const [showCreate, setShowCreate] = useState(false);\n const { data, isLoading, isError, refetch } = usePeople({\n limit: PAGE_LIMIT,\n offset: page * PAGE_LIMIT,\n });\n const createPerson = useCreatePerson();\n\n const [name, setName] = useState('');\n const [knownFor, setKnownFor] = useState('');\n const [primaryLanguage, setPrimaryLanguage] = useState('');\n const [biography, setBiography] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const resetForm = () => {\n setName('');\n setKnownFor('');\n setPrimaryLanguage('');\n setBiography('');\n setError(null);\n };\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setError(null);\n if (!name.trim()) {\n setError('Name is required.');\n return;\n }\n const input: CreatePersonInput = {\n name: name.trim(),\n knownFor: knownFor.trim() || undefined,\n primaryLanguage: primaryLanguage.trim() || undefined,\n biography: biography.trim() || undefined,\n };\n try {\n await createPerson.mutateAsync(input);\n resetForm();\n setShowCreate(false);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create person');\n }\n };\n\n const items = data?.items ?? [];\n\n return (\n <PageLayout\n title=\"People\"\n description=\"Cast & crew directory\"\n purpose=\"Actors, directors, writers, and crew across the network. People are credited on titles and can be linked to Artistrybase portfolios.\"\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 Person\n </button>\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\">Add Person</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 className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"person-name\" className={labelClass}>\n Name *\n </label>\n <input\n id=\"person-name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. Aria Mendez\"\n className={fieldClass}\n required\n />\n </div>\n <div>\n <label htmlFor=\"person-known\" className={labelClass}>\n Known For\n </label>\n <input\n id=\"person-known\"\n type=\"text\"\n value={knownFor}\n onChange={(e) => setKnownFor(e.target.value)}\n placeholder=\"Acting\"\n className={fieldClass}\n />\n </div>\n </div>\n <div>\n <label htmlFor=\"person-lang\" className={labelClass}>\n Primary Language\n </label>\n <input\n id=\"person-lang\"\n type=\"text\"\n value={primaryLanguage}\n onChange={(e) => setPrimaryLanguage(e.target.value)}\n placeholder=\"en\"\n className={fieldClass}\n />\n </div>\n <div>\n <label htmlFor=\"person-bio\" className={labelClass}>\n Biography\n </label>\n <textarea\n id=\"person-bio\"\n value={biography}\n onChange={(e) => setBiography(e.target.value)}\n rows={3}\n className={fieldClass}\n />\n </div>\n <button\n type=\"submit\"\n disabled={createPerson.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 {createPerson.isPending ? 'Adding…' : 'Add Person'}\n </button>\n </form>\n </GlassCard>\n )}\n\n {isLoading ? (\n <LoadingState label=\"Loading people…\" />\n ) : isError ? (\n <ErrorState onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n <EmptyState\n icon={<Users className=\"h-12 w-12\" />}\n title=\"No people yet\"\n description=\"Add cast and crew to credit them on your titles.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\">\n {items.map((person) => (\n <GlassCard\n key={person.id}\n treatment=\"metric\"\n className=\"flex flex-col border-border-seam p-4\"\n >\n <div className=\"flex items-center gap-3\">\n <span className=\"inline-flex h-10 w-10 items-center justify-center rounded-full bg-bg-sunken text-sm font-semibold text-text-primary\">\n {person.name.charAt(0).toUpperCase()}\n </span>\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-sm font-semibold text-text-primary\">\n {person.name}\n </h3>\n {person.knownFor && (\n <p className=\"truncate text-xs text-text-secondary\">{person.knownFor}</p>\n )}\n </div>\n </div>\n {person.primaryLanguage && (\n <p className=\"mt-3 inline-flex items-center gap-1 text-xs text-text-secondary\">\n <Languages className=\"h-3 w-3\" />\n {person.primaryLanguage}\n </p>\n )}\n {person.biography && (\n <p className=\"mt-2 line-clamp-3 text-xs text-text-secondary\">{person.biography}</p>\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":"PeoplePage.js","names":[],"sources":["../../../../src/hookmovies/pages/catalog/PeoplePage.tsx"],"sourcesContent":["/**\n * People list (Catalog)\n * @module hookmovies/pages/catalog\n *\n * Cast & crew directory via `hokListPeople` with inline creation through\n * `hokCreatePerson`. People are linked to titles through credits.\n */\n\nimport { type FC, type FormEvent, useState } from 'react';\nimport { Users, Plus, X, Languages } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingState, ErrorState } from '../../components/StateViews';\nimport { Pagination } from '../../components/Pagination';\nimport { usePeople, useCreatePerson, type CreatePersonInput } from '../../hooks/useHookMoviesApi';\n\nconst PAGE_LIMIT = 24;\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 PeoplePage: FC = () => {\n const [page, setPage] = useState(0);\n const [showCreate, setShowCreate] = useState(false);\n const { data, isLoading, isError, refetch } = usePeople({\n limit: PAGE_LIMIT,\n offset: page * PAGE_LIMIT,\n });\n const createPerson = useCreatePerson();\n\n const [name, setName] = useState('');\n const [knownFor, setKnownFor] = useState('');\n const [primaryLanguage, setPrimaryLanguage] = useState('');\n const [biography, setBiography] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const resetForm = () => {\n setName('');\n setKnownFor('');\n setPrimaryLanguage('');\n setBiography('');\n setError(null);\n };\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setError(null);\n if (!name.trim()) {\n setError('Name is required.');\n return;\n }\n const input: CreatePersonInput = {\n name: name.trim(),\n knownFor: knownFor.trim() || undefined,\n primaryLanguage: primaryLanguage.trim() || undefined,\n biography: biography.trim() || undefined,\n };\n try {\n await createPerson.mutateAsync(input);\n resetForm();\n setShowCreate(false);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create person');\n }\n };\n\n const items = data?.items ?? [];\n\n return (\n <PageLayout\n title=\"People\"\n description=\"Cast & crew directory\"\n purpose=\"Actors, directors, writers, and crew across the network. People are credited on titles and can be linked to Artistrybase portfolios.\"\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 Person\n </button>\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\">Add Person</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 className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"person-name\" className={labelClass}>\n Name *\n </label>\n <input\n id=\"person-name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. Aria Mendez\"\n className={fieldClass}\n required\n />\n </div>\n <div>\n <label htmlFor=\"person-known\" className={labelClass}>\n Known For\n </label>\n <input\n id=\"person-known\"\n type=\"text\"\n value={knownFor}\n onChange={(e) => setKnownFor(e.target.value)}\n placeholder=\"Acting\"\n className={fieldClass}\n />\n </div>\n </div>\n <div>\n <label htmlFor=\"person-lang\" className={labelClass}>\n Primary Language\n </label>\n <input\n id=\"person-lang\"\n type=\"text\"\n value={primaryLanguage}\n onChange={(e) => setPrimaryLanguage(e.target.value)}\n placeholder=\"en\"\n className={fieldClass}\n />\n </div>\n <div>\n <label htmlFor=\"person-bio\" className={labelClass}>\n Biography\n </label>\n <textarea\n id=\"person-bio\"\n value={biography}\n onChange={(e) => setBiography(e.target.value)}\n rows={3}\n className={fieldClass}\n />\n </div>\n <button\n type=\"submit\"\n disabled={createPerson.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 {createPerson.isPending ? 'Adding…' : 'Add Person'}\n </button>\n </form>\n </GlassCard>\n )}\n\n {isLoading ? (\n <LoadingState label=\"Loading people…\" />\n ) : isError ? (\n <ErrorState onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n <EmptyState\n icon={<Users className=\"h-12 w-12\" />}\n title=\"No people yet\"\n description=\"Add cast and crew to credit them on your titles.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\">\n {items.map((person) => (\n <GlassCard\n key={person.id}\n treatment=\"metric\"\n className=\"flex flex-col border-border-seam p-4\"\n >\n <div className=\"flex items-center gap-3\">\n <span className=\"inline-flex h-10 w-10 items-center justify-center rounded-full bg-bg-sunken text-sm font-semibold text-text-primary\">\n {person.name.charAt(0).toUpperCase()}\n </span>\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-sm font-semibold text-text-primary\">\n {person.name}\n </h3>\n {person.knownFor && (\n <p className=\"truncate text-xs text-text-secondary\">{person.knownFor}</p>\n )}\n </div>\n </div>\n {person.primaryLanguage && (\n <p className=\"mt-3 inline-flex items-center gap-1 text-xs text-text-secondary\">\n <Languages className=\"h-3 w-3\" />\n {person.primaryLanguage}\n </p>\n )}\n {person.biography && (\n <p className=\"mt-2 line-clamp-3 text-xs text-text-secondary\">{person.biography}</p>\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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StudiosPage.js","names":[],"sources":["../../../../src/hookmovies/pages/catalog/StudiosPage.tsx"],"sourcesContent":["/**\n * Studios list (Catalog)\n * @module hookmovies/pages/catalog\n *\n * Lists studios/channels via `hokListStudios` and creates them inline via\n * `hokCreateStudio`.\n */\n\nimport { type FC, type FormEvent, useState } from 'react';\nimport { Building2, Plus, X, CheckCircle2, Globe } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingState, ErrorState } from '../../components/StateViews';\nimport { Pagination } from '../../components/Pagination';\nimport {\n useStudios,\n useCreateStudio,\n type CreateStudioInput,\n} from '../../hooks/useHookMoviesApi';\n\nconst PAGE_LIMIT = 24;\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 StudiosPage: FC = () => {\n const [page, setPage] = useState(0);\n const [showCreate, setShowCreate] = useState(false);\n const { data, isLoading, isError, refetch } = useStudios({\n limit: PAGE_LIMIT,\n offset: page * PAGE_LIMIT,\n });\n const createStudio = useCreateStudio();\n\n const [name, setName] = useState('');\n const [country, setCountry] = useState('');\n const [websiteUrl, setWebsiteUrl] = useState('');\n const [description, setDescription] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const resetForm = () => {\n setName('');\n setCountry('');\n setWebsiteUrl('');\n setDescription('');\n setError(null);\n };\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setError(null);\n if (!name.trim()) {\n setError('Name is required.');\n return;\n }\n const input: CreateStudioInput = {\n name: name.trim(),\n country: country.trim() || undefined,\n websiteUrl: websiteUrl.trim() || undefined,\n description: description.trim() || undefined,\n };\n try {\n await createStudio.mutateAsync(input);\n resetForm();\n setShowCreate(false);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create studio');\n }\n };\n\n const items = data?.items ?? [];\n\n return (\n <PageLayout\n title=\"Studios\"\n description=\"Studios, channels, networks & distributors\"\n purpose=\"Every business actor in the network is a studio/channel record. Manage the catalog owners that publish, finance, and monetize titles.\"\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 Studio\n </button>\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 Studio</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 className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"studio-name\" className={labelClass}>\n Name *\n </label>\n <input\n id=\"studio-name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. Northstar Pictures\"\n className={fieldClass}\n required\n />\n </div>\n <div>\n <label htmlFor=\"studio-country\" className={labelClass}>\n Country\n </label>\n <input\n id=\"studio-country\"\n type=\"text\"\n value={country}\n onChange={(e) => setCountry(e.target.value)}\n placeholder=\"US\"\n className={fieldClass}\n />\n </div>\n </div>\n <div>\n <label htmlFor=\"studio-website\" className={labelClass}>\n Website\n </label>\n <input\n id=\"studio-website\"\n type=\"url\"\n value={websiteUrl}\n onChange={(e) => setWebsiteUrl(e.target.value)}\n placeholder=\"https://example.com\"\n className={fieldClass}\n />\n </div>\n <div>\n <label htmlFor=\"studio-desc\" className={labelClass}>\n Description\n </label>\n <textarea\n id=\"studio-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n className={fieldClass}\n />\n </div>\n <button\n type=\"submit\"\n disabled={createStudio.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 {createStudio.isPending ? 'Creating…' : 'Create Studio'}\n </button>\n </form>\n </GlassCard>\n )}\n\n {isLoading ? (\n <LoadingState label=\"Loading studios…\" />\n ) : isError ? (\n <ErrorState onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n <EmptyState\n icon={<Building2 className=\"h-12 w-12\" />}\n title=\"No studios yet\"\n description=\"Create your first studio to start organizing your catalog.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {items.map((studio) => (\n <GlassCard\n key={studio.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 <h3 className=\"flex items-center gap-2 text-sm font-semibold text-text-primary\">\n {studio.name}\n {studio.isVerified && (\n <CheckCircle2 className=\"h-4 w-4 text-status-success-text\" />\n )}\n </h3>\n </div>\n {studio.country && (\n <p className=\"mt-1 text-xs text-text-secondary\">{studio.country}</p>\n )}\n {studio.description && (\n <p className=\"mt-2 line-clamp-2 text-xs text-text-secondary\">\n {studio.description}\n </p>\n )}\n {studio.websiteUrl && (\n <a\n href={studio.websiteUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"mt-3 inline-flex items-center gap-1 text-xs font-medium text-action-primary-bg hover:opacity-80\"\n >\n <Globe className=\"h-3 w-3\" />\n Website\n </a>\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":"StudiosPage.js","names":[],"sources":["../../../../src/hookmovies/pages/catalog/StudiosPage.tsx"],"sourcesContent":["/**\n * Studios list (Catalog)\n * @module hookmovies/pages/catalog\n *\n * Lists studios/channels via `hokListStudios` and creates them inline via\n * `hokCreateStudio`.\n */\n\nimport { type FC, type FormEvent, useState } from 'react';\nimport { Building2, Plus, X, CheckCircle2, Globe } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingState, ErrorState } from '../../components/StateViews';\nimport { Pagination } from '../../components/Pagination';\nimport { useStudios, useCreateStudio, type CreateStudioInput } from '../../hooks/useHookMoviesApi';\n\nconst PAGE_LIMIT = 24;\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 StudiosPage: FC = () => {\n const [page, setPage] = useState(0);\n const [showCreate, setShowCreate] = useState(false);\n const { data, isLoading, isError, refetch } = useStudios({\n limit: PAGE_LIMIT,\n offset: page * PAGE_LIMIT,\n });\n const createStudio = useCreateStudio();\n\n const [name, setName] = useState('');\n const [country, setCountry] = useState('');\n const [websiteUrl, setWebsiteUrl] = useState('');\n const [description, setDescription] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const resetForm = () => {\n setName('');\n setCountry('');\n setWebsiteUrl('');\n setDescription('');\n setError(null);\n };\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setError(null);\n if (!name.trim()) {\n setError('Name is required.');\n return;\n }\n const input: CreateStudioInput = {\n name: name.trim(),\n country: country.trim() || undefined,\n websiteUrl: websiteUrl.trim() || undefined,\n description: description.trim() || undefined,\n };\n try {\n await createStudio.mutateAsync(input);\n resetForm();\n setShowCreate(false);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create studio');\n }\n };\n\n const items = data?.items ?? [];\n\n return (\n <PageLayout\n title=\"Studios\"\n description=\"Studios, channels, networks & distributors\"\n purpose=\"Every business actor in the network is a studio/channel record. Manage the catalog owners that publish, finance, and monetize titles.\"\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 Studio\n </button>\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 Studio</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 className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"studio-name\" className={labelClass}>\n Name *\n </label>\n <input\n id=\"studio-name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. Northstar Pictures\"\n className={fieldClass}\n required\n />\n </div>\n <div>\n <label htmlFor=\"studio-country\" className={labelClass}>\n Country\n </label>\n <input\n id=\"studio-country\"\n type=\"text\"\n value={country}\n onChange={(e) => setCountry(e.target.value)}\n placeholder=\"US\"\n className={fieldClass}\n />\n </div>\n </div>\n <div>\n <label htmlFor=\"studio-website\" className={labelClass}>\n Website\n </label>\n <input\n id=\"studio-website\"\n type=\"url\"\n value={websiteUrl}\n onChange={(e) => setWebsiteUrl(e.target.value)}\n placeholder=\"https://example.com\"\n className={fieldClass}\n />\n </div>\n <div>\n <label htmlFor=\"studio-desc\" className={labelClass}>\n Description\n </label>\n <textarea\n id=\"studio-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n className={fieldClass}\n />\n </div>\n <button\n type=\"submit\"\n disabled={createStudio.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 {createStudio.isPending ? 'Creating…' : 'Create Studio'}\n </button>\n </form>\n </GlassCard>\n )}\n\n {isLoading ? (\n <LoadingState label=\"Loading studios…\" />\n ) : isError ? (\n <ErrorState onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n <EmptyState\n icon={<Building2 className=\"h-12 w-12\" />}\n title=\"No studios yet\"\n description=\"Create your first studio to start organizing your catalog.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {items.map((studio) => (\n <GlassCard\n key={studio.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 <h3 className=\"flex items-center gap-2 text-sm font-semibold text-text-primary\">\n {studio.name}\n {studio.isVerified && (\n <CheckCircle2 className=\"h-4 w-4 text-status-success-text\" />\n )}\n </h3>\n </div>\n {studio.country && (\n <p className=\"mt-1 text-xs text-text-secondary\">{studio.country}</p>\n )}\n {studio.description && (\n <p className=\"mt-2 line-clamp-2 text-xs text-text-secondary\">\n {studio.description}\n </p>\n )}\n {studio.websiteUrl && (\n <a\n href={studio.websiteUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"mt-3 inline-flex items-center gap-1 text-xs font-medium text-action-primary-bg hover:opacity-80\"\n >\n <Globe className=\"h-3 w-3\" />\n Website\n </a>\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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"}
|