@burdenoff/microfe-hookmovies 2026.1027.2 → 2026.1031.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/components/AddToListButton.js +12 -12
- package/dist/hookmovies/components/GettingStartedChecklist.d.ts +4 -0
- package/dist/hookmovies/components/GettingStartedChecklist.d.ts.map +1 -0
- package/dist/hookmovies/components/GettingStartedChecklist.js +212 -0
- package/dist/hookmovies/components/GettingStartedChecklist.js.map +1 -0
- package/dist/hookmovies/components/PageLayout.d.ts +6 -0
- package/dist/hookmovies/components/PageLayout.d.ts.map +1 -1
- package/dist/hookmovies/components/PageLayout.js +2 -1
- package/dist/hookmovies/components/PageLayout.js.map +1 -1
- package/dist/hookmovies/components/index.js +1 -1
- package/dist/hookmovies/events/activation.d.ts +37 -0
- package/dist/hookmovies/events/activation.d.ts.map +1 -0
- package/dist/hookmovies/events/activation.js +40 -0
- package/dist/hookmovies/events/activation.js.map +1 -0
- package/dist/hookmovies/hooks/useHookMoviesApi.d.ts +45 -1
- package/dist/hookmovies/hooks/useHookMoviesApi.d.ts.map +1 -1
- package/dist/hookmovies/hooks/useHookMoviesApi.js +604 -557
- package/dist/hookmovies/hooks/useHookMoviesApi.js.map +1 -1
- package/dist/hookmovies/index.js +1 -1
- package/dist/hookmovies/pages/HomePage.d.ts.map +1 -1
- package/dist/hookmovies/pages/HomePage.js +149 -142
- package/dist/hookmovies/pages/HomePage.js.map +1 -1
- package/dist/hookmovies/pages/ai/ConciergePage.js +23 -23
- package/dist/hookmovies/pages/analytics/ActivationFunnelSection.d.ts +4 -0
- package/dist/hookmovies/pages/analytics/ActivationFunnelSection.d.ts.map +1 -0
- package/dist/hookmovies/pages/analytics/ActivationFunnelSection.js +150 -0
- package/dist/hookmovies/pages/analytics/ActivationFunnelSection.js.map +1 -0
- package/dist/hookmovies/pages/analytics/AnalyticsPage.d.ts.map +1 -1
- package/dist/hookmovies/pages/analytics/AnalyticsPage.js +224 -222
- package/dist/hookmovies/pages/analytics/AnalyticsPage.js.map +1 -1
- package/dist/hookmovies/pages/analytics/PayoutsPage.js +175 -175
- package/dist/hookmovies/pages/analytics/PayoutsPage.js.map +1 -1
- package/dist/hookmovies/pages/auditions/AuditionCreatePage.js +4 -4
- package/dist/hookmovies/pages/auditions/AuditionDetailPage.js +45 -45
- package/dist/hookmovies/pages/auditions/AuditionDetailPage.js.map +1 -1
- package/dist/hookmovies/pages/auditions/AuditionsPage.d.ts.map +1 -1
- package/dist/hookmovies/pages/auditions/AuditionsPage.js +12 -9
- package/dist/hookmovies/pages/auditions/AuditionsPage.js.map +1 -1
- package/dist/hookmovies/pages/casting/GuardianInvitePage.js +1 -1
- package/dist/hookmovies/pages/casting/GuardiansPanel.js +6 -6
- package/dist/hookmovies/pages/casting/MyCastingPage.js +6 -6
- package/dist/hookmovies/pages/casting/SavedSearchesPage.js +6 -6
- package/dist/hookmovies/pages/catalog/PeoplePage.js +9 -9
- package/dist/hookmovies/pages/catalog/StudiosPage.js +8 -8
- package/dist/hookmovies/pages/catalog/TitleCreatePage.js +4 -4
- package/dist/hookmovies/pages/catalog/TitleDetailPage.d.ts.map +1 -1
- package/dist/hookmovies/pages/catalog/TitleDetailPage.js +671 -668
- package/dist/hookmovies/pages/catalog/TitleDetailPage.js.map +1 -1
- package/dist/hookmovies/pages/catalog/TitlesPage.d.ts.map +1 -1
- package/dist/hookmovies/pages/catalog/TitlesPage.js +38 -34
- package/dist/hookmovies/pages/catalog/TitlesPage.js.map +1 -1
- package/dist/hookmovies/pages/commerce/CheckoutPage.js +82 -82
- package/dist/hookmovies/pages/commerce/CheckoutPage.js.map +1 -1
- package/dist/hookmovies/pages/consumer/DiscoveryPage.js +48 -48
- package/dist/hookmovies/pages/consumer/HooksPage.js +23 -23
- package/dist/hookmovies/pages/consumer/TitleViewPage.d.ts.map +1 -1
- package/dist/hookmovies/pages/consumer/TitleViewPage.js +52 -49
- package/dist/hookmovies/pages/consumer/TitleViewPage.js.map +1 -1
- package/dist/hookmovies/pages/developer/DevPortalPage.js +43 -43
- package/dist/hookmovies/pages/distribution/RightsWindowsPage.js +133 -133
- package/dist/hookmovies/pages/distribution/RightsWindowsPage.js.map +1 -1
- package/dist/hookmovies/pages/engagement/ReviewsPage.js +129 -129
- package/dist/hookmovies/pages/engagement/ReviewsPage.js.map +1 -1
- package/dist/hookmovies/pages/library/EntitlementsPage.js +135 -135
- package/dist/hookmovies/pages/library/EntitlementsPage.js.map +1 -1
- package/dist/hookmovies/pages/library/ListsPage.js +44 -44
- package/dist/hookmovies/pages/library/ListsPage.js.map +1 -1
- package/dist/hookmovies/pages/live/LivePage.js +42 -42
- package/dist/hookmovies/pages/localization/LocalizationPage.js +174 -174
- package/dist/hookmovies/pages/localization/LocalizationPage.js.map +1 -1
- package/dist/hookmovies/pages/onboarding/OnboardingPage.js +35 -35
- package/dist/hookmovies/pages/productions/ProductionCreatePage.js +4 -4
- package/dist/hookmovies/pages/productions/ProductionDetailPage.js +23 -23
- package/dist/hookmovies/pages/productions/ProductionsPage.d.ts.map +1 -1
- package/dist/hookmovies/pages/productions/ProductionsPage.js +26 -23
- package/dist/hookmovies/pages/productions/ProductionsPage.js.map +1 -1
- package/dist/hookmovies/pages/social/ProfilesPage.js +50 -50
- package/dist/hookmovies/pages/social/WatchPartyListPage.js +5 -5
- package/dist/hookmovies/pages/social/WatchPartyRoomPage.js +281 -281
- package/dist/hookmovies/pages/social/WatchPartyRoomPage.js.map +1 -1
- package/dist/hookmovies/pages/trust/DisputesPage.js +4 -4
- package/dist/hookmovies/pages/trust/ModerationPage.js +8 -8
- package/dist/hookmovies/pages/trust/ModerationPage.js.map +1 -1
- package/dist/hookmovies/pages/trust/VerificationsPage.js +4 -4
- package/dist/index.js +3 -3
- package/package.json +2 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { parseListItems as t, useCreateList as n, useMyLists as r, useUpdateList as i } from "../hooks/useHookMoviesApi.js";
|
|
3
|
+
import { cn as a } from "../utils/cn.js";
|
|
4
4
|
import { ErrorState as o, LoadingState as s } from "./StateViews.js";
|
|
5
5
|
import { useEffect as c, useState as l } from "react";
|
|
6
6
|
import { Bookmark as u, Check as d, ListPlus as f, ListVideo as p, Loader2 as m, Plus as h, X as g } from "lucide-react";
|
|
@@ -13,7 +13,7 @@ var b = {
|
|
|
13
13
|
WATCHLIST: u,
|
|
14
14
|
PLAYLIST: p
|
|
15
15
|
}, C = (e) => e.charAt(0) + e.slice(1).toLowerCase().replace(/_/g, " "), w = "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)]", T = "mb-1.5 block text-xs font-medium text-text-secondary", E = ({ titleId: u, titleName: p, variant: E = "cta", className: D }) => {
|
|
16
|
-
let O = e(), [k, A] = l(!1), [j, M] = l(!1), [N, P] = l(null), [F, I] = l(null), [L, R] = l(""), [z, B] = l("WATCHLIST"), V =
|
|
16
|
+
let O = e(), [k, A] = l(!1), [j, M] = l(!1), [N, P] = l(null), [F, I] = l(null), [L, R] = l(""), [z, B] = l("WATCHLIST"), V = r(void 0, b), H = i(), U = n(), W = V.data?.items ?? [], G = W.filter((e) => t(e.items).some((e) => e.titleId === u)).length > 0;
|
|
17
17
|
c(() => {
|
|
18
18
|
if (!k) return;
|
|
19
19
|
let e = (e) => {
|
|
@@ -25,7 +25,7 @@ var b = {
|
|
|
25
25
|
A(!1), M(!1), I(null);
|
|
26
26
|
}, q = async (e) => {
|
|
27
27
|
I(null), P(e.id);
|
|
28
|
-
let
|
|
28
|
+
let n = t(e.items), r = n.some((e) => e.titleId === u) ? n.filter((e) => e.titleId !== u) : [...n, {
|
|
29
29
|
titleId: u,
|
|
30
30
|
note: null
|
|
31
31
|
}];
|
|
@@ -63,13 +63,13 @@ var b = {
|
|
|
63
63
|
onClick: () => A(!0),
|
|
64
64
|
"aria-label": `${X}${Y ? ` —${Y}` : ""}`,
|
|
65
65
|
title: X,
|
|
66
|
-
className:
|
|
66
|
+
className: a("inline-flex size-8 items-center justify-center rounded-lg border border-border-seam", "bg-bg-overlay/80 text-text-secondary backdrop-blur-sm transition-colors", "hover:border-border-strong hover:text-text-primary", "focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]", G && "text-status-success-text", D),
|
|
67
67
|
children: v(G ? d : f, { className: "h-4 w-4" })
|
|
68
68
|
}) : /* @__PURE__ */ y("button", {
|
|
69
69
|
type: "button",
|
|
70
70
|
onClick: () => A(!0),
|
|
71
71
|
"aria-label": `${X}${Y ? ` —${Y}` : ""}`,
|
|
72
|
-
className:
|
|
72
|
+
className: a("inline-flex items-center gap-2 rounded-xl border border-border-seam bg-bg-surface/60", "px-5 py-2.5 text-sm font-semibold text-text-primary backdrop-blur transition-colors", "hover:bg-bg-surface focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]", D),
|
|
73
73
|
children: [G ? /* @__PURE__ */ v(d, { className: "h-5 w-5 text-status-success-text" }) : /* @__PURE__ */ v(h, { className: "h-5 w-5" }), X]
|
|
74
74
|
}), k && /* @__PURE__ */ v("div", {
|
|
75
75
|
className: "fixed inset-0 z-50 flex items-center justify-center bg-bg-canvas/90 p-4 backdrop-blur-sm",
|
|
@@ -119,15 +119,15 @@ var b = {
|
|
|
119
119
|
}) : /* @__PURE__ */ v("ul", {
|
|
120
120
|
className: "-mx-1 max-h-[45vh] space-y-1 overflow-y-auto px-1",
|
|
121
121
|
children: W.map((e) => {
|
|
122
|
-
let
|
|
122
|
+
let n = S[e.kind], r = t(e.items).some((e) => e.titleId === u), i = N === e.id;
|
|
123
123
|
return /* @__PURE__ */ v("li", { children: /* @__PURE__ */ y("button", {
|
|
124
124
|
type: "button",
|
|
125
125
|
onClick: () => void q(e),
|
|
126
|
-
disabled:
|
|
127
|
-
"aria-pressed":
|
|
128
|
-
className:
|
|
126
|
+
disabled: i,
|
|
127
|
+
"aria-pressed": r,
|
|
128
|
+
className: a("flex w-full items-center gap-3 rounded-lg border px-3 py-2 text-left transition-colors", "focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]", "disabled:opacity-60", r ? "border-status-success-border bg-status-success-bg-subtle" : "border-border-subtle hover:border-border-strong hover:bg-bg-sunken"),
|
|
129
129
|
children: [
|
|
130
|
-
/* @__PURE__ */ v(
|
|
130
|
+
/* @__PURE__ */ v(n, { className: "h-4 w-4 shrink-0 text-text-secondary" }),
|
|
131
131
|
/* @__PURE__ */ y("span", {
|
|
132
132
|
className: "min-w-0 flex-1",
|
|
133
133
|
children: [/* @__PURE__ */ v("span", {
|
|
@@ -144,7 +144,7 @@ var b = {
|
|
|
144
144
|
]
|
|
145
145
|
})]
|
|
146
146
|
}),
|
|
147
|
-
|
|
147
|
+
i ? /* @__PURE__ */ v(m, { className: "h-4 w-4 shrink-0 animate-spin text-text-secondary" }) : r ? /* @__PURE__ */ v(d, { className: "h-4 w-4 shrink-0 text-status-success-text" }) : /* @__PURE__ */ v(h, { className: "h-4 w-4 shrink-0 text-text-secondary" })
|
|
148
148
|
]
|
|
149
149
|
}) }, e.id);
|
|
150
150
|
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GettingStartedChecklist.d.ts","sourceRoot":"","sources":["../../../src/hookmovies/components/GettingStartedChecklist.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,KAAK,EAAE,EAA+B,MAAM,OAAO,CAAC;AA2F7D,eAAO,MAAM,uBAAuB,EAAE,EA0MrC,CAAC"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { useHookMovies as e } from "../context/HookMoviesContext.js";
|
|
2
|
+
import { useTr as t } from "../../shared/hooks/useTr.js";
|
|
3
|
+
import { useHookMoviesActivation as n } from "../events/activation.js";
|
|
4
|
+
import { useLoadDemoData as r, useOnboardingStatus as i, useRemoveDemoData as a } from "../hooks/useHookMoviesApi.js";
|
|
5
|
+
import { cn as o } from "../utils/cn.js";
|
|
6
|
+
import { useEffect as s, useRef as c, useState as l } from "react";
|
|
7
|
+
import { ArrowRight as u, CheckCircle2 as d, Circle as f, Loader2 as p, Sparkles as m, Trash2 as h, X as g } from "lucide-react";
|
|
8
|
+
import { jsx as _, jsxs as v } from "react/jsx-runtime";
|
|
9
|
+
import { GlassCard as y } from "@burdenoff/fe-libs/ui";
|
|
10
|
+
//#region src/hookmovies/components/GettingStartedChecklist.tsx
|
|
11
|
+
var b = {
|
|
12
|
+
SET_PREFERENCES: {
|
|
13
|
+
label: ["hookmovies.onboarding.steps.preferences.label", "Set your viewing preferences"],
|
|
14
|
+
description: ["hookmovies.onboarding.steps.preferences.description", "Pick your languages and content types so discovery shows what you want to watch."],
|
|
15
|
+
action: ["hookmovies.onboarding.steps.preferences.action", "Set preferences"],
|
|
16
|
+
path: "onboarding/languages"
|
|
17
|
+
},
|
|
18
|
+
CREATE_TITLE: {
|
|
19
|
+
label: ["hookmovies.onboarding.steps.createTitle.label", "Add your first title"],
|
|
20
|
+
description: ["hookmovies.onboarding.steps.createTitle.description", "Create a movie, series or short in the catalog with its synopsis, genres and artwork."],
|
|
21
|
+
action: ["hookmovies.onboarding.steps.createTitle.action", "New title"],
|
|
22
|
+
path: "catalog/titles/new"
|
|
23
|
+
},
|
|
24
|
+
PUBLISH_TITLE: {
|
|
25
|
+
label: ["hookmovies.onboarding.steps.publishTitle.label", "Publish a title"],
|
|
26
|
+
description: ["hookmovies.onboarding.steps.publishTitle.description", "Move a title through review to Published so viewers can find it in Browse."],
|
|
27
|
+
action: ["hookmovies.onboarding.steps.publishTitle.action", "Open catalog"],
|
|
28
|
+
path: "catalog/titles"
|
|
29
|
+
},
|
|
30
|
+
START_PRODUCTION: {
|
|
31
|
+
label: ["hookmovies.onboarding.steps.startProduction.label", "Start a production"],
|
|
32
|
+
description: ["hookmovies.onboarding.steps.startProduction.description", "Track a project from development to release with its budget and schedule."],
|
|
33
|
+
action: ["hookmovies.onboarding.steps.startProduction.action", "New production"],
|
|
34
|
+
path: "productions/new"
|
|
35
|
+
},
|
|
36
|
+
OPEN_AUDITION: {
|
|
37
|
+
label: ["hookmovies.onboarding.steps.openAudition.label", "Open an audition"],
|
|
38
|
+
description: ["hookmovies.onboarding.steps.openAudition.description", "Post a role for one of your productions so talent can apply with a self-tape."],
|
|
39
|
+
action: ["hookmovies.onboarding.steps.openAudition.action", "Post a role"],
|
|
40
|
+
path: "auditions/new"
|
|
41
|
+
}
|
|
42
|
+
}, x = (e, t) => `hookmovies:gettingStarted:hidden:${e ?? "default"}:${t ?? "anonymous"}`, S = (e) => `hookmovies:gettingStarted:completedTracked:${e ?? "default"}`;
|
|
43
|
+
function C(e) {
|
|
44
|
+
try {
|
|
45
|
+
return window.localStorage.getItem(e) === "1";
|
|
46
|
+
} catch {
|
|
47
|
+
return !1;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function w(e) {
|
|
51
|
+
try {
|
|
52
|
+
window.localStorage.setItem(e, "1");
|
|
53
|
+
} catch {}
|
|
54
|
+
}
|
|
55
|
+
var T = () => {
|
|
56
|
+
let T = t(), { navigateTo: E, workspaceId: D, currentUser: O } = e(), k = n(), A = i(), j = r(), M = a(), N = x(D, O?.id), [P, F] = l(() => C(N));
|
|
57
|
+
s(() => F(C(N)), [N]);
|
|
58
|
+
let I = A.data, L = !!(I && I.totalSteps > 0 && I.completedSteps === I.totalSteps), R = c(!1);
|
|
59
|
+
if (s(() => {
|
|
60
|
+
if (!L || R.current) return;
|
|
61
|
+
R.current = !0;
|
|
62
|
+
let e = S(D);
|
|
63
|
+
C(e) || (w(e), k("hookmovies.onboarding.checklist_completed"));
|
|
64
|
+
}, [
|
|
65
|
+
L,
|
|
66
|
+
D,
|
|
67
|
+
k
|
|
68
|
+
]), P || A.isError || !I) return null;
|
|
69
|
+
let z = () => {
|
|
70
|
+
w(N), F(!0);
|
|
71
|
+
}, B = I.totalSteps > 0 ? Math.round(I.completedSteps / I.totalSteps * 100) : 0, V = j.isPending || M.isPending, H = j.error ?? M.error;
|
|
72
|
+
return /* @__PURE__ */ _(y, {
|
|
73
|
+
treatment: "glass",
|
|
74
|
+
className: "mb-6 border-border-seam p-5",
|
|
75
|
+
children: /* @__PURE__ */ v("section", {
|
|
76
|
+
"data-tour": "hookmovies-onboarding-checklist",
|
|
77
|
+
"aria-labelledby": "hookmovies-getting-started-title",
|
|
78
|
+
children: [
|
|
79
|
+
/* @__PURE__ */ v("div", {
|
|
80
|
+
className: "flex items-start justify-between gap-3",
|
|
81
|
+
children: [/* @__PURE__ */ v("div", {
|
|
82
|
+
className: "min-w-0",
|
|
83
|
+
children: [/* @__PURE__ */ _("h2", {
|
|
84
|
+
id: "hookmovies-getting-started-title",
|
|
85
|
+
className: "text-lg font-semibold text-text-primary",
|
|
86
|
+
children: L ? T("hookmovies.onboarding.doneTitle", "You're all set up") : T("hookmovies.onboarding.title", "Get started with HookMovies")
|
|
87
|
+
}), /* @__PURE__ */ _("p", {
|
|
88
|
+
className: "mt-1 text-sm text-text-secondary",
|
|
89
|
+
children: L ? T("hookmovies.onboarding.doneDescription", "Every getting-started step is done. You can hide this checklist now.") : T("hookmovies.onboarding.description", "{{done}} of {{total}} steps done. Each step opens the page where you do it.", {
|
|
90
|
+
done: I.completedSteps,
|
|
91
|
+
total: I.totalSteps
|
|
92
|
+
})
|
|
93
|
+
})]
|
|
94
|
+
}), /* @__PURE__ */ _("button", {
|
|
95
|
+
type: "button",
|
|
96
|
+
onClick: z,
|
|
97
|
+
"aria-label": T("hookmovies.onboarding.hide", "Hide getting-started checklist"),
|
|
98
|
+
className: "shrink-0 rounded-lg p-1.5 text-text-muted transition-colors hover:bg-bg-sunken hover:text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]",
|
|
99
|
+
children: /* @__PURE__ */ _(g, { className: "h-4 w-4" })
|
|
100
|
+
})]
|
|
101
|
+
}),
|
|
102
|
+
/* @__PURE__ */ _("div", {
|
|
103
|
+
className: "mt-4 h-1.5 w-full overflow-hidden rounded-full bg-bg-sunken",
|
|
104
|
+
role: "progressbar",
|
|
105
|
+
"aria-valuemin": 0,
|
|
106
|
+
"aria-valuemax": 100,
|
|
107
|
+
"aria-valuenow": B,
|
|
108
|
+
"aria-label": T("hookmovies.onboarding.progress", "Getting-started progress"),
|
|
109
|
+
children: /* @__PURE__ */ _("div", {
|
|
110
|
+
className: "h-full rounded-full bg-action-primary-bg transition-[width] duration-300",
|
|
111
|
+
style: { width: `${B}%` }
|
|
112
|
+
})
|
|
113
|
+
}),
|
|
114
|
+
/* @__PURE__ */ _("ol", {
|
|
115
|
+
className: "mt-4 divide-y divide-border-seam",
|
|
116
|
+
children: I.steps.map((e) => {
|
|
117
|
+
let t = b[e.key];
|
|
118
|
+
return /* @__PURE__ */ v("li", {
|
|
119
|
+
"data-step": e.key,
|
|
120
|
+
"data-completed": e.completed ? "true" : "false",
|
|
121
|
+
className: "flex items-center gap-3 py-3",
|
|
122
|
+
children: [
|
|
123
|
+
e.completed ? /* @__PURE__ */ _(d, {
|
|
124
|
+
className: "h-5 w-5 shrink-0 text-status-success",
|
|
125
|
+
"aria-hidden": "true"
|
|
126
|
+
}) : /* @__PURE__ */ _(f, {
|
|
127
|
+
className: "h-5 w-5 shrink-0 text-text-muted",
|
|
128
|
+
"aria-hidden": "true"
|
|
129
|
+
}),
|
|
130
|
+
/* @__PURE__ */ v("div", {
|
|
131
|
+
className: "min-w-0 flex-1",
|
|
132
|
+
children: [/* @__PURE__ */ v("p", {
|
|
133
|
+
className: o("text-sm font-medium", e.completed ? "text-text-secondary line-through" : "text-text-primary"),
|
|
134
|
+
children: [T(...t.label), /* @__PURE__ */ _("span", {
|
|
135
|
+
className: "sr-only",
|
|
136
|
+
children: e.completed ? T("hookmovies.onboarding.stepDone", " (done)") : T("hookmovies.onboarding.stepTodo", " (to do)")
|
|
137
|
+
})]
|
|
138
|
+
}), /* @__PURE__ */ _("p", {
|
|
139
|
+
className: "mt-0.5 text-xs text-text-secondary",
|
|
140
|
+
children: T(...t.description)
|
|
141
|
+
})]
|
|
142
|
+
}),
|
|
143
|
+
!e.completed && /* @__PURE__ */ v("button", {
|
|
144
|
+
type: "button",
|
|
145
|
+
onClick: () => E(t.path),
|
|
146
|
+
className: "inline-flex shrink-0 items-center gap-1 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary transition-colors hover:border-border-strong focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]",
|
|
147
|
+
children: [T(...t.action), /* @__PURE__ */ _(u, {
|
|
148
|
+
className: "h-3.5 w-3.5",
|
|
149
|
+
"aria-hidden": "true"
|
|
150
|
+
})]
|
|
151
|
+
})
|
|
152
|
+
]
|
|
153
|
+
}, e.key);
|
|
154
|
+
})
|
|
155
|
+
}),
|
|
156
|
+
/* @__PURE__ */ v("div", {
|
|
157
|
+
"data-tour": "hookmovies-onboarding-sample-data",
|
|
158
|
+
className: "mt-4 flex flex-col gap-3 rounded-xl border border-border-seam bg-bg-sunken p-4 sm:flex-row sm:items-center sm:justify-between",
|
|
159
|
+
children: [/* @__PURE__ */ v("div", {
|
|
160
|
+
className: "min-w-0",
|
|
161
|
+
children: [
|
|
162
|
+
/* @__PURE__ */ v("p", {
|
|
163
|
+
className: "flex items-center gap-2 text-sm font-medium text-text-primary",
|
|
164
|
+
children: [/* @__PURE__ */ _(m, {
|
|
165
|
+
className: "h-4 w-4 text-action-primary-bg",
|
|
166
|
+
"aria-hidden": "true"
|
|
167
|
+
}), I.demoDataLoaded ? T("hookmovies.onboarding.sample.loadedTitle", "Sample data is loaded") : T("hookmovies.onboarding.sample.title", "Explore with sample data")]
|
|
168
|
+
}),
|
|
169
|
+
/* @__PURE__ */ _("p", {
|
|
170
|
+
className: "mt-1 text-xs text-text-secondary",
|
|
171
|
+
children: I.demoDataLoaded ? T("hookmovies.onboarding.sample.loadedDescription", "Items marked (Sample) are examples. They never count towards this checklist, and removing them leaves your own work untouched.") : T("hookmovies.onboarding.sample.description", "Adds a sample studio, cast, a published film, a draft series, a production and an open audition so you can try every module first.")
|
|
172
|
+
}),
|
|
173
|
+
H && /* @__PURE__ */ _("p", {
|
|
174
|
+
role: "alert",
|
|
175
|
+
className: "mt-2 text-xs text-status-error",
|
|
176
|
+
children: H.message
|
|
177
|
+
})
|
|
178
|
+
]
|
|
179
|
+
}), I.demoDataLoaded ? /* @__PURE__ */ v("button", {
|
|
180
|
+
type: "button",
|
|
181
|
+
disabled: V,
|
|
182
|
+
onClick: () => M.mutate(),
|
|
183
|
+
className: "inline-flex shrink-0 items-center justify-center gap-2 rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:border-border-strong disabled:opacity-60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]",
|
|
184
|
+
children: [M.isPending ? /* @__PURE__ */ _(p, {
|
|
185
|
+
className: "h-4 w-4 animate-spin",
|
|
186
|
+
"aria-hidden": "true"
|
|
187
|
+
}) : /* @__PURE__ */ _(h, {
|
|
188
|
+
className: "h-4 w-4",
|
|
189
|
+
"aria-hidden": "true"
|
|
190
|
+
}), T("hookmovies.onboarding.sample.remove", "Remove sample data")]
|
|
191
|
+
}) : /* @__PURE__ */ v("button", {
|
|
192
|
+
type: "button",
|
|
193
|
+
disabled: V,
|
|
194
|
+
onClick: () => j.mutate(),
|
|
195
|
+
className: "inline-flex shrink-0 items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90 disabled:opacity-60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]",
|
|
196
|
+
children: [j.isPending ? /* @__PURE__ */ _(p, {
|
|
197
|
+
className: "h-4 w-4 animate-spin",
|
|
198
|
+
"aria-hidden": "true"
|
|
199
|
+
}) : /* @__PURE__ */ _(m, {
|
|
200
|
+
className: "h-4 w-4",
|
|
201
|
+
"aria-hidden": "true"
|
|
202
|
+
}), T("hookmovies.onboarding.sample.load", "Load sample data")]
|
|
203
|
+
})]
|
|
204
|
+
})
|
|
205
|
+
]
|
|
206
|
+
})
|
|
207
|
+
});
|
|
208
|
+
};
|
|
209
|
+
//#endregion
|
|
210
|
+
export { T as GettingStartedChecklist };
|
|
211
|
+
|
|
212
|
+
//# sourceMappingURL=GettingStartedChecklist.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GettingStartedChecklist.js","names":[],"sources":["../../../src/hookmovies/components/GettingStartedChecklist.tsx"],"sourcesContent":["/**\n * Getting-started checklist (BOFF-3150)\n * @module hookmovies/components\n *\n * Shown on the HookMovies overview until the viewer hides it. Progress comes\n * from `hokOnboardingStatus`, which the service computes from the workspace's\n * own content, so existing workspaces see their real progress. The optional\n * sample data (a studio, people, a film, a series, a production and an open\n * audition) lets a new user explore every module before creating anything;\n * it never ticks a step and can be removed in one click.\n *\n * Hiding is a per-viewer, per-workspace convenience kept in localStorage; the\n * checklist simply reappears if storage is unavailable.\n */\n\nimport { type FC, useEffect, useRef, useState } from 'react';\nimport { ArrowRight, CheckCircle2, Circle, Loader2, Sparkles, Trash2, X } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\n\nimport { useHookMovies } from '../context/HookMoviesContext';\nimport { useHookMoviesActivation } from '../events/activation';\nimport {\n type HokOnboardingStepKey,\n useLoadDemoData,\n useOnboardingStatus,\n useRemoveDemoData,\n} from '../hooks/useHookMoviesApi';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { cn } from '../utils/cn';\n\ninterface StepCopy {\n label: [key: string, fallback: string];\n description: [key: string, fallback: string];\n action: [key: string, fallback: string];\n path: string;\n}\n\nconst STEP_COPY: Record<HokOnboardingStepKey, StepCopy> = {\n SET_PREFERENCES: {\n label: ['hookmovies.onboarding.steps.preferences.label', 'Set your viewing preferences'],\n description: [\n 'hookmovies.onboarding.steps.preferences.description',\n 'Pick your languages and content types so discovery shows what you want to watch.',\n ],\n action: ['hookmovies.onboarding.steps.preferences.action', 'Set preferences'],\n path: 'onboarding/languages',\n },\n CREATE_TITLE: {\n label: ['hookmovies.onboarding.steps.createTitle.label', 'Add your first title'],\n description: [\n 'hookmovies.onboarding.steps.createTitle.description',\n 'Create a movie, series or short in the catalog with its synopsis, genres and artwork.',\n ],\n action: ['hookmovies.onboarding.steps.createTitle.action', 'New title'],\n path: 'catalog/titles/new',\n },\n PUBLISH_TITLE: {\n label: ['hookmovies.onboarding.steps.publishTitle.label', 'Publish a title'],\n description: [\n 'hookmovies.onboarding.steps.publishTitle.description',\n 'Move a title through review to Published so viewers can find it in Browse.',\n ],\n action: ['hookmovies.onboarding.steps.publishTitle.action', 'Open catalog'],\n path: 'catalog/titles',\n },\n START_PRODUCTION: {\n label: ['hookmovies.onboarding.steps.startProduction.label', 'Start a production'],\n description: [\n 'hookmovies.onboarding.steps.startProduction.description',\n 'Track a project from development to release with its budget and schedule.',\n ],\n action: ['hookmovies.onboarding.steps.startProduction.action', 'New production'],\n path: 'productions/new',\n },\n OPEN_AUDITION: {\n label: ['hookmovies.onboarding.steps.openAudition.label', 'Open an audition'],\n description: [\n 'hookmovies.onboarding.steps.openAudition.description',\n 'Post a role for one of your productions so talent can apply with a self-tape.',\n ],\n action: ['hookmovies.onboarding.steps.openAudition.action', 'Post a role'],\n path: 'auditions/new',\n },\n};\n\nconst hiddenKey = (workspaceId?: string, userId?: string): string =>\n `hookmovies:gettingStarted:hidden:${workspaceId ?? 'default'}:${userId ?? 'anonymous'}`;\nconst completedKey = (workspaceId?: string): string =>\n `hookmovies:gettingStarted:completedTracked:${workspaceId ?? 'default'}`;\n\nfunction readFlag(key: string): boolean {\n try {\n return window.localStorage.getItem(key) === '1';\n } catch {\n return false;\n }\n}\n\nfunction writeFlag(key: string): void {\n try {\n window.localStorage.setItem(key, '1');\n } catch {\n // Storage unavailable (private mode, blocked site data): the flag is a convenience only.\n }\n}\n\nexport const GettingStartedChecklist: FC = () => {\n const tr = useTr();\n const { navigateTo, workspaceId, currentUser } = useHookMovies();\n const track = useHookMoviesActivation();\n const status = useOnboardingStatus();\n const loadDemo = useLoadDemoData();\n const removeDemo = useRemoveDemoData();\n\n const storageKey = hiddenKey(workspaceId, currentUser?.id);\n const [hidden, setHidden] = useState(() => readFlag(storageKey));\n useEffect(() => setHidden(readFlag(storageKey)), [storageKey]);\n\n const data = status.data;\n const allDone = Boolean(data && data.totalSteps > 0 && data.completedSteps === data.totalSteps);\n\n // Record checklist completion once per workspace (per browser).\n const trackedCompletion = useRef(false);\n useEffect(() => {\n if (!allDone || trackedCompletion.current) return;\n trackedCompletion.current = true;\n const key = completedKey(workspaceId);\n if (readFlag(key)) return;\n writeFlag(key);\n track('hookmovies.onboarding.checklist_completed');\n }, [allDone, workspaceId, track]);\n\n // A viewer without catalog access gets a FORBIDDEN here; the overview stays clean.\n if (hidden || status.isError || !data) return null;\n\n const hide = (): void => {\n writeFlag(storageKey);\n setHidden(true);\n };\n\n const progress =\n data.totalSteps > 0 ? Math.round((data.completedSteps / data.totalSteps) * 100) : 0;\n const demoBusy = loadDemo.isPending || removeDemo.isPending;\n const demoError = loadDemo.error ?? removeDemo.error;\n\n return (\n <GlassCard treatment=\"glass\" className=\"mb-6 border-border-seam p-5\">\n <section\n data-tour=\"hookmovies-onboarding-checklist\"\n aria-labelledby=\"hookmovies-getting-started-title\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <h2\n id=\"hookmovies-getting-started-title\"\n className=\"text-lg font-semibold text-text-primary\"\n >\n {allDone\n ? tr('hookmovies.onboarding.doneTitle', \"You're all set up\")\n : tr('hookmovies.onboarding.title', 'Get started with HookMovies')}\n </h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {allDone\n ? tr(\n 'hookmovies.onboarding.doneDescription',\n 'Every getting-started step is done. You can hide this checklist now.'\n )\n : tr(\n 'hookmovies.onboarding.description',\n '{{done}} of {{total}} steps done. Each step opens the page where you do it.',\n { done: data.completedSteps, total: data.totalSteps }\n )}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={hide}\n aria-label={tr('hookmovies.onboarding.hide', 'Hide getting-started checklist')}\n className=\"shrink-0 rounded-lg p-1.5 text-text-muted transition-colors hover:bg-bg-sunken hover:text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n\n <div\n className=\"mt-4 h-1.5 w-full overflow-hidden rounded-full bg-bg-sunken\"\n role=\"progressbar\"\n aria-valuemin={0}\n aria-valuemax={100}\n aria-valuenow={progress}\n aria-label={tr('hookmovies.onboarding.progress', 'Getting-started progress')}\n >\n <div\n className=\"h-full rounded-full bg-action-primary-bg transition-[width] duration-300\"\n style={{ width: `${progress}%` }}\n />\n </div>\n\n <ol className=\"mt-4 divide-y divide-border-seam\">\n {data.steps.map((step) => {\n const copy = STEP_COPY[step.key];\n return (\n <li\n key={step.key}\n data-step={step.key}\n data-completed={step.completed ? 'true' : 'false'}\n className=\"flex items-center gap-3 py-3\"\n >\n {step.completed ? (\n <CheckCircle2\n className=\"h-5 w-5 shrink-0 text-status-success\"\n aria-hidden=\"true\"\n />\n ) : (\n <Circle className=\"h-5 w-5 shrink-0 text-text-muted\" aria-hidden=\"true\" />\n )}\n <div className=\"min-w-0 flex-1\">\n <p\n className={cn(\n 'text-sm font-medium',\n step.completed ? 'text-text-secondary line-through' : 'text-text-primary'\n )}\n >\n {tr(...copy.label)}\n <span className=\"sr-only\">\n {step.completed\n ? tr('hookmovies.onboarding.stepDone', ' (done)')\n : tr('hookmovies.onboarding.stepTodo', ' (to do)')}\n </span>\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">{tr(...copy.description)}</p>\n </div>\n {!step.completed && (\n <button\n type=\"button\"\n onClick={() => navigateTo(copy.path)}\n className=\"inline-flex shrink-0 items-center gap-1 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary transition-colors hover:border-border-strong focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n {tr(...copy.action)}\n <ArrowRight className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n </button>\n )}\n </li>\n );\n })}\n </ol>\n\n <div\n data-tour=\"hookmovies-onboarding-sample-data\"\n className=\"mt-4 flex flex-col gap-3 rounded-xl border border-border-seam bg-bg-sunken p-4 sm:flex-row sm:items-center sm:justify-between\"\n >\n <div className=\"min-w-0\">\n <p className=\"flex items-center gap-2 text-sm font-medium text-text-primary\">\n <Sparkles className=\"h-4 w-4 text-action-primary-bg\" aria-hidden=\"true\" />\n {data.demoDataLoaded\n ? tr('hookmovies.onboarding.sample.loadedTitle', 'Sample data is loaded')\n : tr('hookmovies.onboarding.sample.title', 'Explore with sample data')}\n </p>\n <p className=\"mt-1 text-xs text-text-secondary\">\n {data.demoDataLoaded\n ? tr(\n 'hookmovies.onboarding.sample.loadedDescription',\n 'Items marked (Sample) are examples. They never count towards this checklist, and removing them leaves your own work untouched.'\n )\n : tr(\n 'hookmovies.onboarding.sample.description',\n 'Adds a sample studio, cast, a published film, a draft series, a production and an open audition so you can try every module first.'\n )}\n </p>\n {demoError && (\n <p role=\"alert\" className=\"mt-2 text-xs text-status-error\">\n {demoError.message}\n </p>\n )}\n </div>\n {data.demoDataLoaded ? (\n <button\n type=\"button\"\n disabled={demoBusy}\n onClick={() => removeDemo.mutate()}\n className=\"inline-flex shrink-0 items-center justify-center gap-2 rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:border-border-strong disabled:opacity-60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n {removeDemo.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" aria-hidden=\"true\" />\n ) : (\n <Trash2 className=\"h-4 w-4\" aria-hidden=\"true\" />\n )}\n {tr('hookmovies.onboarding.sample.remove', 'Remove sample data')}\n </button>\n ) : (\n <button\n type=\"button\"\n disabled={demoBusy}\n onClick={() => loadDemo.mutate()}\n className=\"inline-flex shrink-0 items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90 disabled:opacity-60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n {loadDemo.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" aria-hidden=\"true\" />\n ) : (\n <Sparkles className=\"h-4 w-4\" aria-hidden=\"true\" />\n )}\n {tr('hookmovies.onboarding.sample.load', 'Load sample data')}\n </button>\n )}\n </div>\n </section>\n </GlassCard>\n );\n};\n"],"mappings":"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"}
|
|
@@ -17,6 +17,12 @@ interface PageLayoutProps {
|
|
|
17
17
|
* directly under the header so every page is self-explanatory to a new user.
|
|
18
18
|
*/
|
|
19
19
|
purpose?: ReactNode;
|
|
20
|
+
/**
|
|
21
|
+
* Stable product-tour anchor prefix (BOFF-3150). Renders
|
|
22
|
+
* `data-tour="{dataTour}-header"` on the page header so seeded tours in
|
|
23
|
+
* global-tours-svc can target it. Use `hookmovies-{page}`.
|
|
24
|
+
*/
|
|
25
|
+
dataTour?: string;
|
|
20
26
|
}
|
|
21
27
|
export declare const PageLayout: FC<PageLayoutProps>;
|
|
22
28
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PageLayout.d.ts","sourceRoot":"","sources":["../../../src/hookmovies/components/PageLayout.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAI3C,UAAU,eAAe;IACvB,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"PageLayout.d.ts","sourceRoot":"","sources":["../../../src/hookmovies/components/PageLayout.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAI3C,UAAU,eAAe;IACvB,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,UAAU,EAAE,EAAE,CAAC,eAAe,CAuC1C,CAAC"}
|
|
@@ -2,10 +2,11 @@ import { cn as e } from "../utils/cn.js";
|
|
|
2
2
|
import { jsx as t, jsxs as n } from "react/jsx-runtime";
|
|
3
3
|
import { AuroraBackground as r, PagePurpose as i } from "@burdenoff/fe-libs/ui";
|
|
4
4
|
//#region src/hookmovies/components/PageLayout.tsx
|
|
5
|
-
var a = ({ title: a, description: o, actions: s, children: c, className: l, hero: u = !1, purpose: d }) => /* @__PURE__ */ n("div", {
|
|
5
|
+
var a = ({ title: a, description: o, actions: s, children: c, className: l, hero: u = !1, purpose: d, dataTour: f }) => /* @__PURE__ */ n("div", {
|
|
6
6
|
className: e("flex flex-col h-full", l),
|
|
7
7
|
children: [
|
|
8
8
|
/* @__PURE__ */ n("div", {
|
|
9
|
+
"data-tour": f ? `${f}-header` : void 0,
|
|
9
10
|
className: e("relative overflow-hidden flex items-center justify-between gap-4 px-6 py-4 border-b border-border-seam", u ? "bg-hero-mesh" : "bg-bg-surface"),
|
|
10
11
|
children: [
|
|
11
12
|
u && /* @__PURE__ */ t(r, { intensity: "subtle" }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PageLayout.js","names":[],"sources":["../../../src/hookmovies/components/PageLayout.tsx"],"sourcesContent":["/**\n * Page Layout Component\n * @module hookmovies/components\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { AuroraBackground, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\n\ninterface PageLayoutProps {\n title: ReactNode;\n description?: string;\n actions?: ReactNode;\n children: ReactNode;\n className?: string;\n /**\n * Render a subtle aurora backdrop behind the page header. Use on the MFE's\n * primary landing/hero page only (one aurora per surface).\n */\n hero?: boolean;\n /**\n * Plain-language explanation of WHAT this page/feature is for and the\n * use-case (the page \"story\"). Rendered as a calm {@link PagePurpose} band\n * directly under the header so every page is self-explanatory to a new user.\n */\n purpose?: ReactNode;\n}\n\nexport const PageLayout: FC<PageLayoutProps> = ({\n title,\n description,\n actions,\n children,\n className,\n hero = false,\n purpose,\n}) => {\n return (\n <div className={cn('flex flex-col h-full', className)}>\n {/* Page Header */}\n <div\n className={cn(\n 'relative overflow-hidden flex items-center justify-between gap-4 px-6 py-4 border-b border-border-seam',\n hero ? 'bg-hero-mesh' : 'bg-bg-surface'\n )}\n >\n {hero && <AuroraBackground intensity=\"subtle\" />}\n <div className=\"min-w-0\">\n <h1 className=\"text-2xl font-semibold tracking-tight text-text-primary\">{title}</h1>\n {description && <p className=\"mt-1 text-sm text-text-secondary\">{description}</p>}\n </div>\n {actions && <div className=\"flex items-center gap-3 shrink-0\">{actions}</div>}\n </div>\n\n {/* Page purpose / \"story\" band — what this page is for + the use-case */}\n {purpose && (\n <div className=\"border-b border-border-seam bg-bg-surface px-6 py-3\">\n <PagePurpose>{purpose}</PagePurpose>\n </div>\n )}\n\n {/* Page Content */}\n <div className=\"flex-1 overflow-auto p-6\">{children}</div>\n </div>\n );\n};\n"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"PageLayout.js","names":[],"sources":["../../../src/hookmovies/components/PageLayout.tsx"],"sourcesContent":["/**\n * Page Layout Component\n * @module hookmovies/components\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { AuroraBackground, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\n\ninterface PageLayoutProps {\n title: ReactNode;\n description?: string;\n actions?: ReactNode;\n children: ReactNode;\n className?: string;\n /**\n * Render a subtle aurora backdrop behind the page header. Use on the MFE's\n * primary landing/hero page only (one aurora per surface).\n */\n hero?: boolean;\n /**\n * Plain-language explanation of WHAT this page/feature is for and the\n * use-case (the page \"story\"). Rendered as a calm {@link PagePurpose} band\n * directly under the header so every page is self-explanatory to a new user.\n */\n purpose?: ReactNode;\n /**\n * Stable product-tour anchor prefix (BOFF-3150). Renders\n * `data-tour=\"{dataTour}-header\"` on the page header so seeded tours in\n * global-tours-svc can target it. Use `hookmovies-{page}`.\n */\n dataTour?: string;\n}\n\nexport const PageLayout: FC<PageLayoutProps> = ({\n title,\n description,\n actions,\n children,\n className,\n hero = false,\n purpose,\n dataTour,\n}) => {\n return (\n <div className={cn('flex flex-col h-full', className)}>\n {/* Page Header */}\n <div\n data-tour={dataTour ? `${dataTour}-header` : undefined}\n className={cn(\n 'relative overflow-hidden flex items-center justify-between gap-4 px-6 py-4 border-b border-border-seam',\n hero ? 'bg-hero-mesh' : 'bg-bg-surface'\n )}\n >\n {hero && <AuroraBackground intensity=\"subtle\" />}\n <div className=\"min-w-0\">\n <h1 className=\"text-2xl font-semibold tracking-tight text-text-primary\">{title}</h1>\n {description && <p className=\"mt-1 text-sm text-text-secondary\">{description}</p>}\n </div>\n {actions && <div className=\"flex items-center gap-3 shrink-0\">{actions}</div>}\n </div>\n\n {/* Page purpose / \"story\" band — what this page is for + the use-case */}\n {purpose && (\n <div className=\"border-b border-border-seam bg-bg-surface px-6 py-3\">\n <PagePurpose>{purpose}</PagePurpose>\n </div>\n )}\n\n {/* Page Content */}\n <div className=\"flex-1 overflow-auto p-6\">{children}</div>\n </div>\n );\n};\n"],"mappings":";;;;AAkCA,IAAa,KAAmC,EAC9C,UACA,gBACA,YACA,aACA,cACA,UAAO,IACP,YACA,kBAGE,kBAAC,OAAD;CAAK,WAAW,EAAG,wBAAwB,EAAU;WAArD;EAEE,kBAAC,OAAD;GACE,aAAW,IAAW,GAAG,EAAS,WAAW,KAAA;GAC7C,WAAW,EACT,0GACA,IAAO,iBAAiB,gBACzB;aALH;IAOG,KAAQ,kBAAC,GAAD,EAAkB,WAAU,UAAW,CAAA;IAChD,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAA2D;MAAW,CAAA,EACnF,KAAe,kBAAC,KAAD;MAAG,WAAU;gBAAoC;MAAgB,CAAA,CAC7E;;IACL,KAAW,kBAAC,OAAD;KAAK,WAAU;eAAoC;KAAc,CAAA;IACzE;;EAGL,KACC,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,GAAD,EAAA,UAAc,GAAsB,CAAA;GAChC,CAAA;EAIR,kBAAC,OAAD;GAAK,WAAU;GAA4B;GAAe,CAAA;EACtD"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { EventBus } from '@burdenoff/fe-libs/shared/events';
|
|
2
|
+
|
|
3
|
+
/** The activation dictionary, in funnel order (viewer, creator, talent). */
|
|
4
|
+
export declare const HOOKMOVIES_ACTIVATION_EVENTS: readonly ["hookmovies.onboarding.first_run_completed", "hookmovies.title.viewed", "hookmovies.list.created", "hookmovies.review.submitted", "hookmovies.title.created", "hookmovies.production.created", "hookmovies.entitlement.granted", "hookmovies.audition.applied"];
|
|
5
|
+
/**
|
|
6
|
+
* Getting-started events (BOFF-3150). Not funnel steps: they describe how a
|
|
7
|
+
* new workspace uses the onboarding checklist and its sample data.
|
|
8
|
+
*/
|
|
9
|
+
export declare const HOOKMOVIES_ONBOARDING_EVENTS: readonly ["hookmovies.demo_data.loaded", "hookmovies.demo_data.removed", "hookmovies.onboarding.checklist_completed"];
|
|
10
|
+
export type HookMoviesActivationEvent = (typeof HOOKMOVIES_ACTIVATION_EVENTS)[number] | (typeof HOOKMOVIES_ONBOARDING_EVENTS)[number];
|
|
11
|
+
/** Id-only properties. Every value is an opaque identifier or a closed enum. */
|
|
12
|
+
export interface HookMoviesActivationProps {
|
|
13
|
+
titleId?: string;
|
|
14
|
+
listId?: string;
|
|
15
|
+
reviewId?: string;
|
|
16
|
+
productionId?: string;
|
|
17
|
+
entitlementId?: string;
|
|
18
|
+
auditionId?: string;
|
|
19
|
+
applicationId?: string;
|
|
20
|
+
preferencesId?: string;
|
|
21
|
+
studioId?: string;
|
|
22
|
+
/** Which screen fired a view event (e.g. `catalog`, `consumer`). */
|
|
23
|
+
surface?: string;
|
|
24
|
+
}
|
|
25
|
+
/** Emit one activation event. Never throws: analytics must not break a write. */
|
|
26
|
+
export declare function emitHookMoviesActivation(bus: EventBus, event: HookMoviesActivationEvent, workspaceId: string | null | undefined, props?: HookMoviesActivationProps): void;
|
|
27
|
+
/**
|
|
28
|
+
* Returns a stable `track(event, props)` bound to the shell's event bus and
|
|
29
|
+
* the current workspace. Outside an `EventBusProvider` the bus is a no-op.
|
|
30
|
+
*/
|
|
31
|
+
export declare function useHookMoviesActivation(): (event: HookMoviesActivationEvent, props?: HookMoviesActivationProps) => void;
|
|
32
|
+
/**
|
|
33
|
+
* Emits `hookmovies.title.viewed` once per loaded title id on a title page.
|
|
34
|
+
* Re-renders and refetches of the same title do not re-emit.
|
|
35
|
+
*/
|
|
36
|
+
export declare function useTrackTitleViewed(titleId: string | undefined, loaded: boolean, surface: string): void;
|
|
37
|
+
//# sourceMappingURL=activation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activation.d.ts","sourceRoot":"","sources":["../../../src/hookmovies/events/activation.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAIjE,4EAA4E;AAC5E,eAAO,MAAM,4BAA4B,2QAS/B,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,4BAA4B,uHAI/B,CAAC;AAEX,MAAM,MAAM,yBAAyB,GACjC,CAAC,OAAO,4BAA4B,CAAC,CAAC,MAAM,CAAC,GAC7C,CAAC,OAAO,4BAA4B,CAAC,CAAC,MAAM,CAAC,CAAC;AAElD,gFAAgF;AAChF,MAAM,WAAW,yBAAyB;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAWD,iFAAiF;AACjF,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,QAAQ,EACb,KAAK,EAAE,yBAAyB,EAChC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACtC,KAAK,GAAE,yBAA8B,GACpC,IAAI,CAQN;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,CACzC,KAAK,EAAE,yBAAyB,EAChC,KAAK,CAAC,EAAE,yBAAyB,KAC9B,IAAI,CAQR;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,MAAM,GACd,IAAI,CAQN"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useHookMovies as e } from "../context/HookMoviesContext.js";
|
|
2
|
+
import "../context/index.js";
|
|
3
|
+
import { useCallback as t, useEffect as n, useRef as r } from "react";
|
|
4
|
+
import { useEventBus as i } from "@burdenoff/fe-libs/shared/events";
|
|
5
|
+
//#region src/hookmovies/events/activation.ts
|
|
6
|
+
function a(e) {
|
|
7
|
+
let t = {};
|
|
8
|
+
for (let [n, r] of Object.entries(e)) typeof r == "string" && r.length > 0 && (t[n] = r);
|
|
9
|
+
return t;
|
|
10
|
+
}
|
|
11
|
+
function o(e, t, n, r = {}) {
|
|
12
|
+
try {
|
|
13
|
+
e.emitCustom(t, a({
|
|
14
|
+
workspaceId: n ?? void 0,
|
|
15
|
+
...r
|
|
16
|
+
}), { component: "microfe-hookmovies" });
|
|
17
|
+
} catch {}
|
|
18
|
+
}
|
|
19
|
+
function s() {
|
|
20
|
+
let n = i(), { workspaceId: r } = e();
|
|
21
|
+
return t((e, t) => o(n, e, r, t), [n, r]);
|
|
22
|
+
}
|
|
23
|
+
function c(e, t, i) {
|
|
24
|
+
let a = s(), o = r(null);
|
|
25
|
+
n(() => {
|
|
26
|
+
!t || !e || o.current === e || (o.current = e, a("hookmovies.title.viewed", {
|
|
27
|
+
titleId: e,
|
|
28
|
+
surface: i
|
|
29
|
+
}));
|
|
30
|
+
}, [
|
|
31
|
+
t,
|
|
32
|
+
e,
|
|
33
|
+
i,
|
|
34
|
+
a
|
|
35
|
+
]);
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { s as useHookMoviesActivation, c as useTrackTitleViewed };
|
|
39
|
+
|
|
40
|
+
//# sourceMappingURL=activation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activation.js","names":[],"sources":["../../../src/hookmovies/events/activation.ts"],"sourcesContent":["/**\n * HookMovies activation analytics (BOFF-3098).\n *\n * Emits the eight activation events of the HookMovies analytics dictionary\n * (`hookmovies-specs/business/status/ANALYTICS_PLAN.md`) onto the shared\n * fe-libs event bus. The host app shell's sinks take it from there: the\n * OpenTelemetry / backend-audit sinks record every event, and RybbitSink\n * forwards allow-listed names to Rybbit once the shell has a site id and the\n * visitor has granted analytics consent. MFEs never talk to Rybbit directly.\n *\n * The backend records the same names (be-sdk activity tracking in\n * `wspace-hookmovies-svc`) and the `hokActivationFunnel` query counts them from\n * the system-of-record tables, so the in-product dashboard does not depend on\n * any third-party analytics being configured.\n *\n * Payload rule: ids only (`workspaceId` plus the affected entity id). Never\n * names, emails or free text. RybbitSink additionally strips id keys before\n * anything leaves the browser, so Rybbit funnels count by event name.\n *\n * Uses `emitCustom` rather than the typed `emit` so this MFE is not coupled to\n * the fe-libs release that registers every `hookmovies.*` name (the same\n * approach as HealthyBowl and VibeControls).\n * @module hookmovies/events\n */\nimport { useCallback, useEffect, useRef } from 'react';\n\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport type { EventBus } from '@burdenoff/fe-libs/shared/events';\n\nimport { useHookMovies } from '../context';\n\n/** The activation dictionary, in funnel order (viewer, creator, talent). */\nexport const HOOKMOVIES_ACTIVATION_EVENTS = [\n 'hookmovies.onboarding.first_run_completed',\n 'hookmovies.title.viewed',\n 'hookmovies.list.created',\n 'hookmovies.review.submitted',\n 'hookmovies.title.created',\n 'hookmovies.production.created',\n 'hookmovies.entitlement.granted',\n 'hookmovies.audition.applied',\n] as const;\n\n/**\n * Getting-started events (BOFF-3150). Not funnel steps: they describe how a\n * new workspace uses the onboarding checklist and its sample data.\n */\nexport const HOOKMOVIES_ONBOARDING_EVENTS = [\n 'hookmovies.demo_data.loaded',\n 'hookmovies.demo_data.removed',\n 'hookmovies.onboarding.checklist_completed',\n] as const;\n\nexport type HookMoviesActivationEvent =\n | (typeof HOOKMOVIES_ACTIVATION_EVENTS)[number]\n | (typeof HOOKMOVIES_ONBOARDING_EVENTS)[number];\n\n/** Id-only properties. Every value is an opaque identifier or a closed enum. */\nexport interface HookMoviesActivationProps {\n titleId?: string;\n listId?: string;\n reviewId?: string;\n productionId?: string;\n entitlementId?: string;\n auditionId?: string;\n applicationId?: string;\n preferencesId?: string;\n studioId?: string;\n /** Which screen fired a view event (e.g. `catalog`, `consumer`). */\n surface?: string;\n}\n\n/** Drop undefined keys so sinks never see `{ titleId: undefined }`. */\nfunction compact(props: Record<string, string | undefined>): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [key, value] of Object.entries(props)) {\n if (typeof value === 'string' && value.length > 0) out[key] = value;\n }\n return out;\n}\n\n/** Emit one activation event. Never throws: analytics must not break a write. */\nexport function emitHookMoviesActivation(\n bus: EventBus,\n event: HookMoviesActivationEvent,\n workspaceId: string | null | undefined,\n props: HookMoviesActivationProps = {}\n): void {\n try {\n bus.emitCustom(event, compact({ workspaceId: workspaceId ?? undefined, ...props }), {\n component: 'microfe-hookmovies',\n });\n } catch {\n // The bus is fire-and-forget; a sink failure must never surface to the user.\n }\n}\n\n/**\n * Returns a stable `track(event, props)` bound to the shell's event bus and\n * the current workspace. Outside an `EventBusProvider` the bus is a no-op.\n */\nexport function useHookMoviesActivation(): (\n event: HookMoviesActivationEvent,\n props?: HookMoviesActivationProps\n) => void {\n const bus = useEventBus();\n const { workspaceId } = useHookMovies();\n return useCallback(\n (event: HookMoviesActivationEvent, props?: HookMoviesActivationProps) =>\n emitHookMoviesActivation(bus, event, workspaceId, props),\n [bus, workspaceId]\n );\n}\n\n/**\n * Emits `hookmovies.title.viewed` once per loaded title id on a title page.\n * Re-renders and refetches of the same title do not re-emit.\n */\nexport function useTrackTitleViewed(\n titleId: string | undefined,\n loaded: boolean,\n surface: string\n): void {\n const track = useHookMoviesActivation();\n const lastTracked = useRef<string | null>(null);\n useEffect(() => {\n if (!loaded || !titleId || lastTracked.current === titleId) return;\n lastTracked.current = titleId;\n track('hookmovies.title.viewed', { titleId, surface });\n }, [loaded, titleId, surface, track]);\n}\n"],"mappings":";;;;;AAyEA,SAAS,EAAQ,GAAmE;CAClF,IAAM,IAA8B,EAAE;AACtC,MAAK,IAAM,CAAC,GAAK,MAAU,OAAO,QAAQ,EAAM,CAC9C,CAAI,OAAO,KAAU,YAAY,EAAM,SAAS,MAAG,EAAI,KAAO;AAEhE,QAAO;;AAIT,SAAgB,EACd,GACA,GACA,GACA,IAAmC,EAAE,EAC/B;AACN,KAAI;AACF,IAAI,WAAW,GAAO,EAAQ;GAAE,aAAa,KAAe,KAAA;GAAW,GAAG;GAAO,CAAC,EAAE,EAClF,WAAW,sBACZ,CAAC;SACI;;AASV,SAAgB,IAGN;CACR,IAAM,IAAM,GAAa,EACnB,EAAE,mBAAgB,GAAe;AACvC,QAAO,GACJ,GAAkC,MACjC,EAAyB,GAAK,GAAO,GAAa,EAAM,EAC1D,CAAC,GAAK,EAAY,CACnB;;AAOH,SAAgB,EACd,GACA,GACA,GACM;CACN,IAAM,IAAQ,GAAyB,EACjC,IAAc,EAAsB,KAAK;AAC/C,SAAgB;AACV,GAAC,KAAU,CAAC,KAAW,EAAY,YAAY,MACnD,EAAY,UAAU,GACtB,EAAM,2BAA2B;GAAE;GAAS;GAAS,CAAC;IACrD;EAAC;EAAQ;EAAS;EAAS;EAAM,CAAC"}
|
|
@@ -732,7 +732,9 @@ export interface SetPreferencesInput {
|
|
|
732
732
|
maturityLimit?: string;
|
|
733
733
|
onboarded?: boolean;
|
|
734
734
|
}
|
|
735
|
-
export declare const useSetPreferences: () => import('@tanstack/react-query').UseMutationResult<HokPreferences, Error, SetPreferencesInput,
|
|
735
|
+
export declare const useSetPreferences: () => import('@tanstack/react-query').UseMutationResult<HokPreferences, Error, SetPreferencesInput, {
|
|
736
|
+
wasOnboarded: boolean;
|
|
737
|
+
}>;
|
|
736
738
|
export type HokChannelStatus = 'DRAFT' | 'LIVE' | 'OFFLINE';
|
|
737
739
|
export interface HokChannel {
|
|
738
740
|
id: string;
|
|
@@ -1044,6 +1046,48 @@ export interface HokStudioAnalytics {
|
|
|
1044
1046
|
byTitle: HokTitleAnalyticsSummary[];
|
|
1045
1047
|
}
|
|
1046
1048
|
export declare const useStudioAnalytics: (studioId: string | undefined, period?: string) => import('@tanstack/react-query').UseQueryResult<NoInfer<HokStudioAnalytics>, Error>;
|
|
1049
|
+
export type HokActivationAudience = 'VIEWER' | 'CREATOR' | 'TALENT';
|
|
1050
|
+
export interface HokActivationStep {
|
|
1051
|
+
event: string;
|
|
1052
|
+
audience: HokActivationAudience;
|
|
1053
|
+
count: number;
|
|
1054
|
+
uniqueActors: number;
|
|
1055
|
+
firstOccurredAt: string | null;
|
|
1056
|
+
lastOccurredAt: string | null;
|
|
1057
|
+
}
|
|
1058
|
+
export interface HokActivationFunnel {
|
|
1059
|
+
period: string | null;
|
|
1060
|
+
steps: HokActivationStep[];
|
|
1061
|
+
completedSteps: number;
|
|
1062
|
+
totalSteps: number;
|
|
1063
|
+
}
|
|
1064
|
+
export declare const useActivationFunnel: (period?: string) => import('@tanstack/react-query').UseQueryResult<NoInfer<HokActivationFunnel>, Error>;
|
|
1065
|
+
export type HokOnboardingStepKey = 'SET_PREFERENCES' | 'CREATE_TITLE' | 'PUBLISH_TITLE' | 'START_PRODUCTION' | 'OPEN_AUDITION';
|
|
1066
|
+
export interface HokOnboardingStatus {
|
|
1067
|
+
steps: Array<{
|
|
1068
|
+
key: HokOnboardingStepKey;
|
|
1069
|
+
completed: boolean;
|
|
1070
|
+
}>;
|
|
1071
|
+
completedSteps: number;
|
|
1072
|
+
totalSteps: number;
|
|
1073
|
+
demoDataLoaded: boolean;
|
|
1074
|
+
}
|
|
1075
|
+
export interface HokDemoData {
|
|
1076
|
+
created: boolean;
|
|
1077
|
+
studioId: string;
|
|
1078
|
+
titleIds: string[];
|
|
1079
|
+
productionId: string | null;
|
|
1080
|
+
auditionId: string | null;
|
|
1081
|
+
}
|
|
1082
|
+
export declare const useOnboardingStatus: () => import('@tanstack/react-query').UseQueryResult<NoInfer<HokOnboardingStatus>, Error>;
|
|
1083
|
+
/**
|
|
1084
|
+
* Loads the onboarding sample data. Every HookMovies read is invalidated on
|
|
1085
|
+
* success because the sample touches the catalog, productions and auditions.
|
|
1086
|
+
*/
|
|
1087
|
+
export declare const useLoadDemoData: () => import('@tanstack/react-query').UseMutationResult<HokDemoData, Error, void, unknown>;
|
|
1088
|
+
export declare const useRemoveDemoData: () => import('@tanstack/react-query').UseMutationResult<{
|
|
1089
|
+
removed: number;
|
|
1090
|
+
}, Error, void, unknown>;
|
|
1047
1091
|
export type HokPlaybackEventType = 'IMPRESSION' | 'PLAY' | 'HEARTBEAT' | 'COMPLETE';
|
|
1048
1092
|
export interface RecordPlaybackEventInput {
|
|
1049
1093
|
titleId: string;
|