@burdenoff/microfe-hookmovies 2026.705.2 → 2026.706.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/hookmovies/hooks/index.js +1 -0
- package/dist/hookmovies/hooks/useFiles.js +66 -0
- package/dist/hookmovies/hooks/useFiles.js.map +1 -0
- package/dist/hookmovies/hooks/useHookMoviesApi.js +118 -32
- package/dist/hookmovies/hooks/useHookMoviesApi.js.map +1 -1
- package/dist/hookmovies/pages/HomePage.js +2 -2
- package/dist/hookmovies/pages/HomePage.js.map +1 -1
- package/dist/hookmovies/pages/auditions/AuditionDetailPage.js +100 -45
- package/dist/hookmovies/pages/auditions/AuditionDetailPage.js.map +1 -1
- package/dist/hookmovies/pages/catalog/TitleDetailPage.js +942 -266
- package/dist/hookmovies/pages/catalog/TitleDetailPage.js.map +1 -1
- package/dist/hookmovies/pages/catalog/TitlesPage.js +95 -74
- package/dist/hookmovies/pages/catalog/TitlesPage.js.map +1 -1
- package/dist/hookmovies/pages/distribution/RightsWindowsPage.js +1 -1
- package/dist/hookmovies/pages/distribution/RightsWindowsPage.js.map +1 -1
- package/dist/hookmovies/pages/library/EntitlementsPage.js +273 -75
- package/dist/hookmovies/pages/library/EntitlementsPage.js.map +1 -1
- package/dist/hookmovies/pages/library/ListsPage.js +186 -106
- package/dist/hookmovies/pages/library/ListsPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,213 +1,293 @@
|
|
|
1
1
|
import { PageLayout as e } from "../../components/PageLayout.js";
|
|
2
|
-
import { useCreateList as t, useDeleteList as n, useMyLists as r } from "../../hooks/useHookMoviesApi.js";
|
|
3
|
-
import { formatRelativeTime as
|
|
4
|
-
import { ErrorState as
|
|
5
|
-
import { EmptyState as
|
|
6
|
-
import { useState as
|
|
7
|
-
import { Globe as
|
|
8
|
-
import {
|
|
9
|
-
import { GlassCard as
|
|
2
|
+
import { useCreateList as t, useDeleteList as n, useMyLists as r, useUpdateList as i } from "../../hooks/useHookMoviesApi.js";
|
|
3
|
+
import { formatRelativeTime as a } from "../../utils/formatters.js";
|
|
4
|
+
import { ErrorState as o, LoadingState as s } from "../../components/StateViews.js";
|
|
5
|
+
import { EmptyState as c } from "../../components/EmptyState.js";
|
|
6
|
+
import { useState as l } from "react";
|
|
7
|
+
import { Globe as u, Link as d, ListVideo as ee, Lock as f, Pencil as p, Plus as m, Trash2 as h, X as te } from "lucide-react";
|
|
8
|
+
import { Fragment as g, 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/library/ListsPage.tsx
|
|
11
|
-
var
|
|
11
|
+
var b = ["WATCHLIST", "PLAYLIST"], x = [
|
|
12
12
|
"PRIVATE",
|
|
13
13
|
"UNLISTED",
|
|
14
14
|
"PUBLIC"
|
|
15
|
-
],
|
|
15
|
+
], S = "w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]", C = "mb-1.5 block text-xs font-medium text-text-secondary", w = {
|
|
16
16
|
PRIVATE: f,
|
|
17
|
-
UNLISTED:
|
|
18
|
-
PUBLIC:
|
|
19
|
-
},
|
|
20
|
-
let [
|
|
17
|
+
UNLISTED: d,
|
|
18
|
+
PUBLIC: u
|
|
19
|
+
}, T = () => {
|
|
20
|
+
let [u, d] = l(""), [f, T] = l(!1), { data: E, isLoading: D, isError: O, refetch: k } = r(u || void 0, {
|
|
21
21
|
limit: 100,
|
|
22
22
|
offset: 0
|
|
23
|
-
}),
|
|
24
|
-
|
|
23
|
+
}), A = t(), j = n(), M = i(), [N, P] = l(""), [F, I] = l(""), [L, R] = l("WATCHLIST"), [z, B] = l("PRIVATE"), [V, H] = l(null), [U, W] = l(null), [G, K] = l(null), [q, J] = l(""), [Y, X] = l("PRIVATE"), [Z, Q] = l(null), ne = (e) => {
|
|
24
|
+
Q(null), K(e.id), J(e.name), X(e.visibility);
|
|
25
|
+
}, re = () => {
|
|
26
|
+
K(null), Q(null);
|
|
27
|
+
}, ie = async (e, t) => {
|
|
28
|
+
if (e.preventDefault(), Q(null), !q.trim()) {
|
|
29
|
+
Q("Name is required.");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
await M.mutateAsync({
|
|
34
|
+
id: t,
|
|
35
|
+
input: {
|
|
36
|
+
name: q.trim(),
|
|
37
|
+
visibility: Y
|
|
38
|
+
}
|
|
39
|
+
}), K(null);
|
|
40
|
+
} catch (e) {
|
|
41
|
+
Q(e instanceof Error ? e.message : "Failed to update list (you can only edit your own)");
|
|
42
|
+
}
|
|
43
|
+
}, ae = async (e) => {
|
|
44
|
+
W(null);
|
|
25
45
|
try {
|
|
26
|
-
await
|
|
46
|
+
await j.mutateAsync(e);
|
|
27
47
|
} catch (e) {
|
|
28
|
-
|
|
48
|
+
W(e instanceof Error ? e.message : "Failed to delete list (you can only delete your own)");
|
|
29
49
|
}
|
|
30
|
-
},
|
|
31
|
-
if (e.preventDefault(),
|
|
32
|
-
|
|
50
|
+
}, oe = async (e) => {
|
|
51
|
+
if (e.preventDefault(), H(null), !N.trim()) {
|
|
52
|
+
H("Name is required.");
|
|
33
53
|
return;
|
|
34
54
|
}
|
|
35
55
|
let t = {
|
|
36
|
-
name:
|
|
37
|
-
description:
|
|
38
|
-
kind:
|
|
39
|
-
visibility:
|
|
56
|
+
name: N.trim(),
|
|
57
|
+
description: F.trim() || void 0,
|
|
58
|
+
kind: L,
|
|
59
|
+
visibility: z
|
|
40
60
|
};
|
|
41
61
|
try {
|
|
42
|
-
await
|
|
62
|
+
await A.mutateAsync(t), P(""), I(""), R("WATCHLIST"), B("PRIVATE"), T(!1);
|
|
43
63
|
} catch (e) {
|
|
44
|
-
|
|
64
|
+
H(e instanceof Error ? e.message : "Failed to create list");
|
|
45
65
|
}
|
|
46
|
-
},
|
|
47
|
-
return /* @__PURE__ */
|
|
66
|
+
}, $ = E?.items ?? [];
|
|
67
|
+
return /* @__PURE__ */ v(e, {
|
|
48
68
|
title: "My Library",
|
|
49
69
|
description: "Watchlists & playlists",
|
|
50
70
|
purpose: "Your personal collections — a watchlist of what to watch next and curated playlists you can keep private, unlisted, or public.",
|
|
51
|
-
actions: /* @__PURE__ */
|
|
71
|
+
actions: /* @__PURE__ */ v("button", {
|
|
52
72
|
type: "button",
|
|
53
|
-
onClick: () =>
|
|
73
|
+
onClick: () => T((e) => !e),
|
|
54
74
|
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",
|
|
55
|
-
children: [/* @__PURE__ */
|
|
75
|
+
children: [/* @__PURE__ */ _(m, { className: "h-4 w-4" }), "New List"]
|
|
56
76
|
}),
|
|
57
77
|
children: [
|
|
58
|
-
f && /* @__PURE__ */
|
|
78
|
+
f && /* @__PURE__ */ v(y, {
|
|
59
79
|
treatment: "glass",
|
|
60
80
|
className: "mb-6 border-border-seam p-5",
|
|
61
|
-
children: [/* @__PURE__ */
|
|
81
|
+
children: [/* @__PURE__ */ v("div", {
|
|
62
82
|
className: "mb-3 flex items-center justify-between",
|
|
63
|
-
children: [/* @__PURE__ */
|
|
83
|
+
children: [/* @__PURE__ */ _("h2", {
|
|
64
84
|
className: "text-sm font-semibold text-text-primary",
|
|
65
85
|
children: "Create List"
|
|
66
|
-
}), /* @__PURE__ */
|
|
86
|
+
}), /* @__PURE__ */ _("button", {
|
|
67
87
|
type: "button",
|
|
68
|
-
onClick: () =>
|
|
88
|
+
onClick: () => T(!1),
|
|
69
89
|
className: "rounded-md p-1 text-text-secondary hover:text-text-primary",
|
|
70
|
-
children: /* @__PURE__ */
|
|
90
|
+
children: /* @__PURE__ */ _(te, { className: "h-4 w-4" })
|
|
71
91
|
})]
|
|
72
|
-
}), /* @__PURE__ */
|
|
73
|
-
onSubmit:
|
|
92
|
+
}), /* @__PURE__ */ v("form", {
|
|
93
|
+
onSubmit: oe,
|
|
74
94
|
className: "space-y-4",
|
|
75
95
|
children: [
|
|
76
|
-
|
|
96
|
+
V && /* @__PURE__ */ _("div", {
|
|
77
97
|
className: "rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
|
|
78
|
-
children:
|
|
98
|
+
children: V
|
|
79
99
|
}),
|
|
80
|
-
/* @__PURE__ */
|
|
100
|
+
/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
|
|
81
101
|
htmlFor: "list-name",
|
|
82
|
-
className:
|
|
102
|
+
className: C,
|
|
83
103
|
children: "Name *"
|
|
84
|
-
}), /* @__PURE__ */
|
|
104
|
+
}), /* @__PURE__ */ _("input", {
|
|
85
105
|
id: "list-name",
|
|
86
106
|
type: "text",
|
|
87
|
-
value:
|
|
88
|
-
onChange: (e) =>
|
|
107
|
+
value: N,
|
|
108
|
+
onChange: (e) => P(e.target.value),
|
|
89
109
|
placeholder: "e.g. Weekend Watch",
|
|
90
|
-
className:
|
|
110
|
+
className: S,
|
|
91
111
|
required: !0
|
|
92
112
|
})] }),
|
|
93
|
-
/* @__PURE__ */
|
|
113
|
+
/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
|
|
94
114
|
htmlFor: "list-desc",
|
|
95
|
-
className:
|
|
115
|
+
className: C,
|
|
96
116
|
children: "Description"
|
|
97
|
-
}), /* @__PURE__ */
|
|
117
|
+
}), /* @__PURE__ */ _("textarea", {
|
|
98
118
|
id: "list-desc",
|
|
99
|
-
value:
|
|
100
|
-
onChange: (e) =>
|
|
119
|
+
value: F,
|
|
120
|
+
onChange: (e) => I(e.target.value),
|
|
101
121
|
rows: 2,
|
|
102
|
-
className:
|
|
122
|
+
className: S
|
|
103
123
|
})] }),
|
|
104
|
-
/* @__PURE__ */
|
|
124
|
+
/* @__PURE__ */ v("div", {
|
|
105
125
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
106
|
-
children: [/* @__PURE__ */
|
|
126
|
+
children: [/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
|
|
107
127
|
htmlFor: "list-kind",
|
|
108
|
-
className:
|
|
128
|
+
className: C,
|
|
109
129
|
children: "Kind"
|
|
110
|
-
}), /* @__PURE__ */
|
|
130
|
+
}), /* @__PURE__ */ _("select", {
|
|
111
131
|
id: "list-kind",
|
|
112
|
-
value:
|
|
113
|
-
onChange: (e) =>
|
|
114
|
-
className:
|
|
115
|
-
children:
|
|
132
|
+
value: L,
|
|
133
|
+
onChange: (e) => R(e.target.value),
|
|
134
|
+
className: S,
|
|
135
|
+
children: b.map((e) => /* @__PURE__ */ _("option", {
|
|
116
136
|
value: e,
|
|
117
137
|
children: e.charAt(0) + e.slice(1).toLowerCase()
|
|
118
138
|
}, e))
|
|
119
|
-
})] }), /* @__PURE__ */
|
|
139
|
+
})] }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
|
|
120
140
|
htmlFor: "list-visibility",
|
|
121
|
-
className:
|
|
141
|
+
className: C,
|
|
122
142
|
children: "Visibility"
|
|
123
|
-
}), /* @__PURE__ */
|
|
143
|
+
}), /* @__PURE__ */ _("select", {
|
|
124
144
|
id: "list-visibility",
|
|
125
|
-
value:
|
|
126
|
-
onChange: (e) =>
|
|
127
|
-
className:
|
|
128
|
-
children:
|
|
145
|
+
value: z,
|
|
146
|
+
onChange: (e) => B(e.target.value),
|
|
147
|
+
className: S,
|
|
148
|
+
children: x.map((e) => /* @__PURE__ */ _("option", {
|
|
129
149
|
value: e,
|
|
130
150
|
children: e.charAt(0) + e.slice(1).toLowerCase()
|
|
131
151
|
}, e))
|
|
132
152
|
})] })]
|
|
133
153
|
}),
|
|
134
|
-
/* @__PURE__ */
|
|
154
|
+
/* @__PURE__ */ _("button", {
|
|
135
155
|
type: "submit",
|
|
136
|
-
disabled:
|
|
156
|
+
disabled: A.isPending,
|
|
137
157
|
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",
|
|
138
|
-
children:
|
|
158
|
+
children: A.isPending ? "Creating…" : "Create List"
|
|
139
159
|
})
|
|
140
160
|
]
|
|
141
161
|
})]
|
|
142
162
|
}),
|
|
143
|
-
/* @__PURE__ */
|
|
163
|
+
/* @__PURE__ */ _("div", {
|
|
144
164
|
className: "mb-4 flex flex-wrap items-center gap-3",
|
|
145
|
-
children: /* @__PURE__ */
|
|
146
|
-
value:
|
|
147
|
-
onChange: (e) =>
|
|
165
|
+
children: /* @__PURE__ */ v("select", {
|
|
166
|
+
value: u,
|
|
167
|
+
onChange: (e) => d(e.target.value),
|
|
148
168
|
className: "rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
|
|
149
|
-
children: [/* @__PURE__ */
|
|
169
|
+
children: [/* @__PURE__ */ _("option", {
|
|
150
170
|
value: "",
|
|
151
171
|
children: "All Lists"
|
|
152
|
-
}),
|
|
172
|
+
}), b.map((e) => /* @__PURE__ */ _("option", {
|
|
153
173
|
value: e,
|
|
154
174
|
children: e.charAt(0) + e.slice(1).toLowerCase()
|
|
155
175
|
}, e))]
|
|
156
176
|
})
|
|
157
177
|
}),
|
|
158
|
-
|
|
178
|
+
U && /* @__PURE__ */ _("div", {
|
|
159
179
|
className: "mb-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text",
|
|
160
|
-
children:
|
|
180
|
+
children: U
|
|
161
181
|
}),
|
|
162
|
-
|
|
163
|
-
icon: /* @__PURE__ */
|
|
182
|
+
D ? /* @__PURE__ */ _(s, { label: "Loading your lists…" }) : O ? /* @__PURE__ */ _(o, { onRetry: () => void k() }) : $.length === 0 ? /* @__PURE__ */ _(c, {
|
|
183
|
+
icon: /* @__PURE__ */ _(ee, { className: "h-12 w-12" }),
|
|
164
184
|
title: "No lists yet",
|
|
165
185
|
description: "Create a watchlist or playlist to organize what you watch."
|
|
166
|
-
}) : /* @__PURE__ */
|
|
186
|
+
}) : /* @__PURE__ */ _("div", {
|
|
167
187
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
168
|
-
children:
|
|
169
|
-
let t =
|
|
170
|
-
return /* @__PURE__ */ _(
|
|
188
|
+
children: $.map((e) => {
|
|
189
|
+
let t = w[e.visibility];
|
|
190
|
+
return /* @__PURE__ */ _(y, {
|
|
171
191
|
treatment: "metric",
|
|
172
192
|
className: "flex flex-col border-border-seam p-4",
|
|
173
|
-
children:
|
|
174
|
-
|
|
193
|
+
children: G === e.id ? /* @__PURE__ */ v("form", {
|
|
194
|
+
onSubmit: (t) => void ie(t, e.id),
|
|
195
|
+
className: "space-y-3",
|
|
196
|
+
children: [
|
|
197
|
+
Z && /* @__PURE__ */ _("div", {
|
|
198
|
+
className: "rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-xs text-status-error-text",
|
|
199
|
+
children: Z
|
|
200
|
+
}),
|
|
201
|
+
/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
|
|
202
|
+
htmlFor: `list-edit-name-${e.id}`,
|
|
203
|
+
className: C,
|
|
204
|
+
children: "Name *"
|
|
205
|
+
}), /* @__PURE__ */ _("input", {
|
|
206
|
+
id: `list-edit-name-${e.id}`,
|
|
207
|
+
type: "text",
|
|
208
|
+
value: q,
|
|
209
|
+
onChange: (e) => J(e.target.value),
|
|
210
|
+
className: S,
|
|
211
|
+
autoFocus: !0,
|
|
212
|
+
required: !0
|
|
213
|
+
})] }),
|
|
214
|
+
/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
|
|
215
|
+
htmlFor: `list-edit-visibility-${e.id}`,
|
|
216
|
+
className: C,
|
|
217
|
+
children: "Visibility"
|
|
218
|
+
}), /* @__PURE__ */ _("select", {
|
|
219
|
+
id: `list-edit-visibility-${e.id}`,
|
|
220
|
+
value: Y,
|
|
221
|
+
onChange: (e) => X(e.target.value),
|
|
222
|
+
className: S,
|
|
223
|
+
children: x.map((e) => /* @__PURE__ */ _("option", {
|
|
224
|
+
value: e,
|
|
225
|
+
children: e.charAt(0) + e.slice(1).toLowerCase()
|
|
226
|
+
}, e))
|
|
227
|
+
})] }),
|
|
228
|
+
/* @__PURE__ */ v("div", {
|
|
229
|
+
className: "flex items-center gap-2",
|
|
230
|
+
children: [/* @__PURE__ */ _("button", {
|
|
231
|
+
type: "submit",
|
|
232
|
+
disabled: M.isPending,
|
|
233
|
+
className: "rounded-lg bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50 transition-opacity",
|
|
234
|
+
children: M.isPending ? "Saving…" : "Save"
|
|
235
|
+
}), /* @__PURE__ */ _("button", {
|
|
236
|
+
type: "button",
|
|
237
|
+
onClick: re,
|
|
238
|
+
disabled: M.isPending,
|
|
239
|
+
className: "rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-secondary hover:text-text-primary disabled:opacity-50 transition-colors",
|
|
240
|
+
children: "Cancel"
|
|
241
|
+
})]
|
|
242
|
+
})
|
|
243
|
+
]
|
|
244
|
+
}) : /* @__PURE__ */ v(g, { children: [
|
|
245
|
+
/* @__PURE__ */ v("div", {
|
|
175
246
|
className: "flex items-start justify-between gap-2",
|
|
176
|
-
children: [/* @__PURE__ */
|
|
247
|
+
children: [/* @__PURE__ */ _("h3", {
|
|
177
248
|
className: "line-clamp-1 text-sm font-semibold text-text-primary",
|
|
178
249
|
children: e.name
|
|
179
|
-
}), /* @__PURE__ */
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
250
|
+
}), /* @__PURE__ */ v("div", {
|
|
251
|
+
className: "flex items-center gap-0.5",
|
|
252
|
+
children: [/* @__PURE__ */ _("button", {
|
|
253
|
+
type: "button",
|
|
254
|
+
onClick: () => ne(e),
|
|
255
|
+
className: "rounded-md p-1 text-text-secondary hover:text-text-primary transition-colors",
|
|
256
|
+
"aria-label": `Edit list ${e.name}`,
|
|
257
|
+
children: /* @__PURE__ */ _(p, { className: "h-4 w-4" })
|
|
258
|
+
}), /* @__PURE__ */ _("button", {
|
|
259
|
+
type: "button",
|
|
260
|
+
disabled: j.isPending,
|
|
261
|
+
onClick: () => void ae(e.id),
|
|
262
|
+
className: "rounded-md p-1 text-text-secondary hover:text-status-error-text disabled:opacity-50 transition-colors",
|
|
263
|
+
"aria-label": `Delete list ${e.name}`,
|
|
264
|
+
children: /* @__PURE__ */ _(h, { className: "h-4 w-4" })
|
|
265
|
+
})]
|
|
186
266
|
})]
|
|
187
267
|
}),
|
|
188
|
-
e.description && /* @__PURE__ */
|
|
268
|
+
e.description && /* @__PURE__ */ _("p", {
|
|
189
269
|
className: "mt-1 line-clamp-2 text-xs text-text-secondary",
|
|
190
270
|
children: e.description
|
|
191
271
|
}),
|
|
192
|
-
/* @__PURE__ */
|
|
272
|
+
/* @__PURE__ */ v("div", {
|
|
193
273
|
className: "mt-3 flex items-center gap-3 text-xs text-text-secondary",
|
|
194
274
|
children: [
|
|
195
|
-
/* @__PURE__ */
|
|
275
|
+
/* @__PURE__ */ _("span", {
|
|
196
276
|
className: "rounded-full bg-bg-sunken px-2 py-0.5",
|
|
197
277
|
children: e.kind.charAt(0) + e.kind.slice(1).toLowerCase()
|
|
198
278
|
}),
|
|
199
|
-
/* @__PURE__ */
|
|
279
|
+
/* @__PURE__ */ v("span", {
|
|
200
280
|
className: "inline-flex items-center gap-1",
|
|
201
|
-
children: [/* @__PURE__ */
|
|
281
|
+
children: [/* @__PURE__ */ _(t, { className: "h-3 w-3" }), e.visibility.charAt(0) + e.visibility.slice(1).toLowerCase()]
|
|
202
282
|
}),
|
|
203
|
-
/* @__PURE__ */
|
|
283
|
+
/* @__PURE__ */ v("span", { children: [e.itemCount, " items"] })
|
|
204
284
|
]
|
|
205
285
|
}),
|
|
206
|
-
/* @__PURE__ */
|
|
286
|
+
/* @__PURE__ */ v("p", {
|
|
207
287
|
className: "mt-2 text-[11px] text-text-muted",
|
|
208
|
-
children: ["Updated ",
|
|
288
|
+
children: ["Updated ", a(e.updatedAt)]
|
|
209
289
|
})
|
|
210
|
-
]
|
|
290
|
+
] })
|
|
211
291
|
}, e.id);
|
|
212
292
|
})
|
|
213
293
|
})
|
|
@@ -215,6 +295,6 @@ var y = ["WATCHLIST", "PLAYLIST"], b = [
|
|
|
215
295
|
});
|
|
216
296
|
};
|
|
217
297
|
//#endregion
|
|
218
|
-
export {
|
|
298
|
+
export { T as ListsPage };
|
|
219
299
|
|
|
220
300
|
//# sourceMappingURL=ListsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ListsPage.js","names":[],"sources":["../../../../src/hookmovies/pages/library/ListsPage.tsx"],"sourcesContent":["/**\n * My Lists (Library — self-scoped)\n * @module hookmovies/pages/library\n *\n * The caller's own watchlists and playlists via `hokMyLists`, created with\n * `hokCreateList` and removed with `hokDeleteList`. All self-scoped to the\n * calling user by the subgraph.\n */\n\nimport { type FC, type FormEvent, useState } from 'react';\nimport { ListVideo, Plus, X, Trash2, Lock, Globe, Link as LinkIcon } 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 {\n useMyLists,\n useCreateList,\n useDeleteList,\n type HokListKind,\n type HokListVisibility,\n type CreateListInput,\n} from '../../hooks/useHookMoviesApi';\nimport { formatRelativeTime } from '../../utils/formatters';\n\nconst KIND_OPTIONS: HokListKind[] = ['WATCHLIST', 'PLAYLIST'];\nconst VISIBILITY_OPTIONS: HokListVisibility[] = ['PRIVATE', 'UNLISTED', 'PUBLIC'];\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\nconst visibilityIcon: Record<HokListVisibility, typeof Lock> = {\n PRIVATE: Lock,\n UNLISTED: LinkIcon,\n PUBLIC: Globe,\n};\n\nexport const ListsPage: FC = () => {\n const [kindFilter, setKindFilter] = useState<HokListKind | ''>('');\n const [showCreate, setShowCreate] = useState(false);\n\n const { data, isLoading, isError, refetch } = useMyLists(kindFilter || undefined, {\n limit: 100,\n offset: 0,\n });\n const createList = useCreateList();\n const deleteList = useDeleteList();\n\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [kind, setKind] = useState<HokListKind>('WATCHLIST');\n const [visibility, setVisibility] = useState<HokListVisibility>('PRIVATE');\n const [error, setError] = useState<string | null>(null);\n const [actionError, setActionError] = useState<string | null>(null);\n\n const handleDelete = async (id: string) => {\n setActionError(null);\n try {\n await deleteList.mutateAsync(id);\n } catch (err) {\n setActionError(\n err instanceof Error ? err.message : 'Failed to delete list (you can only delete your own)'\n );\n }\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: CreateListInput = {\n name: name.trim(),\n description: description.trim() || undefined,\n kind,\n visibility,\n };\n try {\n await createList.mutateAsync(input);\n setName('');\n setDescription('');\n setKind('WATCHLIST');\n setVisibility('PRIVATE');\n setShowCreate(false);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create list');\n }\n };\n\n const items = data?.items ?? [];\n\n return (\n <PageLayout\n title=\"My Library\"\n description=\"Watchlists & playlists\"\n purpose=\"Your personal collections — a watchlist of what to watch next and curated playlists you can keep private, unlisted, or public.\"\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 List\n </button>\n }\n >\n {showCreate && (\n <GlassCard treatment=\"glass\" className=\"mb-6 border-border-seam p-5\">\n <div className=\"mb-3 flex items-center justify-between\">\n <h2 className=\"text-sm font-semibold text-text-primary\">Create List</h2>\n <button\n type=\"button\"\n onClick={() => setShowCreate(false)}\n className=\"rounded-md p-1 text-text-secondary hover:text-text-primary\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {error && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {error}\n </div>\n )}\n <div>\n <label htmlFor=\"list-name\" className={labelClass}>\n Name *\n </label>\n <input\n id=\"list-name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. Weekend Watch\"\n className={fieldClass}\n required\n />\n </div>\n <div>\n <label htmlFor=\"list-desc\" className={labelClass}>\n Description\n </label>\n <textarea\n id=\"list-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n className={fieldClass}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"list-kind\" className={labelClass}>\n Kind\n </label>\n <select\n id=\"list-kind\"\n value={kind}\n onChange={(e) => setKind(e.target.value as HokListKind)}\n className={fieldClass}\n >\n {KIND_OPTIONS.map((k) => (\n <option key={k} value={k}>\n {k.charAt(0) + k.slice(1).toLowerCase()}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor=\"list-visibility\" className={labelClass}>\n Visibility\n </label>\n <select\n id=\"list-visibility\"\n value={visibility}\n onChange={(e) => setVisibility(e.target.value as HokListVisibility)}\n className={fieldClass}\n >\n {VISIBILITY_OPTIONS.map((v) => (\n <option key={v} value={v}>\n {v.charAt(0) + v.slice(1).toLowerCase()}\n </option>\n ))}\n </select>\n </div>\n </div>\n <button\n type=\"submit\"\n disabled={createList.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 {createList.isPending ? 'Creating…' : 'Create List'}\n </button>\n </form>\n </GlassCard>\n )}\n\n <div className=\"mb-4 flex flex-wrap items-center gap-3\">\n <select\n value={kindFilter}\n onChange={(e) => setKindFilter(e.target.value as HokListKind | '')}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">All Lists</option>\n {KIND_OPTIONS.map((k) => (\n <option key={k} value={k}>\n {k.charAt(0) + k.slice(1).toLowerCase()}\n </option>\n ))}\n </select>\n </div>\n\n {actionError && (\n <div className=\"mb-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text\">\n {actionError}\n </div>\n )}\n\n {isLoading ? (\n <LoadingState label=\"Loading your lists…\" />\n ) : isError ? (\n <ErrorState onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n <EmptyState\n icon={<ListVideo className=\"h-12 w-12\" />}\n title=\"No lists yet\"\n description=\"Create a watchlist or playlist to organize what you watch.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {items.map((list) => {\n const VisibilityIcon = visibilityIcon[list.visibility];\n return (\n <GlassCard\n key={list.id}\n treatment=\"metric\"\n className=\"flex flex-col border-border-seam p-4\"\n >\n <div className=\"flex items-start justify-between gap-2\">\n <h3 className=\"line-clamp-1 text-sm font-semibold text-text-primary\">\n {list.name}\n </h3>\n <button\n type=\"button\"\n disabled={deleteList.isPending}\n onClick={() => void handleDelete(list.id)}\n className=\"rounded-md p-1 text-text-secondary hover:text-status-error-text disabled:opacity-50 transition-colors\"\n aria-label=\"Delete list\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n {list.description && (\n <p className=\"mt-1 line-clamp-2 text-xs text-text-secondary\">\n {list.description}\n </p>\n )}\n <div className=\"mt-3 flex items-center gap-3 text-xs text-text-secondary\">\n <span className=\"rounded-full bg-bg-sunken px-2 py-0.5\">\n {list.kind.charAt(0) + list.kind.slice(1).toLowerCase()}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <VisibilityIcon className=\"h-3 w-3\" />\n {list.visibility.charAt(0) + list.visibility.slice(1).toLowerCase()}\n </span>\n <span>{list.itemCount} items</span>\n </div>\n <p className=\"mt-2 text-[11px] text-text-muted\">\n Updated {formatRelativeTime(list.updatedAt)}\n </p>\n </GlassCard>\n );\n })}\n </div>\n )}\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ListsPage.js","names":[],"sources":["../../../../src/hookmovies/pages/library/ListsPage.tsx"],"sourcesContent":["/**\n * My Lists (Library — self-scoped)\n * @module hookmovies/pages/library\n *\n * The caller's own watchlists and playlists via `hokMyLists`, created with\n * `hokCreateList` and removed with `hokDeleteList`. All self-scoped to the\n * calling user by the subgraph.\n */\n\nimport { type FC, type FormEvent, useState } from 'react';\nimport { ListVideo, Plus, X, Trash2, Pencil, Lock, Globe, Link as LinkIcon } 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 {\n useMyLists,\n useCreateList,\n useDeleteList,\n useUpdateList,\n type HokListKind,\n type HokListVisibility,\n type HokList,\n type CreateListInput,\n} from '../../hooks/useHookMoviesApi';\nimport { formatRelativeTime } from '../../utils/formatters';\n\nconst KIND_OPTIONS: HokListKind[] = ['WATCHLIST', 'PLAYLIST'];\nconst VISIBILITY_OPTIONS: HokListVisibility[] = ['PRIVATE', 'UNLISTED', 'PUBLIC'];\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\nconst visibilityIcon: Record<HokListVisibility, typeof Lock> = {\n PRIVATE: Lock,\n UNLISTED: LinkIcon,\n PUBLIC: Globe,\n};\n\nexport const ListsPage: FC = () => {\n const [kindFilter, setKindFilter] = useState<HokListKind | ''>('');\n const [showCreate, setShowCreate] = useState(false);\n\n const { data, isLoading, isError, refetch } = useMyLists(kindFilter || undefined, {\n limit: 100,\n offset: 0,\n });\n const createList = useCreateList();\n const deleteList = useDeleteList();\n const updateList = useUpdateList();\n\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [kind, setKind] = useState<HokListKind>('WATCHLIST');\n const [visibility, setVisibility] = useState<HokListVisibility>('PRIVATE');\n const [error, setError] = useState<string | null>(null);\n const [actionError, setActionError] = useState<string | null>(null);\n\n // Inline edit (rename / re-scope) state — one row open at a time.\n const [editingId, setEditingId] = useState<string | null>(null);\n const [editName, setEditName] = useState('');\n const [editVisibility, setEditVisibility] = useState<HokListVisibility>('PRIVATE');\n const [editError, setEditError] = useState<string | null>(null);\n\n const startEdit = (list: HokList) => {\n setEditError(null);\n setEditingId(list.id);\n setEditName(list.name);\n setEditVisibility(list.visibility);\n };\n\n const cancelEdit = () => {\n setEditingId(null);\n setEditError(null);\n };\n\n const handleUpdate = async (e: FormEvent, id: string) => {\n e.preventDefault();\n setEditError(null);\n if (!editName.trim()) {\n setEditError('Name is required.');\n return;\n }\n try {\n await updateList.mutateAsync({\n id,\n input: { name: editName.trim(), visibility: editVisibility },\n });\n setEditingId(null);\n } catch (err) {\n setEditError(\n err instanceof Error ? err.message : 'Failed to update list (you can only edit your own)'\n );\n }\n };\n\n const handleDelete = async (id: string) => {\n setActionError(null);\n try {\n await deleteList.mutateAsync(id);\n } catch (err) {\n setActionError(\n err instanceof Error ? err.message : 'Failed to delete list (you can only delete your own)'\n );\n }\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: CreateListInput = {\n name: name.trim(),\n description: description.trim() || undefined,\n kind,\n visibility,\n };\n try {\n await createList.mutateAsync(input);\n setName('');\n setDescription('');\n setKind('WATCHLIST');\n setVisibility('PRIVATE');\n setShowCreate(false);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create list');\n }\n };\n\n const items = data?.items ?? [];\n\n return (\n <PageLayout\n title=\"My Library\"\n description=\"Watchlists & playlists\"\n purpose=\"Your personal collections — a watchlist of what to watch next and curated playlists you can keep private, unlisted, or public.\"\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 List\n </button>\n }\n >\n {showCreate && (\n <GlassCard treatment=\"glass\" className=\"mb-6 border-border-seam p-5\">\n <div className=\"mb-3 flex items-center justify-between\">\n <h2 className=\"text-sm font-semibold text-text-primary\">Create List</h2>\n <button\n type=\"button\"\n onClick={() => setShowCreate(false)}\n className=\"rounded-md p-1 text-text-secondary hover:text-text-primary\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {error && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {error}\n </div>\n )}\n <div>\n <label htmlFor=\"list-name\" className={labelClass}>\n Name *\n </label>\n <input\n id=\"list-name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. Weekend Watch\"\n className={fieldClass}\n required\n />\n </div>\n <div>\n <label htmlFor=\"list-desc\" className={labelClass}>\n Description\n </label>\n <textarea\n id=\"list-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n className={fieldClass}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"list-kind\" className={labelClass}>\n Kind\n </label>\n <select\n id=\"list-kind\"\n value={kind}\n onChange={(e) => setKind(e.target.value as HokListKind)}\n className={fieldClass}\n >\n {KIND_OPTIONS.map((k) => (\n <option key={k} value={k}>\n {k.charAt(0) + k.slice(1).toLowerCase()}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor=\"list-visibility\" className={labelClass}>\n Visibility\n </label>\n <select\n id=\"list-visibility\"\n value={visibility}\n onChange={(e) => setVisibility(e.target.value as HokListVisibility)}\n className={fieldClass}\n >\n {VISIBILITY_OPTIONS.map((v) => (\n <option key={v} value={v}>\n {v.charAt(0) + v.slice(1).toLowerCase()}\n </option>\n ))}\n </select>\n </div>\n </div>\n <button\n type=\"submit\"\n disabled={createList.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 {createList.isPending ? 'Creating…' : 'Create List'}\n </button>\n </form>\n </GlassCard>\n )}\n\n <div className=\"mb-4 flex flex-wrap items-center gap-3\">\n <select\n value={kindFilter}\n onChange={(e) => setKindFilter(e.target.value as HokListKind | '')}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">All Lists</option>\n {KIND_OPTIONS.map((k) => (\n <option key={k} value={k}>\n {k.charAt(0) + k.slice(1).toLowerCase()}\n </option>\n ))}\n </select>\n </div>\n\n {actionError && (\n <div className=\"mb-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text\">\n {actionError}\n </div>\n )}\n\n {isLoading ? (\n <LoadingState label=\"Loading your lists…\" />\n ) : isError ? (\n <ErrorState onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n <EmptyState\n icon={<ListVideo className=\"h-12 w-12\" />}\n title=\"No lists yet\"\n description=\"Create a watchlist or playlist to organize what you watch.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {items.map((list) => {\n const VisibilityIcon = visibilityIcon[list.visibility];\n const isEditing = editingId === list.id;\n return (\n <GlassCard\n key={list.id}\n treatment=\"metric\"\n className=\"flex flex-col border-border-seam p-4\"\n >\n {isEditing ? (\n <form onSubmit={(e) => void handleUpdate(e, list.id)} className=\"space-y-3\">\n {editError && (\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 {editError}\n </div>\n )}\n <div>\n <label htmlFor={`list-edit-name-${list.id}`} className={labelClass}>\n Name *\n </label>\n <input\n id={`list-edit-name-${list.id}`}\n type=\"text\"\n value={editName}\n onChange={(e) => setEditName(e.target.value)}\n className={fieldClass}\n autoFocus\n required\n />\n </div>\n <div>\n <label htmlFor={`list-edit-visibility-${list.id}`} className={labelClass}>\n Visibility\n </label>\n <select\n id={`list-edit-visibility-${list.id}`}\n value={editVisibility}\n onChange={(e) => setEditVisibility(e.target.value as HokListVisibility)}\n className={fieldClass}\n >\n {VISIBILITY_OPTIONS.map((v) => (\n <option key={v} value={v}>\n {v.charAt(0) + v.slice(1).toLowerCase()}\n </option>\n ))}\n </select>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"submit\"\n disabled={updateList.isPending}\n className=\"rounded-lg bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50 transition-opacity\"\n >\n {updateList.isPending ? 'Saving…' : 'Save'}\n </button>\n <button\n type=\"button\"\n onClick={cancelEdit}\n disabled={updateList.isPending}\n className=\"rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-secondary hover:text-text-primary disabled:opacity-50 transition-colors\"\n >\n Cancel\n </button>\n </div>\n </form>\n ) : (\n <>\n <div className=\"flex items-start justify-between gap-2\">\n <h3 className=\"line-clamp-1 text-sm font-semibold text-text-primary\">\n {list.name}\n </h3>\n <div className=\"flex items-center gap-0.5\">\n <button\n type=\"button\"\n onClick={() => startEdit(list)}\n className=\"rounded-md p-1 text-text-secondary hover:text-text-primary transition-colors\"\n aria-label={`Edit list ${list.name}`}\n >\n <Pencil className=\"h-4 w-4\" />\n </button>\n <button\n type=\"button\"\n disabled={deleteList.isPending}\n onClick={() => void handleDelete(list.id)}\n className=\"rounded-md p-1 text-text-secondary hover:text-status-error-text disabled:opacity-50 transition-colors\"\n aria-label={`Delete list ${list.name}`}\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n {list.description && (\n <p className=\"mt-1 line-clamp-2 text-xs text-text-secondary\">\n {list.description}\n </p>\n )}\n <div className=\"mt-3 flex items-center gap-3 text-xs text-text-secondary\">\n <span className=\"rounded-full bg-bg-sunken px-2 py-0.5\">\n {list.kind.charAt(0) + list.kind.slice(1).toLowerCase()}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <VisibilityIcon className=\"h-3 w-3\" />\n {list.visibility.charAt(0) + list.visibility.slice(1).toLowerCase()}\n </span>\n <span>{list.itemCount} items</span>\n </div>\n <p className=\"mt-2 text-[11px] text-text-muted\">\n Updated {formatRelativeTime(list.updatedAt)}\n </p>\n </>\n )}\n </GlassCard>\n );\n })}\n </div>\n )}\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@burdenoff/microfe-hookmovies",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.706.1",
|
|
4
4
|
"description": "Burdenoff HookMovies Microfrontend - Mobile services marketplace platform for connecting service providers with customers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|