@burdenoff/microfe-hookmovies 2026.706.3 → 2026.706.4
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/pages/HomePage.js +211 -139
- package/dist/hookmovies/pages/HomePage.js.map +1 -1
- package/dist/hookmovies/pages/ai/ConciergePage.js +3 -3
- package/dist/hookmovies/pages/auditions/AuditionDetailPage.js +223 -140
- package/dist/hookmovies/pages/auditions/AuditionDetailPage.js.map +1 -1
- package/dist/hookmovies/pages/auditions/AuditionsPage.js +159 -81
- package/dist/hookmovies/pages/auditions/AuditionsPage.js.map +1 -1
- package/dist/hookmovies/pages/catalog/PeoplePage.js +175 -101
- package/dist/hookmovies/pages/catalog/PeoplePage.js.map +1 -1
- package/dist/hookmovies/pages/catalog/StudiosPage.js +189 -106
- package/dist/hookmovies/pages/catalog/StudiosPage.js.map +1 -1
- package/dist/hookmovies/pages/catalog/TitleDetailPage.js +745 -623
- package/dist/hookmovies/pages/catalog/TitleDetailPage.js.map +1 -1
- package/dist/hookmovies/pages/catalog/TitlesPage.js +218 -138
- package/dist/hookmovies/pages/catalog/TitlesPage.js.map +1 -1
- package/dist/hookmovies/pages/commerce/CheckoutPage.js +8 -8
- package/dist/hookmovies/pages/consumer/DiscoveryPage.js +14 -14
- package/dist/hookmovies/pages/consumer/TitleViewPage.js +6 -6
- package/dist/hookmovies/pages/distribution/RightsWindowsPage.js +235 -162
- package/dist/hookmovies/pages/distribution/RightsWindowsPage.js.map +1 -1
- package/dist/hookmovies/pages/library/EntitlementsPage.js +447 -221
- package/dist/hookmovies/pages/library/EntitlementsPage.js.map +1 -1
- package/dist/hookmovies/pages/library/ListsPage.js +242 -166
- package/dist/hookmovies/pages/library/ListsPage.js.map +1 -1
- package/dist/hookmovies/pages/live/LivePage.js +12 -12
- package/dist/hookmovies/pages/localization/LocalizationPage.js +10 -10
- package/dist/hookmovies/pages/productions/ProductionDetailPage.js +265 -179
- package/dist/hookmovies/pages/productions/ProductionDetailPage.js.map +1 -1
- package/dist/hookmovies/pages/productions/ProductionsPage.js +264 -71
- package/dist/hookmovies/pages/productions/ProductionsPage.js.map +1 -1
- package/dist/hookmovies/pages/social/WatchPartyRoomPage.js +205 -205
- package/dist/hookmovies/pages/social/WatchPartyRoomPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,173 +3,247 @@ import { useCreatePerson as t, usePeople as n } from "../../hooks/useHookMoviesA
|
|
|
3
3
|
import { EmptyState as r } from "../../components/EmptyState.js";
|
|
4
4
|
import { ErrorState as i, LoadingState as a } from "../../components/StateViews.js";
|
|
5
5
|
import { Pagination as o } from "../../components/Pagination.js";
|
|
6
|
-
import {
|
|
7
|
-
import { Languages as
|
|
8
|
-
import { jsx as
|
|
9
|
-
import { GlassCard as
|
|
6
|
+
import { useEffect as s, useState as c } from "react";
|
|
7
|
+
import { ExternalLink as l, Languages as u, Plus as d, Search as f, Tag as p, UserRound as m, Users 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
10
|
//#region src/hookmovies/pages/catalog/PeoplePage.tsx
|
|
11
|
-
var
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
var b = 24, x = "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)]", S = "mb-1.5 block text-xs font-medium text-text-secondary", C = [
|
|
12
|
+
"bg-accent-teal-subtle text-accent-teal",
|
|
13
|
+
"bg-accent-blue-subtle text-accent-blue",
|
|
14
|
+
"bg-accent-purple-subtle text-accent-purple",
|
|
15
|
+
"bg-accent-indigo-subtle text-accent-indigo"
|
|
16
|
+
], w = (e) => {
|
|
17
|
+
let t = 0;
|
|
18
|
+
for (let n = 0; n < e.length; n += 1) t = t * 31 + e.charCodeAt(n) >>> 0;
|
|
19
|
+
return C[t % C.length];
|
|
20
|
+
}, T = (e) => e.trim().split(/\s+/).slice(0, 2).map((e) => e.charAt(0).toUpperCase()).join("") || "?", E = ({ person: e }) => {
|
|
21
|
+
let t = e.tags ?? [], n = t.slice(0, 3), r = t.length - n.length;
|
|
22
|
+
return /* @__PURE__ */ v(y, {
|
|
23
|
+
treatment: "metric",
|
|
24
|
+
className: "group flex h-full flex-col border-border-seam p-4 transition-transform duration-200 hover:-translate-y-0.5 motion-reduce:transform-none motion-reduce:transition-none",
|
|
25
|
+
children: [
|
|
26
|
+
/* @__PURE__ */ v("div", {
|
|
27
|
+
className: "flex items-start gap-3",
|
|
28
|
+
children: [/* @__PURE__ */ _("span", {
|
|
29
|
+
"aria-hidden": "true",
|
|
30
|
+
className: `inline-flex h-12 w-12 shrink-0 items-center justify-center rounded-full text-sm font-semibold ring-1 ring-border-subtle ${w(e.id || e.name)}`,
|
|
31
|
+
children: T(e.name)
|
|
32
|
+
}), /* @__PURE__ */ v("div", {
|
|
33
|
+
className: "min-w-0 flex-1",
|
|
34
|
+
children: [/* @__PURE__ */ _("h3", {
|
|
35
|
+
className: "truncate text-sm font-semibold text-text-primary",
|
|
36
|
+
title: e.name,
|
|
37
|
+
children: e.name
|
|
38
|
+
}), e.knownFor ? /* @__PURE__ */ v("span", {
|
|
39
|
+
className: "mt-1 inline-flex max-w-full items-center gap-1 truncate rounded-full bg-bg-sunken px-2 py-0.5 text-[11px] font-medium text-text-secondary",
|
|
40
|
+
children: [/* @__PURE__ */ _(m, { className: "h-3 w-3 shrink-0" }), /* @__PURE__ */ _("span", {
|
|
41
|
+
className: "truncate",
|
|
42
|
+
children: e.knownFor
|
|
43
|
+
})]
|
|
44
|
+
}) : /* @__PURE__ */ _("p", {
|
|
45
|
+
className: "mt-1 text-xs text-text-secondary",
|
|
46
|
+
children: "Cast & crew"
|
|
47
|
+
})]
|
|
48
|
+
})]
|
|
49
|
+
}),
|
|
50
|
+
e.primaryLanguage && /* @__PURE__ */ v("p", {
|
|
51
|
+
className: "mt-3 inline-flex items-center gap-1.5 text-xs text-text-secondary",
|
|
52
|
+
children: [/* @__PURE__ */ _(u, { className: "h-3.5 w-3.5 shrink-0 text-accent-teal" }), /* @__PURE__ */ _("span", {
|
|
53
|
+
className: "uppercase tracking-wide",
|
|
54
|
+
children: e.primaryLanguage
|
|
55
|
+
})]
|
|
56
|
+
}),
|
|
57
|
+
e.biography && /* @__PURE__ */ _("p", {
|
|
58
|
+
className: "mt-2 line-clamp-3 text-xs leading-relaxed text-text-secondary",
|
|
59
|
+
children: e.biography
|
|
60
|
+
}),
|
|
61
|
+
n.length > 0 && /* @__PURE__ */ v("div", {
|
|
62
|
+
className: "mt-3 flex flex-wrap items-center gap-1.5",
|
|
63
|
+
children: [n.map((e) => /* @__PURE__ */ v("span", {
|
|
64
|
+
className: "inline-flex items-center gap-1 rounded-md bg-bg-sunken px-1.5 py-0.5 text-[11px] text-text-secondary",
|
|
65
|
+
children: [/* @__PURE__ */ _(p, { className: "h-2.5 w-2.5" }), e]
|
|
66
|
+
}, e)), r > 0 && /* @__PURE__ */ v("span", {
|
|
67
|
+
className: "text-[11px] text-text-secondary",
|
|
68
|
+
children: ["+", r]
|
|
69
|
+
})]
|
|
70
|
+
}),
|
|
71
|
+
e.portfolioRef && /* @__PURE__ */ v("a", {
|
|
72
|
+
href: e.portfolioRef,
|
|
73
|
+
target: "_blank",
|
|
74
|
+
rel: "noreferrer",
|
|
75
|
+
className: "mt-auto inline-flex items-center gap-1 pt-3 text-xs font-medium text-action-primary-bg hover:opacity-80",
|
|
76
|
+
children: [/* @__PURE__ */ _(l, { className: "h-3 w-3" }), "Portfolio"]
|
|
77
|
+
})
|
|
78
|
+
]
|
|
79
|
+
});
|
|
80
|
+
}, D = () => {
|
|
81
|
+
let [l, u] = c(0), [p, m] = c(!1), [C, w] = c(""), [T, D] = c("");
|
|
82
|
+
s(() => {
|
|
83
|
+
let e = setTimeout(() => D(C), 300);
|
|
84
|
+
return () => clearTimeout(e);
|
|
85
|
+
}, [C]);
|
|
86
|
+
let { data: O, isLoading: k, isError: A, refetch: j } = n({
|
|
87
|
+
limit: b,
|
|
88
|
+
offset: l * b
|
|
89
|
+
}, T), M = t(), [N, P] = c(""), [F, I] = c(""), [L, R] = c(""), [z, B] = c(""), [V, H] = c(null), U = () => {
|
|
90
|
+
P(""), I(""), R(""), B(""), H(null);
|
|
91
|
+
}, W = async (e) => {
|
|
92
|
+
if (e.preventDefault(), H(null), !N.trim()) {
|
|
93
|
+
H("Name is required.");
|
|
20
94
|
return;
|
|
21
95
|
}
|
|
22
96
|
let t = {
|
|
23
|
-
name:
|
|
24
|
-
knownFor:
|
|
25
|
-
primaryLanguage:
|
|
26
|
-
biography:
|
|
97
|
+
name: N.trim(),
|
|
98
|
+
knownFor: F.trim() || void 0,
|
|
99
|
+
primaryLanguage: L.trim() || void 0,
|
|
100
|
+
biography: z.trim() || void 0
|
|
27
101
|
};
|
|
28
102
|
try {
|
|
29
|
-
await
|
|
103
|
+
await M.mutateAsync(t), U(), m(!1);
|
|
30
104
|
} catch (e) {
|
|
31
|
-
|
|
105
|
+
H(e instanceof Error ? e.message : "Failed to create person");
|
|
32
106
|
}
|
|
33
|
-
},
|
|
34
|
-
|
|
107
|
+
}, G = (e) => {
|
|
108
|
+
w(e), u(0);
|
|
109
|
+
}, K = O?.items ?? [], q = T.trim().length > 0;
|
|
110
|
+
return /* @__PURE__ */ v(e, {
|
|
35
111
|
title: "People",
|
|
36
112
|
description: "Cast & crew directory",
|
|
37
113
|
purpose: "Actors, directors, writers, and crew across the network. People are credited on titles and can be linked to Artistrybase portfolios.",
|
|
38
|
-
actions: /* @__PURE__ */
|
|
114
|
+
actions: /* @__PURE__ */ v("button", {
|
|
39
115
|
type: "button",
|
|
40
|
-
onClick: () =>
|
|
116
|
+
onClick: () => m((e) => !e),
|
|
41
117
|
className: "inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 transition-opacity",
|
|
42
|
-
children: [/* @__PURE__ */
|
|
118
|
+
children: [/* @__PURE__ */ _(d, { className: "h-4 w-4" }), "New Person"]
|
|
43
119
|
}),
|
|
44
120
|
children: [
|
|
45
|
-
|
|
121
|
+
p && /* @__PURE__ */ v(y, {
|
|
46
122
|
treatment: "glass",
|
|
47
123
|
className: "mb-6 border-border-seam p-5",
|
|
48
|
-
children: [/* @__PURE__ */
|
|
124
|
+
children: [/* @__PURE__ */ v("div", {
|
|
49
125
|
className: "mb-3 flex items-center justify-between",
|
|
50
|
-
children: [/* @__PURE__ */
|
|
126
|
+
children: [/* @__PURE__ */ _("h2", {
|
|
51
127
|
className: "text-sm font-semibold text-text-primary",
|
|
52
128
|
children: "Add Person"
|
|
53
|
-
}), /* @__PURE__ */
|
|
129
|
+
}), /* @__PURE__ */ _("button", {
|
|
54
130
|
type: "button",
|
|
55
|
-
onClick: () =>
|
|
131
|
+
onClick: () => m(!1),
|
|
132
|
+
"aria-label": "Close form",
|
|
56
133
|
className: "rounded-md p-1 text-text-secondary hover:text-text-primary",
|
|
57
|
-
children: /* @__PURE__ */
|
|
134
|
+
children: /* @__PURE__ */ _(g, { className: "h-4 w-4" })
|
|
58
135
|
})]
|
|
59
|
-
}), /* @__PURE__ */
|
|
60
|
-
onSubmit:
|
|
136
|
+
}), /* @__PURE__ */ v("form", {
|
|
137
|
+
onSubmit: W,
|
|
61
138
|
className: "space-y-4",
|
|
62
139
|
children: [
|
|
63
|
-
|
|
140
|
+
V && /* @__PURE__ */ _("div", {
|
|
64
141
|
className: "rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
|
|
65
|
-
children:
|
|
142
|
+
children: V
|
|
66
143
|
}),
|
|
67
|
-
/* @__PURE__ */
|
|
144
|
+
/* @__PURE__ */ v("div", {
|
|
68
145
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
69
|
-
children: [/* @__PURE__ */
|
|
146
|
+
children: [/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
|
|
70
147
|
htmlFor: "person-name",
|
|
71
|
-
className:
|
|
148
|
+
className: S,
|
|
72
149
|
children: "Name *"
|
|
73
|
-
}), /* @__PURE__ */
|
|
150
|
+
}), /* @__PURE__ */ _("input", {
|
|
74
151
|
id: "person-name",
|
|
75
152
|
type: "text",
|
|
76
|
-
value:
|
|
77
|
-
onChange: (e) =>
|
|
153
|
+
value: N,
|
|
154
|
+
onChange: (e) => P(e.target.value),
|
|
78
155
|
placeholder: "e.g. Aria Mendez",
|
|
79
|
-
className:
|
|
156
|
+
className: x,
|
|
80
157
|
required: !0
|
|
81
|
-
})] }), /* @__PURE__ */
|
|
158
|
+
})] }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
|
|
82
159
|
htmlFor: "person-known",
|
|
83
|
-
className:
|
|
160
|
+
className: S,
|
|
84
161
|
children: "Known For"
|
|
85
|
-
}), /* @__PURE__ */
|
|
162
|
+
}), /* @__PURE__ */ _("input", {
|
|
86
163
|
id: "person-known",
|
|
87
164
|
type: "text",
|
|
88
|
-
value:
|
|
89
|
-
onChange: (e) =>
|
|
165
|
+
value: F,
|
|
166
|
+
onChange: (e) => I(e.target.value),
|
|
90
167
|
placeholder: "Acting",
|
|
91
|
-
className:
|
|
168
|
+
className: x
|
|
92
169
|
})] })]
|
|
93
170
|
}),
|
|
94
|
-
/* @__PURE__ */
|
|
171
|
+
/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
|
|
95
172
|
htmlFor: "person-lang",
|
|
96
|
-
className:
|
|
173
|
+
className: S,
|
|
97
174
|
children: "Primary Language"
|
|
98
|
-
}), /* @__PURE__ */
|
|
175
|
+
}), /* @__PURE__ */ _("input", {
|
|
99
176
|
id: "person-lang",
|
|
100
177
|
type: "text",
|
|
101
|
-
value:
|
|
102
|
-
onChange: (e) =>
|
|
178
|
+
value: L,
|
|
179
|
+
onChange: (e) => R(e.target.value),
|
|
103
180
|
placeholder: "en",
|
|
104
|
-
className:
|
|
181
|
+
className: x
|
|
105
182
|
})] }),
|
|
106
|
-
/* @__PURE__ */
|
|
183
|
+
/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
|
|
107
184
|
htmlFor: "person-bio",
|
|
108
|
-
className:
|
|
185
|
+
className: S,
|
|
109
186
|
children: "Biography"
|
|
110
|
-
}), /* @__PURE__ */
|
|
187
|
+
}), /* @__PURE__ */ _("textarea", {
|
|
111
188
|
id: "person-bio",
|
|
112
|
-
value:
|
|
113
|
-
onChange: (e) =>
|
|
189
|
+
value: z,
|
|
190
|
+
onChange: (e) => B(e.target.value),
|
|
114
191
|
rows: 3,
|
|
115
|
-
className:
|
|
192
|
+
className: x
|
|
116
193
|
})] }),
|
|
117
|
-
/* @__PURE__ */
|
|
194
|
+
/* @__PURE__ */ _("button", {
|
|
118
195
|
type: "submit",
|
|
119
|
-
disabled:
|
|
196
|
+
disabled: M.isPending,
|
|
120
197
|
className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50 transition-opacity",
|
|
121
|
-
children:
|
|
198
|
+
children: M.isPending ? "Adding…" : "Add Person"
|
|
122
199
|
})
|
|
123
200
|
]
|
|
124
201
|
})]
|
|
125
202
|
}),
|
|
126
|
-
|
|
127
|
-
|
|
203
|
+
/* @__PURE__ */ v("div", {
|
|
204
|
+
className: "mb-5 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",
|
|
205
|
+
children: [/* @__PURE__ */ v("div", {
|
|
206
|
+
className: "relative w-full sm:max-w-sm",
|
|
207
|
+
children: [/* @__PURE__ */ _(f, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary" }), /* @__PURE__ */ _("input", {
|
|
208
|
+
type: "search",
|
|
209
|
+
value: C,
|
|
210
|
+
onChange: (e) => G(e.target.value),
|
|
211
|
+
placeholder: "Search cast, crew, roles…",
|
|
212
|
+
"aria-label": "Search people",
|
|
213
|
+
className: "w-full rounded-lg border border-border-subtle bg-bg-surface py-2 pl-9 pr-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]"
|
|
214
|
+
})]
|
|
215
|
+
}), !k && !A && K.length > 0 && /* @__PURE__ */ v("p", {
|
|
216
|
+
className: "text-xs text-text-secondary",
|
|
217
|
+
children: [
|
|
218
|
+
K.length,
|
|
219
|
+
" ",
|
|
220
|
+
K.length === 1 ? "person" : "people",
|
|
221
|
+
q ? " matched" : " shown"
|
|
222
|
+
]
|
|
223
|
+
})]
|
|
224
|
+
}),
|
|
225
|
+
k ? /* @__PURE__ */ _(a, { label: "Loading people…" }) : A ? /* @__PURE__ */ _(i, { onRetry: () => void j() }) : K.length === 0 ? q ? /* @__PURE__ */ _(r, {
|
|
226
|
+
icon: /* @__PURE__ */ _(f, { className: "h-12 w-12" }),
|
|
227
|
+
title: "No matches",
|
|
228
|
+
description: `No people match “${T.trim()}”. Try a different name or role.`
|
|
229
|
+
}) : /* @__PURE__ */ _(r, {
|
|
230
|
+
icon: /* @__PURE__ */ _(h, { className: "h-12 w-12" }),
|
|
128
231
|
title: "No people yet",
|
|
129
232
|
description: "Add cast and crew to credit them on your titles."
|
|
130
|
-
}) : /* @__PURE__ */
|
|
233
|
+
}) : /* @__PURE__ */ _("div", {
|
|
131
234
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4",
|
|
132
|
-
children:
|
|
133
|
-
treatment: "metric",
|
|
134
|
-
className: "flex flex-col border-border-seam p-4",
|
|
135
|
-
children: [
|
|
136
|
-
/* @__PURE__ */ p("div", {
|
|
137
|
-
className: "flex items-center gap-3",
|
|
138
|
-
children: [/* @__PURE__ */ f("span", {
|
|
139
|
-
className: "inline-flex h-10 w-10 items-center justify-center rounded-full bg-bg-sunken text-sm font-semibold text-text-primary",
|
|
140
|
-
children: e.name.charAt(0).toUpperCase()
|
|
141
|
-
}), /* @__PURE__ */ p("div", {
|
|
142
|
-
className: "min-w-0",
|
|
143
|
-
children: [/* @__PURE__ */ f("h3", {
|
|
144
|
-
className: "truncate text-sm font-semibold text-text-primary",
|
|
145
|
-
children: e.name
|
|
146
|
-
}), e.knownFor && /* @__PURE__ */ f("p", {
|
|
147
|
-
className: "truncate text-xs text-text-secondary",
|
|
148
|
-
children: e.knownFor
|
|
149
|
-
})]
|
|
150
|
-
})]
|
|
151
|
-
}),
|
|
152
|
-
e.primaryLanguage && /* @__PURE__ */ p("p", {
|
|
153
|
-
className: "mt-3 inline-flex items-center gap-1 text-xs text-text-secondary",
|
|
154
|
-
children: [/* @__PURE__ */ f(c, { className: "h-3 w-3" }), e.primaryLanguage]
|
|
155
|
-
}),
|
|
156
|
-
e.biography && /* @__PURE__ */ f("p", {
|
|
157
|
-
className: "mt-2 line-clamp-3 text-xs text-text-secondary",
|
|
158
|
-
children: e.biography
|
|
159
|
-
})
|
|
160
|
-
]
|
|
161
|
-
}, e.id))
|
|
235
|
+
children: K.map((e) => /* @__PURE__ */ _(E, { person: e }, e.id))
|
|
162
236
|
}),
|
|
163
|
-
!
|
|
164
|
-
page:
|
|
165
|
-
hasMore:
|
|
166
|
-
onPageChange:
|
|
167
|
-
shownCount:
|
|
237
|
+
!k && !A && /* @__PURE__ */ _(o, {
|
|
238
|
+
page: l,
|
|
239
|
+
hasMore: O?.pageInfo.hasMore ?? !1,
|
|
240
|
+
onPageChange: u,
|
|
241
|
+
shownCount: K.length
|
|
168
242
|
})
|
|
169
243
|
]
|
|
170
244
|
});
|
|
171
245
|
};
|
|
172
246
|
//#endregion
|
|
173
|
-
export {
|
|
247
|
+
export { D as PeoplePage };
|
|
174
248
|
|
|
175
249
|
//# sourceMappingURL=PeoplePage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PeoplePage.js","names":[],"sources":["../../../../src/hookmovies/pages/catalog/PeoplePage.tsx"],"sourcesContent":["/**\n * People list (Catalog)\n * @module hookmovies/pages/catalog\n *\n * Cast & crew directory via `hokListPeople` with inline creation through\n * `hokCreatePerson`. People are linked to titles through credits.\n */\n\nimport { type FC, type FormEvent, useState } from 'react';\nimport { Users, Plus, X, Languages } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingState, ErrorState } from '../../components/StateViews';\nimport { Pagination } from '../../components/Pagination';\nimport { usePeople, useCreatePerson, type CreatePersonInput } from '../../hooks/useHookMoviesApi';\n\nconst PAGE_LIMIT = 24;\nconst fieldClass =\n 'w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\nconst labelClass = 'mb-1.5 block text-xs font-medium text-text-secondary';\n\nexport const PeoplePage: FC = () => {\n const [page, setPage] = useState(0);\n const [showCreate, setShowCreate] = useState(false);\n const { data, isLoading, isError, refetch } = usePeople({\n limit: PAGE_LIMIT,\n offset: page * PAGE_LIMIT,\n });\n const createPerson = useCreatePerson();\n\n const [name, setName] = useState('');\n const [knownFor, setKnownFor] = useState('');\n const [primaryLanguage, setPrimaryLanguage] = useState('');\n const [biography, setBiography] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const resetForm = () => {\n setName('');\n setKnownFor('');\n setPrimaryLanguage('');\n setBiography('');\n setError(null);\n };\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setError(null);\n if (!name.trim()) {\n setError('Name is required.');\n return;\n }\n const input: CreatePersonInput = {\n name: name.trim(),\n knownFor: knownFor.trim() || undefined,\n primaryLanguage: primaryLanguage.trim() || undefined,\n biography: biography.trim() || undefined,\n };\n try {\n await createPerson.mutateAsync(input);\n resetForm();\n setShowCreate(false);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create person');\n }\n };\n\n const items = data?.items ?? [];\n\n return (\n <PageLayout\n title=\"People\"\n description=\"Cast & crew directory\"\n purpose=\"Actors, directors, writers, and crew across the network. People are credited on titles and can be linked to Artistrybase portfolios.\"\n actions={\n <button\n type=\"button\"\n onClick={() => setShowCreate((v) => !v)}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 transition-opacity\"\n >\n <Plus className=\"h-4 w-4\" />\n New Person\n </button>\n }\n >\n {showCreate && (\n <GlassCard treatment=\"glass\" className=\"mb-6 border-border-seam p-5\">\n <div className=\"mb-3 flex items-center justify-between\">\n <h2 className=\"text-sm font-semibold text-text-primary\">Add Person</h2>\n <button\n type=\"button\"\n onClick={() => setShowCreate(false)}\n className=\"rounded-md p-1 text-text-secondary hover:text-text-primary\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {error && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {error}\n </div>\n )}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"person-name\" className={labelClass}>\n Name *\n </label>\n <input\n id=\"person-name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. Aria Mendez\"\n className={fieldClass}\n required\n />\n </div>\n <div>\n <label htmlFor=\"person-known\" className={labelClass}>\n Known For\n </label>\n <input\n id=\"person-known\"\n type=\"text\"\n value={knownFor}\n onChange={(e) => setKnownFor(e.target.value)}\n placeholder=\"Acting\"\n className={fieldClass}\n />\n </div>\n </div>\n <div>\n <label htmlFor=\"person-lang\" className={labelClass}>\n Primary Language\n </label>\n <input\n id=\"person-lang\"\n type=\"text\"\n value={primaryLanguage}\n onChange={(e) => setPrimaryLanguage(e.target.value)}\n placeholder=\"en\"\n className={fieldClass}\n />\n </div>\n <div>\n <label htmlFor=\"person-bio\" className={labelClass}>\n Biography\n </label>\n <textarea\n id=\"person-bio\"\n value={biography}\n onChange={(e) => setBiography(e.target.value)}\n rows={3}\n className={fieldClass}\n />\n </div>\n <button\n type=\"submit\"\n disabled={createPerson.isPending}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50 transition-opacity\"\n >\n {createPerson.isPending ? 'Adding…' : 'Add Person'}\n </button>\n </form>\n </GlassCard>\n )}\n\n {isLoading ? (\n <LoadingState label=\"Loading people…\" />\n ) : isError ? (\n <ErrorState onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n <EmptyState\n icon={<Users className=\"h-12 w-12\" />}\n title=\"No people yet\"\n description=\"Add cast and crew to credit them on your titles.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\">\n {items.map((person) => (\n <GlassCard\n key={person.id}\n treatment=\"metric\"\n className=\"flex flex-col border-border-seam p-4\"\n >\n <div className=\"flex items-center gap-3\">\n <span className=\"inline-flex h-10 w-10 items-center justify-center rounded-full bg-bg-sunken text-sm font-semibold text-text-primary\">\n {person.name.charAt(0).toUpperCase()}\n </span>\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-sm font-semibold text-text-primary\">\n {person.name}\n </h3>\n {person.knownFor && (\n <p className=\"truncate text-xs text-text-secondary\">{person.knownFor}</p>\n )}\n </div>\n </div>\n {person.primaryLanguage && (\n <p className=\"mt-3 inline-flex items-center gap-1 text-xs text-text-secondary\">\n <Languages className=\"h-3 w-3\" />\n {person.primaryLanguage}\n </p>\n )}\n {person.biography && (\n <p className=\"mt-2 line-clamp-3 text-xs text-text-secondary\">{person.biography}</p>\n )}\n </GlassCard>\n ))}\n </div>\n )}\n\n {!isLoading && !isError && (\n <Pagination\n page={page}\n hasMore={data?.pageInfo.hasMore ?? false}\n onPageChange={setPage}\n shownCount={items.length}\n />\n )}\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PeoplePage.js","names":[],"sources":["../../../../src/hookmovies/pages/catalog/PeoplePage.tsx"],"sourcesContent":["/**\n * People list (Catalog)\n * @module hookmovies/pages/catalog\n *\n * Cast & crew directory via `hokListPeople` with inline creation through\n * `hokCreatePerson`. People are linked to titles through credits.\n */\n\nimport { type FC, type FormEvent, useEffect, useState } from 'react';\nimport { Users, Plus, X, Languages, Search, Tag, ExternalLink, UserRound } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingState, ErrorState } from '../../components/StateViews';\nimport { Pagination } from '../../components/Pagination';\nimport {\n usePeople,\n useCreatePerson,\n type CreatePersonInput,\n type HokPerson,\n} from '../../hooks/useHookMoviesApi';\n\nconst PAGE_LIMIT = 24;\nconst fieldClass =\n 'w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\nconst labelClass = 'mb-1.5 block text-xs font-medium text-text-secondary';\n\n/** Tasteful, deterministic avatar tint drawn from the fe-libs accent palette. */\nconst AVATAR_TINTS = [\n 'bg-accent-teal-subtle text-accent-teal',\n 'bg-accent-blue-subtle text-accent-blue',\n 'bg-accent-purple-subtle text-accent-purple',\n 'bg-accent-indigo-subtle text-accent-indigo',\n] as const;\n\nconst tintFor = (seed: string): string => {\n let hash = 0;\n for (let i = 0; i < seed.length; i += 1) {\n hash = (hash * 31 + seed.charCodeAt(i)) >>> 0;\n }\n return AVATAR_TINTS[hash % AVATAR_TINTS.length];\n};\n\nconst initialsFor = (name: string): string =>\n name\n .trim()\n .split(/\\s+/)\n .slice(0, 2)\n .map((part) => part.charAt(0).toUpperCase())\n .join('') || '?';\n\nconst PersonCard: FC<{ person: HokPerson }> = ({ person }) => {\n const tags = person.tags ?? [];\n const visibleTags = tags.slice(0, 3);\n const extraTags = tags.length - visibleTags.length;\n\n return (\n <GlassCard\n treatment=\"metric\"\n className=\"group flex h-full flex-col border-border-seam p-4 transition-transform duration-200 hover:-translate-y-0.5 motion-reduce:transform-none motion-reduce:transition-none\"\n >\n <div className=\"flex items-start gap-3\">\n <span\n aria-hidden=\"true\"\n className={`inline-flex h-12 w-12 shrink-0 items-center justify-center rounded-full text-sm font-semibold ring-1 ring-border-subtle ${tintFor(\n person.id || person.name\n )}`}\n >\n {initialsFor(person.name)}\n </span>\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"truncate text-sm font-semibold text-text-primary\" title={person.name}>\n {person.name}\n </h3>\n {person.knownFor ? (\n <span className=\"mt-1 inline-flex max-w-full items-center gap-1 truncate rounded-full bg-bg-sunken px-2 py-0.5 text-[11px] font-medium text-text-secondary\">\n <UserRound className=\"h-3 w-3 shrink-0\" />\n <span className=\"truncate\">{person.knownFor}</span>\n </span>\n ) : (\n <p className=\"mt-1 text-xs text-text-secondary\">Cast & crew</p>\n )}\n </div>\n </div>\n\n {person.primaryLanguage && (\n <p className=\"mt-3 inline-flex items-center gap-1.5 text-xs text-text-secondary\">\n <Languages className=\"h-3.5 w-3.5 shrink-0 text-accent-teal\" />\n <span className=\"uppercase tracking-wide\">{person.primaryLanguage}</span>\n </p>\n )}\n\n {person.biography && (\n <p className=\"mt-2 line-clamp-3 text-xs leading-relaxed text-text-secondary\">\n {person.biography}\n </p>\n )}\n\n {visibleTags.length > 0 && (\n <div className=\"mt-3 flex flex-wrap items-center gap-1.5\">\n {visibleTags.map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center gap-1 rounded-md bg-bg-sunken px-1.5 py-0.5 text-[11px] text-text-secondary\"\n >\n <Tag className=\"h-2.5 w-2.5\" />\n {tag}\n </span>\n ))}\n {extraTags > 0 && <span className=\"text-[11px] text-text-secondary\">+{extraTags}</span>}\n </div>\n )}\n\n {person.portfolioRef && (\n <a\n href={person.portfolioRef}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"mt-auto inline-flex items-center gap-1 pt-3 text-xs font-medium text-action-primary-bg hover:opacity-80\"\n >\n <ExternalLink className=\"h-3 w-3\" />\n Portfolio\n </a>\n )}\n </GlassCard>\n );\n};\n\nexport const PeoplePage: FC = () => {\n const [page, setPage] = useState(0);\n const [showCreate, setShowCreate] = useState(false);\n\n // Search — controlled input debounced into the query so typing stays snappy\n // without firing a request per keystroke.\n const [searchInput, setSearchInput] = useState('');\n const [search, setSearch] = useState('');\n useEffect(() => {\n const t = setTimeout(() => setSearch(searchInput), 300);\n return () => clearTimeout(t);\n }, [searchInput]);\n\n const { data, isLoading, isError, refetch } = usePeople(\n { limit: PAGE_LIMIT, offset: page * PAGE_LIMIT },\n search\n );\n const createPerson = useCreatePerson();\n\n const [name, setName] = useState('');\n const [knownFor, setKnownFor] = useState('');\n const [primaryLanguage, setPrimaryLanguage] = useState('');\n const [biography, setBiography] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const resetForm = () => {\n setName('');\n setKnownFor('');\n setPrimaryLanguage('');\n setBiography('');\n setError(null);\n };\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setError(null);\n if (!name.trim()) {\n setError('Name is required.');\n return;\n }\n const input: CreatePersonInput = {\n name: name.trim(),\n knownFor: knownFor.trim() || undefined,\n primaryLanguage: primaryLanguage.trim() || undefined,\n biography: biography.trim() || undefined,\n };\n try {\n await createPerson.mutateAsync(input);\n resetForm();\n setShowCreate(false);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create person');\n }\n };\n\n const handleSearchChange = (value: string) => {\n setSearchInput(value);\n setPage(0);\n };\n\n const items = data?.items ?? [];\n const isSearching = search.trim().length > 0;\n\n return (\n <PageLayout\n title=\"People\"\n description=\"Cast & crew directory\"\n purpose=\"Actors, directors, writers, and crew across the network. People are credited on titles and can be linked to Artistrybase portfolios.\"\n actions={\n <button\n type=\"button\"\n onClick={() => setShowCreate((v) => !v)}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 transition-opacity\"\n >\n <Plus className=\"h-4 w-4\" />\n New Person\n </button>\n }\n >\n {showCreate && (\n <GlassCard treatment=\"glass\" className=\"mb-6 border-border-seam p-5\">\n <div className=\"mb-3 flex items-center justify-between\">\n <h2 className=\"text-sm font-semibold text-text-primary\">Add Person</h2>\n <button\n type=\"button\"\n onClick={() => setShowCreate(false)}\n aria-label=\"Close form\"\n className=\"rounded-md p-1 text-text-secondary hover:text-text-primary\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {error && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {error}\n </div>\n )}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"person-name\" className={labelClass}>\n Name *\n </label>\n <input\n id=\"person-name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. Aria Mendez\"\n className={fieldClass}\n required\n />\n </div>\n <div>\n <label htmlFor=\"person-known\" className={labelClass}>\n Known For\n </label>\n <input\n id=\"person-known\"\n type=\"text\"\n value={knownFor}\n onChange={(e) => setKnownFor(e.target.value)}\n placeholder=\"Acting\"\n className={fieldClass}\n />\n </div>\n </div>\n <div>\n <label htmlFor=\"person-lang\" className={labelClass}>\n Primary Language\n </label>\n <input\n id=\"person-lang\"\n type=\"text\"\n value={primaryLanguage}\n onChange={(e) => setPrimaryLanguage(e.target.value)}\n placeholder=\"en\"\n className={fieldClass}\n />\n </div>\n <div>\n <label htmlFor=\"person-bio\" className={labelClass}>\n Biography\n </label>\n <textarea\n id=\"person-bio\"\n value={biography}\n onChange={(e) => setBiography(e.target.value)}\n rows={3}\n className={fieldClass}\n />\n </div>\n <button\n type=\"submit\"\n disabled={createPerson.isPending}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50 transition-opacity\"\n >\n {createPerson.isPending ? 'Adding…' : 'Add Person'}\n </button>\n </form>\n </GlassCard>\n )}\n\n {/* Search toolbar */}\n <div className=\"mb-5 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"relative w-full sm:max-w-sm\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <input\n type=\"search\"\n value={searchInput}\n onChange={(e) => handleSearchChange(e.target.value)}\n placeholder=\"Search cast, crew, roles…\"\n aria-label=\"Search people\"\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface py-2 pl-9 pr-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n {!isLoading && !isError && items.length > 0 && (\n <p className=\"text-xs text-text-secondary\">\n {items.length} {items.length === 1 ? 'person' : 'people'}\n {isSearching ? ' matched' : ' shown'}\n </p>\n )}\n </div>\n\n {isLoading ? (\n <LoadingState label=\"Loading people…\" />\n ) : isError ? (\n <ErrorState onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n isSearching ? (\n <EmptyState\n icon={<Search className=\"h-12 w-12\" />}\n title=\"No matches\"\n description={`No people match “${search.trim()}”. Try a different name or role.`}\n />\n ) : (\n <EmptyState\n icon={<Users className=\"h-12 w-12\" />}\n title=\"No people yet\"\n description=\"Add cast and crew to credit them on your titles.\"\n />\n )\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\">\n {items.map((person) => (\n <PersonCard key={person.id} person={person} />\n ))}\n </div>\n )}\n\n {!isLoading && !isError && (\n <Pagination\n page={page}\n hasMore={data?.pageInfo.hasMore ?? false}\n onPageChange={setPage}\n shownCount={items.length}\n />\n )}\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|