@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.
@@ -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 i } from "../../utils/formatters.js";
4
- import { ErrorState as a, LoadingState as o } from "../../components/StateViews.js";
5
- import { EmptyState as s } from "../../components/EmptyState.js";
6
- import { useState as c } from "react";
7
- import { Globe as l, Link as u, ListVideo as d, Lock as f, Plus as p, Trash2 as m, X as h } from "lucide-react";
8
- import { jsx as g, jsxs as _ } from "react/jsx-runtime";
9
- import { GlassCard as v } from "@burdenoff/fe-libs/ui";
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 y = ["WATCHLIST", "PLAYLIST"], b = [
11
+ var b = ["WATCHLIST", "PLAYLIST"], x = [
12
12
  "PRIVATE",
13
13
  "UNLISTED",
14
14
  "PUBLIC"
15
- ], 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 = {
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: u,
18
- PUBLIC: l
19
- }, w = () => {
20
- let [l, u] = c(""), [f, w] = c(!1), { data: T, isLoading: E, isError: D, refetch: O } = r(l || void 0, {
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
- }), k = t(), A = n(), [j, M] = c(""), [N, P] = c(""), [F, I] = c("WATCHLIST"), [L, R] = c("PRIVATE"), [z, B] = c(null), [V, H] = c(null), U = async (e) => {
24
- H(null);
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 A.mutateAsync(e);
46
+ await j.mutateAsync(e);
27
47
  } catch (e) {
28
- H(e instanceof Error ? e.message : "Failed to delete list (you can only delete your own)");
48
+ W(e instanceof Error ? e.message : "Failed to delete list (you can only delete your own)");
29
49
  }
30
- }, W = async (e) => {
31
- if (e.preventDefault(), B(null), !j.trim()) {
32
- B("Name is required.");
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: j.trim(),
37
- description: N.trim() || void 0,
38
- kind: F,
39
- visibility: L
56
+ name: N.trim(),
57
+ description: F.trim() || void 0,
58
+ kind: L,
59
+ visibility: z
40
60
  };
41
61
  try {
42
- await k.mutateAsync(t), M(""), P(""), I("WATCHLIST"), R("PRIVATE"), w(!1);
62
+ await A.mutateAsync(t), P(""), I(""), R("WATCHLIST"), B("PRIVATE"), T(!1);
43
63
  } catch (e) {
44
- B(e instanceof Error ? e.message : "Failed to create list");
64
+ H(e instanceof Error ? e.message : "Failed to create list");
45
65
  }
46
- }, G = T?.items ?? [];
47
- return /* @__PURE__ */ _(e, {
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__ */ _("button", {
71
+ actions: /* @__PURE__ */ v("button", {
52
72
  type: "button",
53
- onClick: () => w((e) => !e),
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__ */ g(p, { className: "h-4 w-4" }), "New List"]
75
+ children: [/* @__PURE__ */ _(m, { className: "h-4 w-4" }), "New List"]
56
76
  }),
57
77
  children: [
58
- f && /* @__PURE__ */ _(v, {
78
+ f && /* @__PURE__ */ v(y, {
59
79
  treatment: "glass",
60
80
  className: "mb-6 border-border-seam p-5",
61
- children: [/* @__PURE__ */ _("div", {
81
+ children: [/* @__PURE__ */ v("div", {
62
82
  className: "mb-3 flex items-center justify-between",
63
- children: [/* @__PURE__ */ g("h2", {
83
+ children: [/* @__PURE__ */ _("h2", {
64
84
  className: "text-sm font-semibold text-text-primary",
65
85
  children: "Create List"
66
- }), /* @__PURE__ */ g("button", {
86
+ }), /* @__PURE__ */ _("button", {
67
87
  type: "button",
68
- onClick: () => w(!1),
88
+ onClick: () => T(!1),
69
89
  className: "rounded-md p-1 text-text-secondary hover:text-text-primary",
70
- children: /* @__PURE__ */ g(h, { className: "h-4 w-4" })
90
+ children: /* @__PURE__ */ _(te, { className: "h-4 w-4" })
71
91
  })]
72
- }), /* @__PURE__ */ _("form", {
73
- onSubmit: W,
92
+ }), /* @__PURE__ */ v("form", {
93
+ onSubmit: oe,
74
94
  className: "space-y-4",
75
95
  children: [
76
- z && /* @__PURE__ */ g("div", {
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: z
98
+ children: V
79
99
  }),
80
- /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
100
+ /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
81
101
  htmlFor: "list-name",
82
- className: S,
102
+ className: C,
83
103
  children: "Name *"
84
- }), /* @__PURE__ */ g("input", {
104
+ }), /* @__PURE__ */ _("input", {
85
105
  id: "list-name",
86
106
  type: "text",
87
- value: j,
88
- onChange: (e) => M(e.target.value),
107
+ value: N,
108
+ onChange: (e) => P(e.target.value),
89
109
  placeholder: "e.g. Weekend Watch",
90
- className: x,
110
+ className: S,
91
111
  required: !0
92
112
  })] }),
93
- /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
113
+ /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
94
114
  htmlFor: "list-desc",
95
- className: S,
115
+ className: C,
96
116
  children: "Description"
97
- }), /* @__PURE__ */ g("textarea", {
117
+ }), /* @__PURE__ */ _("textarea", {
98
118
  id: "list-desc",
99
- value: N,
100
- onChange: (e) => P(e.target.value),
119
+ value: F,
120
+ onChange: (e) => I(e.target.value),
101
121
  rows: 2,
102
- className: x
122
+ className: S
103
123
  })] }),
104
- /* @__PURE__ */ _("div", {
124
+ /* @__PURE__ */ v("div", {
105
125
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
106
- children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
126
+ children: [/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
107
127
  htmlFor: "list-kind",
108
- className: S,
128
+ className: C,
109
129
  children: "Kind"
110
- }), /* @__PURE__ */ g("select", {
130
+ }), /* @__PURE__ */ _("select", {
111
131
  id: "list-kind",
112
- value: F,
113
- onChange: (e) => I(e.target.value),
114
- className: x,
115
- children: y.map((e) => /* @__PURE__ */ g("option", {
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__ */ _("div", { children: [/* @__PURE__ */ g("label", {
139
+ })] }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
120
140
  htmlFor: "list-visibility",
121
- className: S,
141
+ className: C,
122
142
  children: "Visibility"
123
- }), /* @__PURE__ */ g("select", {
143
+ }), /* @__PURE__ */ _("select", {
124
144
  id: "list-visibility",
125
- value: L,
126
- onChange: (e) => R(e.target.value),
127
- className: x,
128
- children: b.map((e) => /* @__PURE__ */ g("option", {
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__ */ g("button", {
154
+ /* @__PURE__ */ _("button", {
135
155
  type: "submit",
136
- disabled: k.isPending,
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: k.isPending ? "Creating…" : "Create List"
158
+ children: A.isPending ? "Creating…" : "Create List"
139
159
  })
140
160
  ]
141
161
  })]
142
162
  }),
143
- /* @__PURE__ */ g("div", {
163
+ /* @__PURE__ */ _("div", {
144
164
  className: "mb-4 flex flex-wrap items-center gap-3",
145
- children: /* @__PURE__ */ _("select", {
146
- value: l,
147
- onChange: (e) => u(e.target.value),
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__ */ g("option", {
169
+ children: [/* @__PURE__ */ _("option", {
150
170
  value: "",
151
171
  children: "All Lists"
152
- }), y.map((e) => /* @__PURE__ */ g("option", {
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
- V && /* @__PURE__ */ g("div", {
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: V
180
+ children: U
161
181
  }),
162
- E ? /* @__PURE__ */ g(o, { label: "Loading your lists…" }) : D ? /* @__PURE__ */ g(a, { onRetry: () => void O() }) : G.length === 0 ? /* @__PURE__ */ g(s, {
163
- icon: /* @__PURE__ */ g(d, { className: "h-12 w-12" }),
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__ */ g("div", {
186
+ }) : /* @__PURE__ */ _("div", {
167
187
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
168
- children: G.map((e) => {
169
- let t = C[e.visibility];
170
- return /* @__PURE__ */ _(v, {
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
- /* @__PURE__ */ _("div", {
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__ */ g("h3", {
247
+ children: [/* @__PURE__ */ _("h3", {
177
248
  className: "line-clamp-1 text-sm font-semibold text-text-primary",
178
249
  children: e.name
179
- }), /* @__PURE__ */ g("button", {
180
- type: "button",
181
- disabled: A.isPending,
182
- onClick: () => void U(e.id),
183
- className: "rounded-md p-1 text-text-secondary hover:text-status-error-text disabled:opacity-50 transition-colors",
184
- "aria-label": "Delete list",
185
- children: /* @__PURE__ */ g(m, { className: "h-4 w-4" })
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__ */ g("p", {
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__ */ _("div", {
272
+ /* @__PURE__ */ v("div", {
193
273
  className: "mt-3 flex items-center gap-3 text-xs text-text-secondary",
194
274
  children: [
195
- /* @__PURE__ */ g("span", {
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__ */ _("span", {
279
+ /* @__PURE__ */ v("span", {
200
280
  className: "inline-flex items-center gap-1",
201
- children: [/* @__PURE__ */ g(t, { className: "h-3 w-3" }), e.visibility.charAt(0) + e.visibility.slice(1).toLowerCase()]
281
+ children: [/* @__PURE__ */ _(t, { className: "h-3 w-3" }), e.visibility.charAt(0) + e.visibility.slice(1).toLowerCase()]
202
282
  }),
203
- /* @__PURE__ */ _("span", { children: [e.itemCount, " items"] })
283
+ /* @__PURE__ */ v("span", { children: [e.itemCount, " items"] })
204
284
  ]
205
285
  }),
206
- /* @__PURE__ */ _("p", {
286
+ /* @__PURE__ */ v("p", {
207
287
  className: "mt-2 text-[11px] text-text-muted",
208
- children: ["Updated ", i(e.updatedAt)]
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 { w as ListsPage };
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.705.2",
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": [