@burdenoff/microfe-hookmovies 2026.731.1 → 2026.801.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/HookMoviesRoutes.js +1 -1
- package/dist/hookmovies/HookMoviesRoutes.js.map +1 -1
- package/dist/hookmovies/context/HookMoviesContext.js +1 -1
- package/dist/hookmovies/context/HookMoviesContext.js.map +1 -1
- package/dist/hookmovies/pages/auditions/AuditionCreatePage.js +1 -1
- package/dist/hookmovies/pages/auditions/AuditionCreatePage.js.map +1 -1
- package/dist/hookmovies/pages/auditions/AuditionDetailPage.js +1 -1
- package/dist/hookmovies/pages/auditions/AuditionDetailPage.js.map +1 -1
- package/dist/hookmovies/pages/catalog/TitleDetailPage.js +1 -1
- package/dist/hookmovies/pages/catalog/TitleDetailPage.js.map +1 -1
- package/dist/hookmovies/pages/commerce/CheckoutPage.js +1 -1
- package/dist/hookmovies/pages/commerce/CheckoutPage.js.map +1 -1
- package/dist/hookmovies/pages/consumer/PlayerPage.js +1 -1
- package/dist/hookmovies/pages/consumer/PlayerPage.js.map +1 -1
- package/dist/hookmovies/pages/consumer/TitleViewPage.js +1 -1
- package/dist/hookmovies/pages/consumer/TitleViewPage.js.map +1 -1
- package/dist/hookmovies/pages/localization/LocalizationPage.js +1 -1
- package/dist/hookmovies/pages/localization/LocalizationPage.js.map +1 -1
- package/dist/hookmovies/pages/productions/ProductionDetailPage.js +1 -1
- package/dist/hookmovies/pages/productions/ProductionDetailPage.js.map +1 -1
- package/dist/hookmovies/pages/social/WatchPartyRoomPage.js +1 -1
- package/dist/hookmovies/pages/social/WatchPartyRoomPage.js.map +1 -1
- package/package.json +4 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useHookMovies as e } from "./context/HookMoviesContext.js";
|
|
2
2
|
import { HomePage as t } from "./pages/HomePage.js";
|
|
3
3
|
import { Suspense as n, lazy as r } from "react";
|
|
4
|
-
import { Navigate as i, Route as a, Routes as o, useLocation as s } from "react-router
|
|
4
|
+
import { Navigate as i, Route as a, Routes as o, useLocation as s } from "react-router";
|
|
5
5
|
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
6
6
|
import { FeatureGate as u } from "@burdenoff/fe-libs/shared/feature-flags";
|
|
7
7
|
//#region src/hookmovies/HookMoviesRoutes.tsx
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HookMoviesRoutes.js","names":[],"sources":["../../src/hookmovies/HookMoviesRoutes.tsx"],"sourcesContent":["/**\n * HookMovies Routes\n * @module hookmovies\n *\n * Real domain routes for the HookMovies workspace product: catalog (titles,\n * studios, people), production lifecycle, casting auditions, audience\n * engagement (reviews), rights & distribution, and the user library (lists +\n * entitlements). All data comes from the live `hookmovies` subgraph.\n */\n\nimport { type FC, lazy, Suspense, type ReactNode, type ReactElement } from 'react';\nimport { Routes, Route, Navigate, useLocation } from 'react-router-dom';\nimport { FeatureGate } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { useHookMovies } from './context/HookMoviesContext';\n\n// Eager — primary landing\nimport { HomePage } from './pages/HomePage';\n\n// Catalog\nconst TitlesPage = lazy(() =>\n import('./pages/catalog/TitlesPage').then((m) => ({ default: m.TitlesPage }))\n);\nconst TitleCreatePage = lazy(() =>\n import('./pages/catalog/TitleCreatePage').then((m) => ({ default: m.TitleCreatePage }))\n);\nconst TitleDetailPage = lazy(() =>\n import('./pages/catalog/TitleDetailPage').then((m) => ({ default: m.TitleDetailPage }))\n);\nconst StudiosPage = lazy(() =>\n import('./pages/catalog/StudiosPage').then((m) => ({ default: m.StudiosPage }))\n);\nconst PeoplePage = lazy(() =>\n import('./pages/catalog/PeoplePage').then((m) => ({ default: m.PeoplePage }))\n);\n\n// Productions\nconst ProductionsPage = lazy(() =>\n import('./pages/productions/ProductionsPage').then((m) => ({ default: m.ProductionsPage }))\n);\nconst ProductionCreatePage = lazy(() =>\n import('./pages/productions/ProductionCreatePage').then((m) => ({\n default: m.ProductionCreatePage,\n }))\n);\nconst ProductionDetailPage = lazy(() =>\n import('./pages/productions/ProductionDetailPage').then((m) => ({\n default: m.ProductionDetailPage,\n }))\n);\n\n// Auditions\nconst AuditionsPage = lazy(() =>\n import('./pages/auditions/AuditionsPage').then((m) => ({ default: m.AuditionsPage }))\n);\nconst AuditionCreatePage = lazy(() =>\n import('./pages/auditions/AuditionCreatePage').then((m) => ({ default: m.AuditionCreatePage }))\n);\nconst AuditionDetailPage = lazy(() =>\n import('./pages/auditions/AuditionDetailPage').then((m) => ({ default: m.AuditionDetailPage }))\n);\n\n// Engagement\nconst ReviewsPage = lazy(() =>\n import('./pages/engagement/ReviewsPage').then((m) => ({ default: m.ReviewsPage }))\n);\n\n// Distribution\nconst RightsWindowsPage = lazy(() =>\n import('./pages/distribution/RightsWindowsPage').then((m) => ({ default: m.RightsWindowsPage }))\n);\n\n// Library\nconst ListsPage = lazy(() =>\n import('./pages/library/ListsPage').then((m) => ({ default: m.ListsPage }))\n);\nconst EntitlementsPage = lazy(() =>\n import('./pages/library/EntitlementsPage').then((m) => ({ default: m.EntitlementsPage }))\n);\n\n// Consumer (audience-facing) — discovery, title view, playback, hooks feed\nconst DiscoveryPage = lazy(() =>\n import('./pages/consumer/DiscoveryPage').then((m) => ({ default: m.DiscoveryPage }))\n);\nconst TitleViewPage = lazy(() =>\n import('./pages/consumer/TitleViewPage').then((m) => ({ default: m.TitleViewPage }))\n);\nconst PlayerPage = lazy(() =>\n import('./pages/consumer/PlayerPage').then((m) => ({ default: m.PlayerPage }))\n);\nconst HooksPage = lazy(() =>\n import('./pages/consumer/HooksPage').then((m) => ({ default: m.HooksPage }))\n);\n\n// Social — viewing profiles & synchronized watch parties\nconst ProfilesPage = lazy(() =>\n import('./pages/social/ProfilesPage').then((m) => ({ default: m.ProfilesPage }))\n);\nconst WatchPartyListPage = lazy(() =>\n import('./pages/social/WatchPartyListPage').then((m) => ({ default: m.WatchPartyListPage }))\n);\nconst WatchPartyRoomPage = lazy(() =>\n import('./pages/social/WatchPartyRoomPage').then((m) => ({ default: m.WatchPartyRoomPage }))\n);\n\n// Commerce — checkout (TVOD purchase / rental) & creator payouts\nconst CheckoutPage = lazy(() =>\n import('./pages/commerce/CheckoutPage').then((m) => ({ default: m.CheckoutPage }))\n);\nconst PayoutsPage = lazy(() =>\n import('./pages/analytics/PayoutsPage').then((m) => ({ default: m.PayoutsPage }))\n);\n\n// Analytics — creator / operator performance dashboards\nconst AnalyticsPage = lazy(() =>\n import('./pages/analytics/AnalyticsPage').then((m) => ({ default: m.AnalyticsPage }))\n);\n\n// Trust & safety — moderation queue\nconst ModerationPage = lazy(() =>\n import('./pages/trust/ModerationPage').then((m) => ({ default: m.ModerationPage }))\n);\n\n// Onboarding — language / content preferences\nconst OnboardingPage = lazy(() =>\n import('./pages/onboarding/OnboardingPage').then((m) => ({ default: m.OnboardingPage }))\n);\n\n// Developer portal — API keys\nconst DevPortalPage = lazy(() =>\n import('./pages/developer/DevPortalPage').then((m) => ({ default: m.DevPortalPage }))\n);\n\n// Live — FAST linear channels & schedules\nconst LivePage = lazy(() => import('./pages/live/LivePage').then((m) => ({ default: m.LivePage })));\n\n// Localization — per-title subtitle / caption / dub track management\nconst LocalizationPage = lazy(() =>\n import('./pages/localization/LocalizationPage').then((m) => ({ default: m.LocalizationPage }))\n);\n\n// AI — concierge / recommendations assistant\nconst ConciergePage = lazy(() =>\n import('./pages/ai/ConciergePage').then((m) => ({ default: m.ConciergePage }))\n);\n\nconst PageLoader: FC = () => (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <div className=\"h-8 w-8 animate-spin rounded-full border-b-2 border-action-primary-bg\" />\n </div>\n);\n\nconst FeatureDisabledFallback: FC = () => (\n <div className=\"flex min-h-[400px] flex-col items-center justify-center gap-2 px-6 text-center\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Coming soon</h2>\n <p className=\"max-w-sm text-sm text-text-secondary\">\n This feature is currently disabled for this workspace.\n </p>\n </div>\n);\n\n/**\n * Wrap a route element in a `FeatureGate` with the shared \"coming soon\"\n * fallback. `defaultValue={true}` keeps already-shipped screens visible\n * before their flag is seeded — seeding must never hide a working feature.\n */\nfunction gated(flag: string, element: ReactNode): ReactElement {\n return (\n <FeatureGate flag={flag} defaultValue={true} fallback={<FeatureDisabledFallback />}>\n {element}\n </FeatureGate>\n );\n}\n\nexport const HookMoviesRoutes: FC = () => {\n const { basePath, isStandalone } = useHookMovies();\n const location = useLocation();\n\n const normalizedBasePath = basePath === '/' ? '' : (basePath ?? '').replace(/\\/$/, '');\n\n if (normalizedBasePath && !isStandalone) {\n const pathMatches =\n location.pathname === normalizedBasePath ||\n location.pathname.startsWith(normalizedBasePath + '/');\n\n if (!pathMatches) {\n return null;\n }\n }\n\n return (\n <div className=\"flex min-h-full flex-col\">\n <Suspense fallback={<PageLoader />}>\n <Routes>\n {/* Overview / dashboard */}\n <Route index element={<HomePage />} />\n\n {/* Catalog */}\n <Route path=\"catalog/titles\" element={<TitlesPage />} />\n <Route path=\"catalog/titles/new\" element={<TitleCreatePage />} />\n <Route path=\"catalog/titles/:id\" element={<TitleDetailPage />} />\n <Route path=\"catalog/studios\" element={<StudiosPage />} />\n <Route path=\"catalog/people\" element={<PeoplePage />} />\n\n {/* Productions */}\n <Route path=\"productions\" element={<ProductionsPage />} />\n <Route path=\"productions/new\" element={<ProductionCreatePage />} />\n <Route path=\"productions/:id\" element={<ProductionDetailPage />} />\n\n {/* Auditions */}\n <Route path=\"auditions\" element={<AuditionsPage />} />\n <Route path=\"auditions/new\" element={<AuditionCreatePage />} />\n <Route path=\"auditions/:id\" element={<AuditionDetailPage />} />\n\n {/* Engagement */}\n <Route path=\"engagement/reviews\" element={<ReviewsPage />} />\n\n {/* Distribution */}\n <Route path=\"distribution/rights\" element={<RightsWindowsPage />} />\n\n {/* Library */}\n <Route path=\"library/lists\" element={<ListsPage />} />\n <Route path=\"library/entitlements\" element={<EntitlementsPage />} />\n\n {/* Consumer (audience-facing) */}\n <Route path=\"browse\" element={<DiscoveryPage />} />\n <Route path=\"title/:id\" element={<TitleViewPage />} />\n <Route path=\"watch/:titleId\" element={<PlayerPage />} />\n <Route path=\"watch/:titleId/episode/:episodeId\" element={<PlayerPage />} />\n <Route path=\"hooks\" element={gated('hookmovies.hooks.enabled', <HooksPage />)} />\n\n {/* Social — profiles & watch parties */}\n <Route path=\"profiles\" element={<ProfilesPage />} />\n <Route path=\"watch-party\" element={<WatchPartyListPage />} />\n <Route path=\"watch-party/:id\" element={<WatchPartyRoomPage />} />\n\n {/* Commerce — checkout & payouts */}\n <Route path=\"checkout/:titleId\" element={<CheckoutPage />} />\n <Route path=\"payouts\" element={<PayoutsPage />} />\n\n {/* Analytics */}\n <Route path=\"analytics\" element={<AnalyticsPage />} />\n\n {/* Trust & safety */}\n <Route path=\"trust/moderation\" element={<ModerationPage />} />\n\n {/* Onboarding */}\n <Route path=\"onboarding/languages\" element={<OnboardingPage />} />\n\n {/* Developer portal */}\n <Route path=\"developer\" element={<DevPortalPage />} />\n\n {/* Live — FAST channels */}\n <Route path=\"live\" element={gated('hookmovies.fast.enabled', <LivePage />)} />\n\n {/* Localization */}\n <Route path=\"title/:id/localization\" element={<LocalizationPage />} />\n\n {/* AI concierge */}\n <Route path=\"concierge\" element={gated('hookmovies.ai.concierge', <ConciergePage />)} />\n\n {/* Fallback */}\n <Route path=\"*\" element={<Navigate to=\"\" replace />} />\n </Routes>\n </Suspense>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"HookMoviesRoutes.js","names":[],"sources":["../../src/hookmovies/HookMoviesRoutes.tsx"],"sourcesContent":["/**\n * HookMovies Routes\n * @module hookmovies\n *\n * Real domain routes for the HookMovies workspace product: catalog (titles,\n * studios, people), production lifecycle, casting auditions, audience\n * engagement (reviews), rights & distribution, and the user library (lists +\n * entitlements). All data comes from the live `hookmovies` subgraph.\n */\n\nimport { type FC, lazy, Suspense, type ReactNode, type ReactElement } from 'react';\nimport { Routes, Route, Navigate, useLocation } from 'react-router';\nimport { FeatureGate } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { useHookMovies } from './context/HookMoviesContext';\n\n// Eager — primary landing\nimport { HomePage } from './pages/HomePage';\n\n// Catalog\nconst TitlesPage = lazy(() =>\n import('./pages/catalog/TitlesPage').then((m) => ({ default: m.TitlesPage }))\n);\nconst TitleCreatePage = lazy(() =>\n import('./pages/catalog/TitleCreatePage').then((m) => ({ default: m.TitleCreatePage }))\n);\nconst TitleDetailPage = lazy(() =>\n import('./pages/catalog/TitleDetailPage').then((m) => ({ default: m.TitleDetailPage }))\n);\nconst StudiosPage = lazy(() =>\n import('./pages/catalog/StudiosPage').then((m) => ({ default: m.StudiosPage }))\n);\nconst PeoplePage = lazy(() =>\n import('./pages/catalog/PeoplePage').then((m) => ({ default: m.PeoplePage }))\n);\n\n// Productions\nconst ProductionsPage = lazy(() =>\n import('./pages/productions/ProductionsPage').then((m) => ({ default: m.ProductionsPage }))\n);\nconst ProductionCreatePage = lazy(() =>\n import('./pages/productions/ProductionCreatePage').then((m) => ({\n default: m.ProductionCreatePage,\n }))\n);\nconst ProductionDetailPage = lazy(() =>\n import('./pages/productions/ProductionDetailPage').then((m) => ({\n default: m.ProductionDetailPage,\n }))\n);\n\n// Auditions\nconst AuditionsPage = lazy(() =>\n import('./pages/auditions/AuditionsPage').then((m) => ({ default: m.AuditionsPage }))\n);\nconst AuditionCreatePage = lazy(() =>\n import('./pages/auditions/AuditionCreatePage').then((m) => ({ default: m.AuditionCreatePage }))\n);\nconst AuditionDetailPage = lazy(() =>\n import('./pages/auditions/AuditionDetailPage').then((m) => ({ default: m.AuditionDetailPage }))\n);\n\n// Engagement\nconst ReviewsPage = lazy(() =>\n import('./pages/engagement/ReviewsPage').then((m) => ({ default: m.ReviewsPage }))\n);\n\n// Distribution\nconst RightsWindowsPage = lazy(() =>\n import('./pages/distribution/RightsWindowsPage').then((m) => ({ default: m.RightsWindowsPage }))\n);\n\n// Library\nconst ListsPage = lazy(() =>\n import('./pages/library/ListsPage').then((m) => ({ default: m.ListsPage }))\n);\nconst EntitlementsPage = lazy(() =>\n import('./pages/library/EntitlementsPage').then((m) => ({ default: m.EntitlementsPage }))\n);\n\n// Consumer (audience-facing) — discovery, title view, playback, hooks feed\nconst DiscoveryPage = lazy(() =>\n import('./pages/consumer/DiscoveryPage').then((m) => ({ default: m.DiscoveryPage }))\n);\nconst TitleViewPage = lazy(() =>\n import('./pages/consumer/TitleViewPage').then((m) => ({ default: m.TitleViewPage }))\n);\nconst PlayerPage = lazy(() =>\n import('./pages/consumer/PlayerPage').then((m) => ({ default: m.PlayerPage }))\n);\nconst HooksPage = lazy(() =>\n import('./pages/consumer/HooksPage').then((m) => ({ default: m.HooksPage }))\n);\n\n// Social — viewing profiles & synchronized watch parties\nconst ProfilesPage = lazy(() =>\n import('./pages/social/ProfilesPage').then((m) => ({ default: m.ProfilesPage }))\n);\nconst WatchPartyListPage = lazy(() =>\n import('./pages/social/WatchPartyListPage').then((m) => ({ default: m.WatchPartyListPage }))\n);\nconst WatchPartyRoomPage = lazy(() =>\n import('./pages/social/WatchPartyRoomPage').then((m) => ({ default: m.WatchPartyRoomPage }))\n);\n\n// Commerce — checkout (TVOD purchase / rental) & creator payouts\nconst CheckoutPage = lazy(() =>\n import('./pages/commerce/CheckoutPage').then((m) => ({ default: m.CheckoutPage }))\n);\nconst PayoutsPage = lazy(() =>\n import('./pages/analytics/PayoutsPage').then((m) => ({ default: m.PayoutsPage }))\n);\n\n// Analytics — creator / operator performance dashboards\nconst AnalyticsPage = lazy(() =>\n import('./pages/analytics/AnalyticsPage').then((m) => ({ default: m.AnalyticsPage }))\n);\n\n// Trust & safety — moderation queue\nconst ModerationPage = lazy(() =>\n import('./pages/trust/ModerationPage').then((m) => ({ default: m.ModerationPage }))\n);\n\n// Onboarding — language / content preferences\nconst OnboardingPage = lazy(() =>\n import('./pages/onboarding/OnboardingPage').then((m) => ({ default: m.OnboardingPage }))\n);\n\n// Developer portal — API keys\nconst DevPortalPage = lazy(() =>\n import('./pages/developer/DevPortalPage').then((m) => ({ default: m.DevPortalPage }))\n);\n\n// Live — FAST linear channels & schedules\nconst LivePage = lazy(() => import('./pages/live/LivePage').then((m) => ({ default: m.LivePage })));\n\n// Localization — per-title subtitle / caption / dub track management\nconst LocalizationPage = lazy(() =>\n import('./pages/localization/LocalizationPage').then((m) => ({ default: m.LocalizationPage }))\n);\n\n// AI — concierge / recommendations assistant\nconst ConciergePage = lazy(() =>\n import('./pages/ai/ConciergePage').then((m) => ({ default: m.ConciergePage }))\n);\n\nconst PageLoader: FC = () => (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <div className=\"h-8 w-8 animate-spin rounded-full border-b-2 border-action-primary-bg\" />\n </div>\n);\n\nconst FeatureDisabledFallback: FC = () => (\n <div className=\"flex min-h-[400px] flex-col items-center justify-center gap-2 px-6 text-center\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Coming soon</h2>\n <p className=\"max-w-sm text-sm text-text-secondary\">\n This feature is currently disabled for this workspace.\n </p>\n </div>\n);\n\n/**\n * Wrap a route element in a `FeatureGate` with the shared \"coming soon\"\n * fallback. `defaultValue={true}` keeps already-shipped screens visible\n * before their flag is seeded — seeding must never hide a working feature.\n */\nfunction gated(flag: string, element: ReactNode): ReactElement {\n return (\n <FeatureGate flag={flag} defaultValue={true} fallback={<FeatureDisabledFallback />}>\n {element}\n </FeatureGate>\n );\n}\n\nexport const HookMoviesRoutes: FC = () => {\n const { basePath, isStandalone } = useHookMovies();\n const location = useLocation();\n\n const normalizedBasePath = basePath === '/' ? '' : (basePath ?? '').replace(/\\/$/, '');\n\n if (normalizedBasePath && !isStandalone) {\n const pathMatches =\n location.pathname === normalizedBasePath ||\n location.pathname.startsWith(normalizedBasePath + '/');\n\n if (!pathMatches) {\n return null;\n }\n }\n\n return (\n <div className=\"flex min-h-full flex-col\">\n <Suspense fallback={<PageLoader />}>\n <Routes>\n {/* Overview / dashboard */}\n <Route index element={<HomePage />} />\n\n {/* Catalog */}\n <Route path=\"catalog/titles\" element={<TitlesPage />} />\n <Route path=\"catalog/titles/new\" element={<TitleCreatePage />} />\n <Route path=\"catalog/titles/:id\" element={<TitleDetailPage />} />\n <Route path=\"catalog/studios\" element={<StudiosPage />} />\n <Route path=\"catalog/people\" element={<PeoplePage />} />\n\n {/* Productions */}\n <Route path=\"productions\" element={<ProductionsPage />} />\n <Route path=\"productions/new\" element={<ProductionCreatePage />} />\n <Route path=\"productions/:id\" element={<ProductionDetailPage />} />\n\n {/* Auditions */}\n <Route path=\"auditions\" element={<AuditionsPage />} />\n <Route path=\"auditions/new\" element={<AuditionCreatePage />} />\n <Route path=\"auditions/:id\" element={<AuditionDetailPage />} />\n\n {/* Engagement */}\n <Route path=\"engagement/reviews\" element={<ReviewsPage />} />\n\n {/* Distribution */}\n <Route path=\"distribution/rights\" element={<RightsWindowsPage />} />\n\n {/* Library */}\n <Route path=\"library/lists\" element={<ListsPage />} />\n <Route path=\"library/entitlements\" element={<EntitlementsPage />} />\n\n {/* Consumer (audience-facing) */}\n <Route path=\"browse\" element={<DiscoveryPage />} />\n <Route path=\"title/:id\" element={<TitleViewPage />} />\n <Route path=\"watch/:titleId\" element={<PlayerPage />} />\n <Route path=\"watch/:titleId/episode/:episodeId\" element={<PlayerPage />} />\n <Route path=\"hooks\" element={gated('hookmovies.hooks.enabled', <HooksPage />)} />\n\n {/* Social — profiles & watch parties */}\n <Route path=\"profiles\" element={<ProfilesPage />} />\n <Route path=\"watch-party\" element={<WatchPartyListPage />} />\n <Route path=\"watch-party/:id\" element={<WatchPartyRoomPage />} />\n\n {/* Commerce — checkout & payouts */}\n <Route path=\"checkout/:titleId\" element={<CheckoutPage />} />\n <Route path=\"payouts\" element={<PayoutsPage />} />\n\n {/* Analytics */}\n <Route path=\"analytics\" element={<AnalyticsPage />} />\n\n {/* Trust & safety */}\n <Route path=\"trust/moderation\" element={<ModerationPage />} />\n\n {/* Onboarding */}\n <Route path=\"onboarding/languages\" element={<OnboardingPage />} />\n\n {/* Developer portal */}\n <Route path=\"developer\" element={<DevPortalPage />} />\n\n {/* Live — FAST channels */}\n <Route path=\"live\" element={gated('hookmovies.fast.enabled', <LivePage />)} />\n\n {/* Localization */}\n <Route path=\"title/:id/localization\" element={<LocalizationPage />} />\n\n {/* AI concierge */}\n <Route path=\"concierge\" element={gated('hookmovies.ai.concierge', <ConciergePage />)} />\n\n {/* Fallback */}\n <Route path=\"*\" element={<Navigate to=\"\" replace />} />\n </Routes>\n </Suspense>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { joinPath as e } from "../utils/navigation.js";
|
|
2
2
|
import { createContext as t, useCallback as n, useContext as r, useMemo as i, useState as a } from "react";
|
|
3
|
-
import { MemoryRouter as o, useNavigate as s } from "react-router
|
|
3
|
+
import { MemoryRouter as o, useNavigate as s } from "react-router";
|
|
4
4
|
import { jsx as c } from "react/jsx-runtime";
|
|
5
5
|
//#region src/hookmovies/context/HookMoviesContext.tsx
|
|
6
6
|
var l = t(null), u = (e) => `hookmovies:activeProfile:${e ?? "default"}`, d = (e) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HookMoviesContext.js","names":[],"sources":["../../../src/hookmovies/context/HookMoviesContext.tsx"],"sourcesContent":["/**\n * HookMovies Context Provider\n * @module hookmovies/context\n */\n\nimport type { FC, PropsWithChildren } from 'react';\nimport { createContext, useContext, useMemo, useCallback, useState } from 'react';\nimport { MemoryRouter, useNavigate as useRouterNavigate } from 'react-router
|
|
1
|
+
{"version":3,"file":"HookMoviesContext.js","names":[],"sources":["../../../src/hookmovies/context/HookMoviesContext.tsx"],"sourcesContent":["/**\n * HookMovies Context Provider\n * @module hookmovies/context\n */\n\nimport type { FC, PropsWithChildren } from 'react';\nimport { createContext, useContext, useMemo, useCallback, useState } from 'react';\nimport { MemoryRouter, useNavigate as useRouterNavigate } from 'react-router';\nimport type { HookMoviesRootProps } from '../types';\nimport { joinPath } from '../utils/navigation';\n\n// ============================================================================\n// Context\n// ============================================================================\n\ninterface HookMoviesContextValue extends Omit<\n HookMoviesRootProps,\n 'registerNavItems' | 'registerFooterItems' | 'navigate'\n> {\n isStandalone: boolean;\n /** Shell's navigate function (only available in integrated mode) */\n navigate?: (path: string) => void;\n /** Navigate to a path relative to basePath */\n navigateTo: (relativePath: string) => void;\n /** Get the full path for a relative path */\n getPath: (relativePath: string) => string;\n /**\n * Id of the currently selected viewing profile (Netflix-style per-account\n * profile), or `null` when none is chosen. Persisted to localStorage,\n * scoped per workspace. Consumer surfaces gate on this (e.g. the player and\n * discovery use it to apply the profile's language / maturity preferences).\n */\n activeProfileId: string | null;\n /** Select (or clear, with `null`) the active viewing profile; persists it. */\n setActiveProfile: (profileId: string | null) => void;\n}\n\nconst HookMoviesContext = createContext<HookMoviesContextValue | null>(null);\n\n// ============================================================================\n// Active profile persistence (localStorage, workspace-scoped)\n// ============================================================================\n\nconst activeProfileStorageKey = (workspaceId?: string): string =>\n `hookmovies:activeProfile:${workspaceId ?? 'default'}`;\n\nconst readStoredActiveProfile = (workspaceId?: string): string | null => {\n if (typeof window === 'undefined') return null;\n try {\n return window.localStorage.getItem(activeProfileStorageKey(workspaceId));\n } catch {\n return null;\n }\n};\n\n/**\n * State + persistence for the active viewing profile. Reads the initial value\n * from localStorage (per workspace) and writes through on every change.\n */\nfunction useActiveProfileState(\n workspaceId?: string\n): [string | null, (profileId: string | null) => void] {\n const [activeProfileId, setActiveProfileId] = useState<string | null>(() =>\n readStoredActiveProfile(workspaceId)\n );\n\n const setActiveProfile = useCallback(\n (profileId: string | null) => {\n setActiveProfileId(profileId);\n if (typeof window === 'undefined') return;\n try {\n const key = activeProfileStorageKey(workspaceId);\n if (profileId) {\n window.localStorage.setItem(key, profileId);\n } else {\n window.localStorage.removeItem(key);\n }\n } catch {\n // Ignore storage failures (private mode / quota) — in-memory state still works.\n }\n },\n [workspaceId]\n );\n\n return [activeProfileId, setActiveProfile];\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport function useHookMovies(): HookMoviesContextValue {\n const context = useContext(HookMoviesContext);\n if (!context) {\n throw new Error('useHookMovies must be used within a HookMoviesProvider');\n }\n return context;\n}\n\n// ============================================================================\n// Provider Component\n// ============================================================================\n\ntype ProviderProps = Omit<HookMoviesRootProps, 'registerNavItems' | 'registerFooterItems'> & {\n isStandalone: boolean;\n};\n\n/** Provider for standalone mode (has access to MemoryRouter) */\nconst StandaloneProviderInner: FC<PropsWithChildren<ProviderProps>> = ({\n children,\n basePath,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n}) => {\n const routerNavigate = useRouterNavigate();\n const effectiveBasePath = basePath || '/';\n const [activeProfileId, setActiveProfile] = useActiveProfileState(workspaceId);\n\n const getPath = useCallback(\n (relativePath: string) => joinPath(effectiveBasePath, relativePath),\n [effectiveBasePath]\n );\n\n const navigateTo = useCallback(\n (relativePath: string) => {\n const fullPath = joinPath(effectiveBasePath, relativePath);\n routerNavigate(fullPath);\n },\n [effectiveBasePath, routerNavigate]\n );\n\n const contextValue = useMemo<HookMoviesContextValue>(\n () => ({\n basePath: effectiveBasePath,\n navigate: undefined,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n isStandalone: true,\n navigateTo,\n getPath,\n activeProfileId,\n setActiveProfile,\n }),\n [\n effectiveBasePath,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n navigateTo,\n getPath,\n activeProfileId,\n setActiveProfile,\n ]\n );\n\n return <HookMoviesContext.Provider value={contextValue}>{children}</HookMoviesContext.Provider>;\n};\n\n/** Provider for integrated mode (shell provides navigate) */\nconst IntegratedProviderInner: FC<\n PropsWithChildren<ProviderProps & { navigate: (path: string) => void }>\n> = ({\n children,\n basePath,\n navigate,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n}) => {\n const effectiveBasePath = basePath || '/';\n const [activeProfileId, setActiveProfile] = useActiveProfileState(workspaceId);\n\n const getPath = useCallback(\n (relativePath: string) => joinPath(effectiveBasePath, relativePath),\n [effectiveBasePath]\n );\n\n const navigateTo = useCallback(\n (relativePath: string) => {\n const fullPath = joinPath(effectiveBasePath, relativePath);\n navigate(fullPath);\n },\n [effectiveBasePath, navigate]\n );\n\n const contextValue = useMemo<HookMoviesContextValue>(\n () => ({\n basePath: effectiveBasePath,\n navigate,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n isStandalone: false,\n navigateTo,\n getPath,\n activeProfileId,\n setActiveProfile,\n }),\n [\n effectiveBasePath,\n navigate,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n navigateTo,\n getPath,\n activeProfileId,\n setActiveProfile,\n ]\n );\n\n return <HookMoviesContext.Provider value={contextValue}>{children}</HookMoviesContext.Provider>;\n};\n\nexport const HookMoviesProvider: FC<PropsWithChildren<HookMoviesRootProps>> = (props) => {\n const { children, navigate } = props;\n // Standalone mode: no navigate function provided (MFE manages its own router)\n // Integrated mode: navigate function provided by shell (shell provides router)\n const isStandalone = !navigate;\n\n // Standalone mode: wrap in MemoryRouter\n if (isStandalone) {\n return (\n <MemoryRouter>\n <StandaloneProviderInner {...props} isStandalone={true}>\n {children}\n </StandaloneProviderInner>\n </MemoryRouter>\n );\n }\n\n // Integrated mode: shell provides router\n return (\n <IntegratedProviderInner {...props} navigate={navigate} isStandalone={false}>\n {children}\n </IntegratedProviderInner>\n );\n};\n"],"mappings":";;;;;AAqCA,IAAM,IAAoB,EAA6C,KAAK,EAMtE,KAA2B,MAC/B,4BAA4B,KAAe,aAEvC,KAA2B,MAAwC;AACvE,KAAI,OAAO,SAAW,IAAa,QAAO;AAC1C,KAAI;AACF,SAAO,OAAO,aAAa,QAAQ,EAAwB,EAAY,CAAC;SAClE;AACN,SAAO;;;AAQX,SAAS,EACP,GACqD;CACrD,IAAM,CAAC,GAAiB,KAAsB,QAC5C,EAAwB,EAAY,CACrC;AAoBD,QAAO,CAAC,GAlBiB,GACtB,MAA6B;AAC5B,QAAmB,EAAU,EACzB,SAAO,SAAW,KACtB,KAAI;GACF,IAAM,IAAM,EAAwB,EAAY;AAChD,GAAI,IACF,OAAO,aAAa,QAAQ,GAAK,EAAU,GAE3C,OAAO,aAAa,WAAW,EAAI;UAE/B;IAIV,CAAC,EAAY,CACd,CAEyC;;AAO5C,SAAgB,IAAwC;CACtD,IAAM,IAAU,EAAW,EAAkB;AAC7C,KAAI,CAAC,EACH,OAAU,MAAM,yDAAyD;AAE3E,QAAO;;AAYT,IAAM,KAAiE,EACrE,aACA,aACA,gBACA,aACA,gBACA,kBACA,mBACI;CACJ,IAAM,IAAiB,GAAmB,EACpC,IAAoB,KAAY,KAChC,CAAC,GAAiB,KAAoB,EAAsB,EAAY,EAExE,IAAU,GACb,MAAyB,EAAS,GAAmB,EAAa,EACnE,CAAC,EAAkB,CACpB,EAEK,IAAa,GAChB,MAAyB;AAExB,IADiB,EAAS,GAAmB,EAAa,CAClC;IAE1B,CAAC,GAAmB,EAAe,CACpC,EAEK,IAAe,SACZ;EACL,UAAU;EACV,UAAU,KAAA;EACV;EACA;EACA;EACA;EACA;EACA,cAAc;EACd;EACA;EACA;EACA;EACD,GACD;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;AAED,QAAO,kBAAC,EAAkB,UAAnB;EAA4B,OAAO;EAAe;EAAsC,CAAA;GAI3F,KAED,EACH,aACA,aACA,aACA,gBACA,aACA,gBACA,kBACA,mBACI;CACJ,IAAM,IAAoB,KAAY,KAChC,CAAC,GAAiB,KAAoB,EAAsB,EAAY,EAExE,IAAU,GACb,MAAyB,EAAS,GAAmB,EAAa,EACnE,CAAC,EAAkB,CACpB,EAEK,IAAa,GAChB,MAAyB;AAExB,IADiB,EAAS,GAAmB,EAAa,CACxC;IAEpB,CAAC,GAAmB,EAAS,CAC9B,EAEK,IAAe,SACZ;EACL,UAAU;EACV;EACA;EACA;EACA;EACA;EACA;EACA,cAAc;EACd;EACA;EACA;EACA;EACD,GACD;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;AAED,QAAO,kBAAC,EAAkB,UAAnB;EAA4B,OAAO;EAAe;EAAsC,CAAA;GAGpF,KAAkE,MAAU;CACvF,IAAM,EAAE,aAAU,gBAAa;AAiB/B,QAdsB,IAepB,kBAAC,GAAD;EAAyB,GAAI;EAAiB;EAAU,cAAc;EACnE;EACuB,CAAA,GAZxB,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;EAAyB,GAAI;EAAO,cAAc;EAC/C;EACuB,CAAA,EACb,CAAA"}
|
|
@@ -3,7 +3,7 @@ import { PageLayout as t } from "../../components/PageLayout.js";
|
|
|
3
3
|
import { useCreateAudition as n, useProductions as r } from "../../hooks/useHookMoviesApi.js";
|
|
4
4
|
import { useState as i } from "react";
|
|
5
5
|
import { ArrowLeft as a, Plus as o } from "lucide-react";
|
|
6
|
-
import { useSearchParams as s } from "react-router
|
|
6
|
+
import { useSearchParams as s } from "react-router";
|
|
7
7
|
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
8
8
|
//#region src/hookmovies/pages/auditions/AuditionCreatePage.tsx
|
|
9
9
|
var u = "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)]", d = "mb-1.5 block text-xs font-medium text-text-secondary", f = () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuditionCreatePage.js","names":[],"sources":["../../../../src/hookmovies/pages/auditions/AuditionCreatePage.tsx"],"sourcesContent":["/**\n * Post Audition / Create Role (Casting marketplace)\n * @module hookmovies/pages/auditions\n *\n * Real create form wired to `hokCreateAudition`. Production is chosen from the\n * live `hokListProductions` list (the role attaches to a production). A\n * `productionId` query param pre-selects the production when linked from a\n * production detail page.\n */\n\nimport { type FC, type FormEvent, useState } from 'react';\nimport { useSearchParams } from 'react-router-dom';\nimport { ArrowLeft, Plus } from 'lucide-react';\nimport { PageLayout } from '../../components/PageLayout';\nimport { useHookMovies } from '../../context/HookMoviesContext';\nimport {\n useCreateAudition,\n useProductions,\n type CreateAuditionInput,\n} from '../../hooks/useHookMoviesApi';\n\nconst fieldClass =\n 'w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\nconst labelClass = 'mb-1.5 block text-xs font-medium text-text-secondary';\n\nexport const AuditionCreatePage: FC = () => {\n const { navigateTo } = useHookMovies();\n const [searchParams] = useSearchParams();\n const createAudition = useCreateAudition();\n const productions = useProductions({}, { limit: 200, offset: 0 });\n\n const [productionId, setProductionId] = useState(searchParams.get('productionId') ?? '');\n const [roleName, setRoleName] = useState('');\n const [description, setDescription] = useState('');\n const [payMajor, setPayMajor] = useState('');\n const [currency, setCurrency] = useState('USD');\n const [location, setLocation] = useState('');\n const [language, setLanguage] = useState('');\n const [isMinorRole, setIsMinorRole] = useState(false);\n const [submissionDeadline, setSubmissionDeadline] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setError(null);\n if (!productionId) {\n setError('A production is required.');\n return;\n }\n if (!roleName.trim()) {\n setError('Role name is required.');\n return;\n }\n let payMinorUnits: string | undefined;\n if (payMajor.trim()) {\n const parsed = Number(payMajor);\n if (!Number.isFinite(parsed) || parsed < 0) {\n setError('Pay must be a non-negative number.');\n return;\n }\n payMinorUnits = String(Math.round(parsed * 100));\n }\n const input: CreateAuditionInput = {\n productionId,\n roleName: roleName.trim(),\n description: description.trim() || undefined,\n payMinorUnits,\n currency: currency.trim() || undefined,\n location: location.trim() || undefined,\n language: language.trim() || undefined,\n isMinorRole,\n submissionDeadline: submissionDeadline\n ? new Date(submissionDeadline).toISOString()\n : undefined,\n };\n try {\n const created = await createAudition.mutateAsync(input);\n navigateTo(`auditions/${created.id}`);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create audition');\n }\n };\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 Post a Role\n </span>\n }\n description=\"Create a casting call attached to a production\"\n >\n <form onSubmit={handleSubmit} className=\"max-w-2xl space-y-5\">\n {error && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text\">\n {error}\n </div>\n )}\n\n <div>\n <label htmlFor=\"aud-production\" className={labelClass}>\n Production *\n </label>\n <select\n id=\"aud-production\"\n value={productionId}\n onChange={(e) => setProductionId(e.target.value)}\n className={fieldClass}\n required\n >\n <option value=\"\">Select a production…</option>\n {(productions.data?.items ?? []).map((p) => (\n <option key={p.id} value={p.id}>\n {p.title}\n </option>\n ))}\n </select>\n </div>\n\n <div>\n <label htmlFor=\"aud-role\" className={labelClass}>\n Role Name *\n </label>\n <input\n id=\"aud-role\"\n type=\"text\"\n value={roleName}\n onChange={(e) => setRoleName(e.target.value)}\n placeholder=\"e.g. Lead — Detective Reyes\"\n className={fieldClass}\n required\n />\n </div>\n\n <div>\n <label htmlFor=\"aud-desc\" className={labelClass}>\n Description\n </label>\n <textarea\n id=\"aud-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={4}\n placeholder=\"Role requirements, character notes, audition instructions…\"\n className={fieldClass}\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"aud-pay\" className={labelClass}>\n Pay\n </label>\n <input\n id=\"aud-pay\"\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n value={payMajor}\n onChange={(e) => setPayMajor(e.target.value)}\n placeholder=\"5000\"\n className={fieldClass}\n />\n </div>\n <div>\n <label htmlFor=\"aud-currency\" className={labelClass}>\n Currency\n </label>\n <input\n id=\"aud-currency\"\n type=\"text\"\n value={currency}\n onChange={(e) => setCurrency(e.target.value)}\n placeholder=\"USD\"\n className={fieldClass}\n />\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"aud-location\" className={labelClass}>\n Location\n </label>\n <input\n id=\"aud-location\"\n type=\"text\"\n value={location}\n onChange={(e) => setLocation(e.target.value)}\n placeholder=\"Remote / Los Angeles, CA\"\n className={fieldClass}\n />\n </div>\n <div>\n <label htmlFor=\"aud-language\" className={labelClass}>\n Language\n </label>\n <input\n id=\"aud-language\"\n type=\"text\"\n value={language}\n onChange={(e) => setLanguage(e.target.value)}\n placeholder=\"en\"\n className={fieldClass}\n />\n </div>\n </div>\n\n <div>\n <label htmlFor=\"aud-deadline\" className={labelClass}>\n Submission Deadline\n </label>\n <input\n id=\"aud-deadline\"\n type=\"date\"\n value={submissionDeadline}\n onChange={(e) => setSubmissionDeadline(e.target.value)}\n className={fieldClass}\n />\n </div>\n\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={isMinorRole}\n onChange={(e) => setIsMinorRole(e.target.checked)}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n This role is for a minor (extra safeguards apply)\n </label>\n\n <div className=\"flex items-center gap-3 pt-2\">\n <button\n type=\"submit\"\n disabled={createAudition.isPending}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50 transition-opacity\"\n >\n <Plus className=\"h-4 w-4\" />\n {createAudition.isPending ? 'Posting…' : 'Post Role'}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo('auditions')}\n className=\"rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n Cancel\n </button>\n </div>\n </form>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AuditionCreatePage.js","names":[],"sources":["../../../../src/hookmovies/pages/auditions/AuditionCreatePage.tsx"],"sourcesContent":["/**\n * Post Audition / Create Role (Casting marketplace)\n * @module hookmovies/pages/auditions\n *\n * Real create form wired to `hokCreateAudition`. Production is chosen from the\n * live `hokListProductions` list (the role attaches to a production). A\n * `productionId` query param pre-selects the production when linked from a\n * production detail page.\n */\n\nimport { type FC, type FormEvent, useState } from 'react';\nimport { useSearchParams } from 'react-router';\nimport { ArrowLeft, Plus } from 'lucide-react';\nimport { PageLayout } from '../../components/PageLayout';\nimport { useHookMovies } from '../../context/HookMoviesContext';\nimport {\n useCreateAudition,\n useProductions,\n type CreateAuditionInput,\n} from '../../hooks/useHookMoviesApi';\n\nconst fieldClass =\n 'w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\nconst labelClass = 'mb-1.5 block text-xs font-medium text-text-secondary';\n\nexport const AuditionCreatePage: FC = () => {\n const { navigateTo } = useHookMovies();\n const [searchParams] = useSearchParams();\n const createAudition = useCreateAudition();\n const productions = useProductions({}, { limit: 200, offset: 0 });\n\n const [productionId, setProductionId] = useState(searchParams.get('productionId') ?? '');\n const [roleName, setRoleName] = useState('');\n const [description, setDescription] = useState('');\n const [payMajor, setPayMajor] = useState('');\n const [currency, setCurrency] = useState('USD');\n const [location, setLocation] = useState('');\n const [language, setLanguage] = useState('');\n const [isMinorRole, setIsMinorRole] = useState(false);\n const [submissionDeadline, setSubmissionDeadline] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setError(null);\n if (!productionId) {\n setError('A production is required.');\n return;\n }\n if (!roleName.trim()) {\n setError('Role name is required.');\n return;\n }\n let payMinorUnits: string | undefined;\n if (payMajor.trim()) {\n const parsed = Number(payMajor);\n if (!Number.isFinite(parsed) || parsed < 0) {\n setError('Pay must be a non-negative number.');\n return;\n }\n payMinorUnits = String(Math.round(parsed * 100));\n }\n const input: CreateAuditionInput = {\n productionId,\n roleName: roleName.trim(),\n description: description.trim() || undefined,\n payMinorUnits,\n currency: currency.trim() || undefined,\n location: location.trim() || undefined,\n language: language.trim() || undefined,\n isMinorRole,\n submissionDeadline: submissionDeadline\n ? new Date(submissionDeadline).toISOString()\n : undefined,\n };\n try {\n const created = await createAudition.mutateAsync(input);\n navigateTo(`auditions/${created.id}`);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create audition');\n }\n };\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 Post a Role\n </span>\n }\n description=\"Create a casting call attached to a production\"\n >\n <form onSubmit={handleSubmit} className=\"max-w-2xl space-y-5\">\n {error && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text\">\n {error}\n </div>\n )}\n\n <div>\n <label htmlFor=\"aud-production\" className={labelClass}>\n Production *\n </label>\n <select\n id=\"aud-production\"\n value={productionId}\n onChange={(e) => setProductionId(e.target.value)}\n className={fieldClass}\n required\n >\n <option value=\"\">Select a production…</option>\n {(productions.data?.items ?? []).map((p) => (\n <option key={p.id} value={p.id}>\n {p.title}\n </option>\n ))}\n </select>\n </div>\n\n <div>\n <label htmlFor=\"aud-role\" className={labelClass}>\n Role Name *\n </label>\n <input\n id=\"aud-role\"\n type=\"text\"\n value={roleName}\n onChange={(e) => setRoleName(e.target.value)}\n placeholder=\"e.g. Lead — Detective Reyes\"\n className={fieldClass}\n required\n />\n </div>\n\n <div>\n <label htmlFor=\"aud-desc\" className={labelClass}>\n Description\n </label>\n <textarea\n id=\"aud-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={4}\n placeholder=\"Role requirements, character notes, audition instructions…\"\n className={fieldClass}\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"aud-pay\" className={labelClass}>\n Pay\n </label>\n <input\n id=\"aud-pay\"\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n value={payMajor}\n onChange={(e) => setPayMajor(e.target.value)}\n placeholder=\"5000\"\n className={fieldClass}\n />\n </div>\n <div>\n <label htmlFor=\"aud-currency\" className={labelClass}>\n Currency\n </label>\n <input\n id=\"aud-currency\"\n type=\"text\"\n value={currency}\n onChange={(e) => setCurrency(e.target.value)}\n placeholder=\"USD\"\n className={fieldClass}\n />\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"aud-location\" className={labelClass}>\n Location\n </label>\n <input\n id=\"aud-location\"\n type=\"text\"\n value={location}\n onChange={(e) => setLocation(e.target.value)}\n placeholder=\"Remote / Los Angeles, CA\"\n className={fieldClass}\n />\n </div>\n <div>\n <label htmlFor=\"aud-language\" className={labelClass}>\n Language\n </label>\n <input\n id=\"aud-language\"\n type=\"text\"\n value={language}\n onChange={(e) => setLanguage(e.target.value)}\n placeholder=\"en\"\n className={fieldClass}\n />\n </div>\n </div>\n\n <div>\n <label htmlFor=\"aud-deadline\" className={labelClass}>\n Submission Deadline\n </label>\n <input\n id=\"aud-deadline\"\n type=\"date\"\n value={submissionDeadline}\n onChange={(e) => setSubmissionDeadline(e.target.value)}\n className={fieldClass}\n />\n </div>\n\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={isMinorRole}\n onChange={(e) => setIsMinorRole(e.target.checked)}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n This role is for a minor (extra safeguards apply)\n </label>\n\n <div className=\"flex items-center gap-3 pt-2\">\n <button\n type=\"submit\"\n disabled={createAudition.isPending}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50 transition-opacity\"\n >\n <Plus className=\"h-4 w-4\" />\n {createAudition.isPending ? 'Posting…' : 'Post Role'}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo('auditions')}\n className=\"rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n Cancel\n </button>\n </div>\n </form>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
@@ -8,7 +8,7 @@ import { formatDate as c, formatMinorUnits as l, formatRelativeTime as re } from
|
|
|
8
8
|
import { ErrorState as ie, LoadingState as u } from "../../components/StateViews.js";
|
|
9
9
|
import { useMemo as d, useRef as ae, useState as f } from "react";
|
|
10
10
|
import { ArrowLeft as p, Clock as oe, Coins as m, Film as h, Languages as se, Loader2 as ce, Lock as g, MapPin as _, Send as v, ShieldAlert as y, Star as b, Upload as le, Users as x, X as ue } from "lucide-react";
|
|
11
|
-
import { useParams as de } from "react-router
|
|
11
|
+
import { useParams as de } from "react-router";
|
|
12
12
|
import { Fragment as S, jsx as C, jsxs as w } from "react/jsx-runtime";
|
|
13
13
|
import { GlassCard as T } from "@burdenoff/fe-libs/ui";
|
|
14
14
|
//#region src/hookmovies/pages/auditions/AuditionDetailPage.tsx
|
|
@@ -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 ChangeEvent, type FC, type FormEvent, useMemo, useRef, 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 Upload,\n Film,\n Loader2,\n Star,\n ShieldAlert,\n X,\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 type HokApplication,\n useAuditionById,\n useAuditionApplications,\n useApplyToAudition,\n useScoreApplication,\n useCloseAudition,\n} from '../../hooks/useHookMoviesApi';\nimport { uploadFileToFiles, FILE_FOLDERS } from '../../hooks/useFiles';\nimport { formatDate, formatMinorUnits, formatRelativeTime } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\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\n/** Short, deterministic label for an anonymised applicant avatar. */\nfunction applicantInitials(applicantId: string): string {\n const cleaned = applicantId.replace(/[^a-zA-Z0-9]/g, '');\n return (cleaned.slice(0, 2) || '?').toUpperCase();\n}\n\n/** Statuses that represent a positive casting outcome — highlighted in the list. */\nconst HIGHLIGHT_STATUSES = new Set(['shortlisted', 'callback', 'offered']);\n\ninterface ScoreStarsProps {\n value: number | null;\n disabled: boolean;\n onScore: (value: number) => void;\n}\n\n/** Accessible 1–5 star rating control wired to `hokScoreApplication`. */\nconst ScoreStars: FC<ScoreStarsProps> = ({ value, disabled, onScore }) => (\n <div\n className=\"flex items-center gap-1\"\n role=\"group\"\n aria-label={value ? `Rating ${value} of 5` : 'Not yet rated'}\n >\n {[1, 2, 3, 4, 5].map((star) => {\n const active = value !== null && star <= value;\n return (\n <button\n key={star}\n type=\"button\"\n disabled={disabled}\n onClick={() => onScore(star)}\n aria-label={`Score ${star} of 5`}\n aria-pressed={value === star}\n title={`Score ${star} of 5`}\n className=\"rounded p-0.5 text-text-muted transition-colors hover:text-status-warning-text focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] disabled:cursor-not-allowed disabled:opacity-60\"\n >\n <Star\n className={cn(\n 'h-5 w-5 transition-transform',\n active ? 'fill-status-warning-text text-status-warning-text' : 'fill-transparent'\n )}\n />\n </button>\n );\n })}\n </div>\n);\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 [selfTapeName, setSelfTapeName] = useState('');\n const [uploading, setUploading] = useState(false);\n const [uploadError, setUploadError] = useState<string | null>(null);\n const [applyError, setApplyError] = useState<string | null>(null);\n const [applied, setApplied] = useState(false);\n const [scoreError, setScoreError] = useState<string | null>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const applicationItems: HokApplication[] = useMemo(\n () => applications.data ?? [],\n [applications.data]\n );\n const ratedCount = useMemo(\n () => applicationItems.filter((appn) => appn.score !== null).length,\n [applicationItems]\n );\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 setSelfTapeName('');\n } catch (err) {\n setApplyError(err instanceof Error ? err.message : 'Failed to apply');\n }\n };\n\n const handleSelfTapeChange = async (e: ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n // Reset the input so re-selecting the same file re-triggers change.\n e.target.value = '';\n if (!file) return;\n setUploadError(null);\n setUploading(true);\n try {\n const uploaded = await uploadFileToFiles(file, FILE_FOLDERS.selfTapes);\n setSelfTapeFileId(uploaded.id);\n setSelfTapeName(uploaded.name || file.name);\n } catch (err) {\n setUploadError(err instanceof Error ? err.message : 'Failed to upload self-tape');\n } finally {\n setUploading(false);\n }\n };\n\n const clearSelfTape = () => {\n setSelfTapeFileId('');\n setSelfTapeName('');\n setUploadError(null);\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\n return (\n <PageLayout\n hero\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 transition-colors hover:bg-bg-sunken hover:text-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\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.5 rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\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 {/* ---- Applicants (casting view) — primary column ---- */}\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 <GlassCard treatment=\"metric\" className=\"border-border-seam p-5\">\n <div className=\"mb-4 flex flex-wrap items-center justify-between gap-2\">\n <h2 className=\"flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <Users className=\"h-4 w-4 text-text-secondary\" />\n Applicants\n <span className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary\">\n {applicationItems.length}\n </span>\n </h2>\n {applicationItems.length > 0 && (\n <span className=\"flex items-center gap-1.5 text-xs text-text-secondary\">\n <Star className=\"h-3.5 w-3.5 fill-status-warning-text text-status-warning-text\" />\n {ratedCount} of {applicationItems.length} rated\n </span>\n )}\n </div>\n\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\n {applications.isLoading ? (\n <LoadingState label=\"Loading applicants…\" className=\"min-h-[160px]\" />\n ) : applicationItems.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center gap-3 rounded-xl border border-dashed border-border-seam bg-bg-surface/60 px-4 py-12 text-center\">\n <div className=\"flex h-11 w-11 items-center justify-center rounded-full bg-bg-sunken\">\n <Users className=\"h-5 w-5 text-text-muted\" />\n </div>\n <p className=\"text-sm font-medium text-text-primary\">No applications yet</p>\n <p className=\"max-w-xs text-xs text-text-secondary\">\n Applicants who apply to this role will appear here for review and scoring.\n </p>\n </div>\n ) : (\n <ul className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n {applicationItems.map((appn) => {\n const highlighted = HIGHLIGHT_STATUSES.has(appn.status.toLowerCase());\n return (\n <li\n key={appn.id}\n className={cn(\n 'flex flex-col rounded-xl border bg-bg-surface p-4 transition-colors',\n highlighted\n ? 'border-status-info-border ring-1 ring-status-info-border'\n : 'border-border-seam hover:border-border-strong'\n )}\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <span\n className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-full border border-border-seam bg-bg-sunken text-xs font-semibold text-text-secondary\"\n aria-hidden=\"true\"\n >\n {applicantInitials(appn.applicantId)}\n </span>\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold 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 </div>\n <StatusBadge status={appn.status} className=\"shrink-0\" />\n </div>\n\n {appn.coverNote ? (\n <p className=\"mt-3 line-clamp-3 text-sm leading-relaxed text-text-secondary\">\n <span className=\"text-text-muted\">“</span>\n {appn.coverNote}\n <span className=\"text-text-muted\">”</span>\n </p>\n ) : (\n <p className=\"mt-3 text-sm italic text-text-muted\">\n No cover note provided.\n </p>\n )}\n\n <div className=\"mt-auto flex items-center justify-between gap-2 pt-4\">\n <ScoreStars\n value={appn.score}\n disabled={score.isPending}\n onScore={(value) => void handleScore(appn.id, value)}\n />\n <span className=\"text-xs font-medium text-text-secondary\">\n {appn.score !== null ? `${appn.score}/5` : 'Unrated'}\n </span>\n </div>\n </li>\n );\n })}\n </ul>\n )}\n </GlassCard>\n </div>\n\n {/* ---- Details + Apply — secondary column ---- */}\n <div className=\"space-y-6\">\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-3 text-sm\">\n <div className=\"flex items-center justify-between gap-3\">\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=\"text-right 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 gap-3\">\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=\"text-right font-medium text-text-primary\">{audition.location}</dd>\n </div>\n )}\n {audition.language && (\n <div className=\"flex items-center justify-between gap-3\">\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=\"text-right font-medium text-text-primary\">{audition.language}</dd>\n </div>\n )}\n {audition.submissionDeadline && (\n <div className=\"flex items-center justify-between gap-3\">\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=\"text-right 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 <div className=\"mt-4 flex items-start gap-2 rounded-lg border border-status-warning-border bg-status-warning-bg-subtle px-3 py-2 text-xs text-status-warning-text\">\n <ShieldAlert className=\"mt-0.5 h-4 w-4 shrink-0\" />\n <span>\n This is a minor role. Guardian consent and restricted communications apply.\n </span>\n </div>\n )}\n </GlassCard>\n\n {/* Apply (talent view) */}\n <GlassCard treatment=\"glass\" className=\"border-border-seam p-5\">\n <h2 className=\"mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <Send className=\"h-4 w-4 text-text-secondary\" />\n Apply for this role\n </h2>\n {applied ? (\n <div className=\"rounded-lg border border-status-success-border bg-status-success-bg-subtle px-3 py-2.5 text-sm text-status-success-text\">\n Your application has been submitted.\n </div>\n ) : !isOpen ? (\n <p className=\"rounded-lg bg-bg-sunken px-3 py-2.5 text-sm text-text-secondary\">\n This audition is no longer open for applications.\n </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 <div className=\"space-y-1.5\">\n <label\n htmlFor=\"cover-note\"\n className=\"block text-xs font-medium text-text-secondary\"\n >\n Cover note\n </label>\n <textarea\n id=\"cover-note\"\n value={coverNote}\n onChange={(e) => setCoverNote(e.target.value)}\n rows={3}\n placeholder=\"Why you're right for this role\"\n className={fieldClass}\n />\n </div>\n <div className=\"space-y-1.5\">\n <label\n htmlFor=\"self-tape-upload\"\n className=\"block text-xs font-medium text-text-secondary\"\n >\n Self-tape (optional)\n </label>\n <input\n ref={fileInputRef}\n id=\"self-tape-upload\"\n type=\"file\"\n accept=\"video/*\"\n onChange={(e) => void handleSelfTapeChange(e)}\n disabled={uploading}\n className=\"sr-only\"\n aria-label=\"Upload self-tape video\"\n />\n {selfTapeFileId ? (\n <div className=\"flex items-center justify-between gap-2 rounded-lg border border-border-seam bg-bg-surface px-3 py-2\">\n <span className=\"flex min-w-0 items-center gap-2 text-sm text-text-primary\">\n <Film className=\"h-4 w-4 shrink-0 text-status-success-text\" />\n <span className=\"truncate\">{selfTapeName || 'Self-tape uploaded'}</span>\n </span>\n <button\n type=\"button\"\n onClick={clearSelfTape}\n className=\"rounded-md p-1 text-text-secondary transition-colors hover:text-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n aria-label=\"Remove self-tape\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={() => fileInputRef.current?.click()}\n disabled={uploading}\n className=\"inline-flex w-full items-center justify-center gap-2 rounded-lg border border-dashed border-border-subtle bg-bg-surface px-3 py-3 text-sm text-text-secondary transition-colors hover:border-border-seam hover:text-text-primary disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n {uploading ? (\n <>\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n Uploading…\n </>\n ) : (\n <>\n <Upload className=\"h-4 w-4\" />\n Upload self-tape video\n </>\n )}\n </button>\n )}\n {uploadError && (\n <p className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-xs text-status-error-text\">\n {uploadError}\n </p>\n )}\n </div>\n <button\n type=\"submit\"\n disabled={apply.isPending || uploading}\n className=\"inline-flex w-full items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2.5 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90 disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\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 ChangeEvent, type FC, type FormEvent, useMemo, useRef, useState } from 'react';\nimport { useParams } from 'react-router';\nimport {\n ArrowLeft,\n MapPin,\n Languages,\n Clock,\n Coins,\n Send,\n Lock,\n Users,\n Upload,\n Film,\n Loader2,\n Star,\n ShieldAlert,\n X,\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 type HokApplication,\n useAuditionById,\n useAuditionApplications,\n useApplyToAudition,\n useScoreApplication,\n useCloseAudition,\n} from '../../hooks/useHookMoviesApi';\nimport { uploadFileToFiles, FILE_FOLDERS } from '../../hooks/useFiles';\nimport { formatDate, formatMinorUnits, formatRelativeTime } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\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\n/** Short, deterministic label for an anonymised applicant avatar. */\nfunction applicantInitials(applicantId: string): string {\n const cleaned = applicantId.replace(/[^a-zA-Z0-9]/g, '');\n return (cleaned.slice(0, 2) || '?').toUpperCase();\n}\n\n/** Statuses that represent a positive casting outcome — highlighted in the list. */\nconst HIGHLIGHT_STATUSES = new Set(['shortlisted', 'callback', 'offered']);\n\ninterface ScoreStarsProps {\n value: number | null;\n disabled: boolean;\n onScore: (value: number) => void;\n}\n\n/** Accessible 1–5 star rating control wired to `hokScoreApplication`. */\nconst ScoreStars: FC<ScoreStarsProps> = ({ value, disabled, onScore }) => (\n <div\n className=\"flex items-center gap-1\"\n role=\"group\"\n aria-label={value ? `Rating ${value} of 5` : 'Not yet rated'}\n >\n {[1, 2, 3, 4, 5].map((star) => {\n const active = value !== null && star <= value;\n return (\n <button\n key={star}\n type=\"button\"\n disabled={disabled}\n onClick={() => onScore(star)}\n aria-label={`Score ${star} of 5`}\n aria-pressed={value === star}\n title={`Score ${star} of 5`}\n className=\"rounded p-0.5 text-text-muted transition-colors hover:text-status-warning-text focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] disabled:cursor-not-allowed disabled:opacity-60\"\n >\n <Star\n className={cn(\n 'h-5 w-5 transition-transform',\n active ? 'fill-status-warning-text text-status-warning-text' : 'fill-transparent'\n )}\n />\n </button>\n );\n })}\n </div>\n);\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 [selfTapeName, setSelfTapeName] = useState('');\n const [uploading, setUploading] = useState(false);\n const [uploadError, setUploadError] = useState<string | null>(null);\n const [applyError, setApplyError] = useState<string | null>(null);\n const [applied, setApplied] = useState(false);\n const [scoreError, setScoreError] = useState<string | null>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const applicationItems: HokApplication[] = useMemo(\n () => applications.data ?? [],\n [applications.data]\n );\n const ratedCount = useMemo(\n () => applicationItems.filter((appn) => appn.score !== null).length,\n [applicationItems]\n );\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 setSelfTapeName('');\n } catch (err) {\n setApplyError(err instanceof Error ? err.message : 'Failed to apply');\n }\n };\n\n const handleSelfTapeChange = async (e: ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n // Reset the input so re-selecting the same file re-triggers change.\n e.target.value = '';\n if (!file) return;\n setUploadError(null);\n setUploading(true);\n try {\n const uploaded = await uploadFileToFiles(file, FILE_FOLDERS.selfTapes);\n setSelfTapeFileId(uploaded.id);\n setSelfTapeName(uploaded.name || file.name);\n } catch (err) {\n setUploadError(err instanceof Error ? err.message : 'Failed to upload self-tape');\n } finally {\n setUploading(false);\n }\n };\n\n const clearSelfTape = () => {\n setSelfTapeFileId('');\n setSelfTapeName('');\n setUploadError(null);\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\n return (\n <PageLayout\n hero\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 transition-colors hover:bg-bg-sunken hover:text-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\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.5 rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\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 {/* ---- Applicants (casting view) — primary column ---- */}\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 <GlassCard treatment=\"metric\" className=\"border-border-seam p-5\">\n <div className=\"mb-4 flex flex-wrap items-center justify-between gap-2\">\n <h2 className=\"flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <Users className=\"h-4 w-4 text-text-secondary\" />\n Applicants\n <span className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary\">\n {applicationItems.length}\n </span>\n </h2>\n {applicationItems.length > 0 && (\n <span className=\"flex items-center gap-1.5 text-xs text-text-secondary\">\n <Star className=\"h-3.5 w-3.5 fill-status-warning-text text-status-warning-text\" />\n {ratedCount} of {applicationItems.length} rated\n </span>\n )}\n </div>\n\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\n {applications.isLoading ? (\n <LoadingState label=\"Loading applicants…\" className=\"min-h-[160px]\" />\n ) : applicationItems.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center gap-3 rounded-xl border border-dashed border-border-seam bg-bg-surface/60 px-4 py-12 text-center\">\n <div className=\"flex h-11 w-11 items-center justify-center rounded-full bg-bg-sunken\">\n <Users className=\"h-5 w-5 text-text-muted\" />\n </div>\n <p className=\"text-sm font-medium text-text-primary\">No applications yet</p>\n <p className=\"max-w-xs text-xs text-text-secondary\">\n Applicants who apply to this role will appear here for review and scoring.\n </p>\n </div>\n ) : (\n <ul className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n {applicationItems.map((appn) => {\n const highlighted = HIGHLIGHT_STATUSES.has(appn.status.toLowerCase());\n return (\n <li\n key={appn.id}\n className={cn(\n 'flex flex-col rounded-xl border bg-bg-surface p-4 transition-colors',\n highlighted\n ? 'border-status-info-border ring-1 ring-status-info-border'\n : 'border-border-seam hover:border-border-strong'\n )}\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <span\n className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-full border border-border-seam bg-bg-sunken text-xs font-semibold text-text-secondary\"\n aria-hidden=\"true\"\n >\n {applicantInitials(appn.applicantId)}\n </span>\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold 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 </div>\n <StatusBadge status={appn.status} className=\"shrink-0\" />\n </div>\n\n {appn.coverNote ? (\n <p className=\"mt-3 line-clamp-3 text-sm leading-relaxed text-text-secondary\">\n <span className=\"text-text-muted\">“</span>\n {appn.coverNote}\n <span className=\"text-text-muted\">”</span>\n </p>\n ) : (\n <p className=\"mt-3 text-sm italic text-text-muted\">\n No cover note provided.\n </p>\n )}\n\n <div className=\"mt-auto flex items-center justify-between gap-2 pt-4\">\n <ScoreStars\n value={appn.score}\n disabled={score.isPending}\n onScore={(value) => void handleScore(appn.id, value)}\n />\n <span className=\"text-xs font-medium text-text-secondary\">\n {appn.score !== null ? `${appn.score}/5` : 'Unrated'}\n </span>\n </div>\n </li>\n );\n })}\n </ul>\n )}\n </GlassCard>\n </div>\n\n {/* ---- Details + Apply — secondary column ---- */}\n <div className=\"space-y-6\">\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-3 text-sm\">\n <div className=\"flex items-center justify-between gap-3\">\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=\"text-right 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 gap-3\">\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=\"text-right font-medium text-text-primary\">{audition.location}</dd>\n </div>\n )}\n {audition.language && (\n <div className=\"flex items-center justify-between gap-3\">\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=\"text-right font-medium text-text-primary\">{audition.language}</dd>\n </div>\n )}\n {audition.submissionDeadline && (\n <div className=\"flex items-center justify-between gap-3\">\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=\"text-right 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 <div className=\"mt-4 flex items-start gap-2 rounded-lg border border-status-warning-border bg-status-warning-bg-subtle px-3 py-2 text-xs text-status-warning-text\">\n <ShieldAlert className=\"mt-0.5 h-4 w-4 shrink-0\" />\n <span>\n This is a minor role. Guardian consent and restricted communications apply.\n </span>\n </div>\n )}\n </GlassCard>\n\n {/* Apply (talent view) */}\n <GlassCard treatment=\"glass\" className=\"border-border-seam p-5\">\n <h2 className=\"mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <Send className=\"h-4 w-4 text-text-secondary\" />\n Apply for this role\n </h2>\n {applied ? (\n <div className=\"rounded-lg border border-status-success-border bg-status-success-bg-subtle px-3 py-2.5 text-sm text-status-success-text\">\n Your application has been submitted.\n </div>\n ) : !isOpen ? (\n <p className=\"rounded-lg bg-bg-sunken px-3 py-2.5 text-sm text-text-secondary\">\n This audition is no longer open for applications.\n </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 <div className=\"space-y-1.5\">\n <label\n htmlFor=\"cover-note\"\n className=\"block text-xs font-medium text-text-secondary\"\n >\n Cover note\n </label>\n <textarea\n id=\"cover-note\"\n value={coverNote}\n onChange={(e) => setCoverNote(e.target.value)}\n rows={3}\n placeholder=\"Why you're right for this role\"\n className={fieldClass}\n />\n </div>\n <div className=\"space-y-1.5\">\n <label\n htmlFor=\"self-tape-upload\"\n className=\"block text-xs font-medium text-text-secondary\"\n >\n Self-tape (optional)\n </label>\n <input\n ref={fileInputRef}\n id=\"self-tape-upload\"\n type=\"file\"\n accept=\"video/*\"\n onChange={(e) => void handleSelfTapeChange(e)}\n disabled={uploading}\n className=\"sr-only\"\n aria-label=\"Upload self-tape video\"\n />\n {selfTapeFileId ? (\n <div className=\"flex items-center justify-between gap-2 rounded-lg border border-border-seam bg-bg-surface px-3 py-2\">\n <span className=\"flex min-w-0 items-center gap-2 text-sm text-text-primary\">\n <Film className=\"h-4 w-4 shrink-0 text-status-success-text\" />\n <span className=\"truncate\">{selfTapeName || 'Self-tape uploaded'}</span>\n </span>\n <button\n type=\"button\"\n onClick={clearSelfTape}\n className=\"rounded-md p-1 text-text-secondary transition-colors hover:text-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n aria-label=\"Remove self-tape\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={() => fileInputRef.current?.click()}\n disabled={uploading}\n className=\"inline-flex w-full items-center justify-center gap-2 rounded-lg border border-dashed border-border-subtle bg-bg-surface px-3 py-3 text-sm text-text-secondary transition-colors hover:border-border-seam hover:text-text-primary disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n {uploading ? (\n <>\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n Uploading…\n </>\n ) : (\n <>\n <Upload className=\"h-4 w-4\" />\n Upload self-tape video\n </>\n )}\n </button>\n )}\n {uploadError && (\n <p className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-xs text-status-error-text\">\n {uploadError}\n </p>\n )}\n </div>\n <button\n type=\"submit\"\n disabled={apply.isPending || uploading}\n className=\"inline-flex w-full items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2.5 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90 disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\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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"}
|
|
@@ -11,7 +11,7 @@ import { Pagination as fe } from "../../components/Pagination.js";
|
|
|
11
11
|
import { RatingDisplay as pe } from "../../components/RatingDisplay.js";
|
|
12
12
|
import { useRef as me, useState as h } from "react";
|
|
13
13
|
import { Archive as he, ArrowLeft as ge, Calendar as g, CheckCircle2 as _e, Clapperboard as _, Clock as ve, Film as v, Globe as ye, Image as y, Languages as be, Layers as xe, Loader2 as b, Lock as Se, Pencil as Ce, Play as we, Plus as x, ShieldCheck as Te, Sparkles as Ee, Star as De, Trash2 as Oe, Upload as ke, UserPlus as Ae, Users as je, Video as Me, X as Ne } from "lucide-react";
|
|
14
|
-
import { useParams as Pe } from "react-router
|
|
14
|
+
import { useParams as Pe } from "react-router";
|
|
15
15
|
import { Fragment as S, jsx as C, jsxs as w } from "react/jsx-runtime";
|
|
16
16
|
import { GlassCard as T } from "@burdenoff/fe-libs/ui";
|
|
17
17
|
//#region src/hookmovies/pages/catalog/TitleDetailPage.tsx
|