@ampless/admin 0.2.0-alpha.6 → 0.2.0-alpha.7

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,3 +1,4 @@
1
+ 'use client';
1
2
  import {
2
3
  AdminDashboard,
3
4
  AdminProviders,
@@ -10,9 +11,10 @@ import {
10
11
  Sidebar,
11
12
  SiteSelector,
12
13
  SiteSettingsForm,
13
- ThemeSettingsForm
14
- } from "../chunk-7WFZULH7.js";
15
- import "../chunk-FI7CM4LH.js";
14
+ ThemeSettingsForm,
15
+ useT
16
+ } from "../chunk-GXPSAOES.js";
17
+ import "../chunk-L5NHN3MY.js";
16
18
  import "../chunk-VXEVLHGL.js";
17
19
 
18
20
  // src/pages/admin-layout.tsx
@@ -35,9 +37,16 @@ function createAdminLayout(admin) {
35
37
  const sites = admin.adminSiteOptions();
36
38
  const currentSiteId = await admin.currentAdminSiteId();
37
39
  const selector = sites.length > 0 ? /* @__PURE__ */ jsx(SiteSelector, { current: currentSiteId, sites }) : null;
38
- return /* @__PURE__ */ jsx(AdminProviders, { outputs: admin.outputs, cmsConfig: sanitizeCmsConfigForClient(admin.cmsConfig), children: /* @__PURE__ */ jsx(I18nProvider, { locale: admin.locale, dict: admin.dict, children: /* @__PURE__ */ jsxs("div", { className: "flex min-h-screen", children: [
39
- /* @__PURE__ */ jsx(Sidebar, { email: session.email, siteSelector: selector }),
40
- /* @__PURE__ */ jsx("main", { className: "flex-1 overflow-auto", children })
40
+ return /* @__PURE__ */ jsx(AdminProviders, { outputs: admin.outputs, cmsConfig: sanitizeCmsConfigForClient(admin.cmsConfig), children: /* @__PURE__ */ jsx(I18nProvider, { locale: admin.locale, dict: admin.dict, children: /* @__PURE__ */ jsxs("div", { className: "flex min-h-screen flex-col md:flex-row", children: [
41
+ /* @__PURE__ */ jsx(
42
+ Sidebar,
43
+ {
44
+ email: session.email,
45
+ siteSelector: selector,
46
+ isAdmin: admin.isAdmin(session)
47
+ }
48
+ ),
49
+ /* @__PURE__ */ jsx("main", { className: "min-w-0 flex-1", children })
41
50
  ] }) }) });
42
51
  }
43
52
  return AdminLayout;
@@ -86,12 +95,12 @@ function createSitesListPage(admin) {
86
95
  async function SitesPage() {
87
96
  const multi = isMultiSite(cmsConfig);
88
97
  const ids = multi ? Object.keys(cmsConfig.sites ?? {}) : [DEFAULT_SITE_ID];
89
- return /* @__PURE__ */ jsxs2("div", { className: "p-8", children: [
90
- /* @__PURE__ */ jsx2("div", { className: "mb-8 flex items-center justify-between", children: /* @__PURE__ */ jsxs2("div", { children: [
91
- /* @__PURE__ */ jsx2("h1", { className: "text-3xl font-bold", children: t("sites.list.title") }),
98
+ return /* @__PURE__ */ jsxs2("div", { className: "mx-auto max-w-7xl p-4 md:p-8", children: [
99
+ /* @__PURE__ */ jsx2("div", { className: "mb-6 flex flex-wrap items-center justify-between gap-3 md:mb-8", children: /* @__PURE__ */ jsxs2("div", { children: [
100
+ /* @__PURE__ */ jsx2("h1", { className: "text-2xl font-bold md:text-3xl", children: t("sites.list.title") }),
92
101
  /* @__PURE__ */ jsx2("p", { className: "mt-1 text-sm text-muted-foreground", children: t("sites.list.description") })
93
102
  ] }) }),
94
- /* @__PURE__ */ jsx2("div", { className: "rounded-md border", children: /* @__PURE__ */ jsxs2(Table, { children: [
103
+ /* @__PURE__ */ jsx2("div", { className: "overflow-x-auto rounded-md border", children: /* @__PURE__ */ jsxs2(Table, { children: [
95
104
  /* @__PURE__ */ jsx2(TableHeader, { children: /* @__PURE__ */ jsxs2(TableRow, { children: [
96
105
  /* @__PURE__ */ jsx2(TableHead, { children: t("sites.list.columnSiteId") }),
97
106
  /* @__PURE__ */ jsx2(TableHead, { children: t("sites.list.columnName") }),
@@ -144,8 +153,8 @@ function createSiteEditPage(admin) {
144
153
  dateFormat: settings.dateFormat,
145
154
  timezone: settings.timezone
146
155
  };
147
- return /* @__PURE__ */ jsxs3("div", { className: "p-8", children: [
148
- /* @__PURE__ */ jsxs3("div", { className: "mb-8", children: [
156
+ return /* @__PURE__ */ jsxs3("div", { className: "mx-auto max-w-7xl p-4 md:p-8", children: [
157
+ /* @__PURE__ */ jsxs3("div", { className: "mb-6 md:mb-8", children: [
149
158
  /* @__PURE__ */ jsxs3(
150
159
  Link2,
151
160
  {
@@ -157,7 +166,7 @@ function createSiteEditPage(admin) {
157
166
  ]
158
167
  }
159
168
  ),
160
- /* @__PURE__ */ jsx3("h1", { className: "mt-2 text-3xl font-bold", children: settings.site.name }),
169
+ /* @__PURE__ */ jsx3("h1", { className: "mt-2 text-2xl font-bold md:text-3xl", children: settings.site.name }),
161
170
  /* @__PURE__ */ jsxs3("p", { className: "text-sm text-muted-foreground", children: [
162
171
  t("common.siteId"),
163
172
  ": ",
@@ -193,8 +202,8 @@ function createSiteThemePage(admin, themeList) {
193
202
  label: m.manifest.label,
194
203
  description: m.manifest.description
195
204
  }));
196
- return /* @__PURE__ */ jsxs4("div", { className: "p-8", children: [
197
- /* @__PURE__ */ jsxs4("div", { className: "mb-8", children: [
205
+ return /* @__PURE__ */ jsxs4("div", { className: "mx-auto max-w-7xl p-4 md:p-8", children: [
206
+ /* @__PURE__ */ jsxs4("div", { className: "mb-6 md:mb-8", children: [
198
207
  /* @__PURE__ */ jsxs4(
199
208
  Link3,
200
209
  {
@@ -206,7 +215,7 @@ function createSiteThemePage(admin, themeList) {
206
215
  ]
207
216
  }
208
217
  ),
209
- /* @__PURE__ */ jsx4("h1", { className: "mt-2 text-3xl font-bold", children: t("theme.title") }),
218
+ /* @__PURE__ */ jsx4("h1", { className: "mt-2 text-2xl font-bold md:text-3xl", children: t("theme.title") }),
210
219
  /* @__PURE__ */ jsxs4("p", { className: "text-sm text-muted-foreground", children: [
211
220
  t("common.active"),
212
221
  ":",
@@ -232,6 +241,163 @@ function createSiteThemePage(admin, themeList) {
232
241
  return ThemePage;
233
242
  }
234
243
 
244
+ // src/pages/users-list.tsx
245
+ import { redirect as redirect2 } from "next/navigation";
246
+
247
+ // src/components/users-list-view.tsx
248
+ import { useEffect, useState } from "react";
249
+ import { generateClient } from "aws-amplify/api";
250
+ import {
251
+ Button as Button2,
252
+ Table as Table2,
253
+ TableBody as TableBody2,
254
+ TableCell as TableCell2,
255
+ TableHead as TableHead2,
256
+ TableHeader as TableHeader2,
257
+ TableRow as TableRow2
258
+ } from "@ampless/runtime/ui";
259
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
260
+ function isAdminRole(value) {
261
+ return value === "admin" || value === "editor" || value === "none";
262
+ }
263
+ function UsersListView({ currentUserId }) {
264
+ const t = useT();
265
+ const [users, setUsers] = useState(null);
266
+ const [loading, setLoading] = useState(true);
267
+ const [loadError, setLoadError] = useState(null);
268
+ const [rows, setRows] = useState({});
269
+ useEffect(() => {
270
+ const client = generateClient();
271
+ client.queries.listAdminUsers().then(({ data, errors }) => {
272
+ if (errors && errors.length > 0) {
273
+ const msg = errors[0]?.message ?? "listAdminUsers failed";
274
+ console.error("[users-list-view] listAdminUsers errors:", errors);
275
+ setLoadError(msg);
276
+ return;
277
+ }
278
+ const list = data ?? [];
279
+ setUsers(list);
280
+ setRows(
281
+ Object.fromEntries(
282
+ list.map((u) => [u.userId, { selected: u.role, saving: false, error: null }])
283
+ )
284
+ );
285
+ }).catch((err) => {
286
+ console.error("[users-list-view] listAdminUsers threw:", err);
287
+ setLoadError(err instanceof Error ? err.message : String(err));
288
+ }).finally(() => setLoading(false));
289
+ }, []);
290
+ function updateRow(userId, patch) {
291
+ setRows((prev) => ({
292
+ ...prev,
293
+ [userId]: { ...prev[userId], ...patch }
294
+ }));
295
+ }
296
+ async function save(userId) {
297
+ const row = rows[userId];
298
+ if (!row) return;
299
+ updateRow(userId, { saving: true, error: null });
300
+ try {
301
+ const client = generateClient();
302
+ const { data, errors } = await client.mutations.setAdminUserRole({
303
+ userId,
304
+ role: row.selected
305
+ });
306
+ if (errors && errors.length > 0) {
307
+ const msg = errors[0]?.message ?? "setAdminUserRole failed";
308
+ console.error("[users-list-view] setAdminUserRole errors:", errors);
309
+ updateRow(userId, { saving: false, error: msg });
310
+ return;
311
+ }
312
+ if (data) {
313
+ setUsers(
314
+ (prev) => (prev ?? []).map((u) => u.userId === userId ? data : u)
315
+ );
316
+ updateRow(userId, { saving: false, selected: data.role, error: null });
317
+ } else {
318
+ updateRow(userId, { saving: false });
319
+ }
320
+ } catch (err) {
321
+ console.error("[users-list-view] setAdminUserRole threw:", err);
322
+ updateRow(userId, {
323
+ saving: false,
324
+ error: err instanceof Error ? err.message : String(err)
325
+ });
326
+ }
327
+ }
328
+ return /* @__PURE__ */ jsxs5("div", { className: "mx-auto max-w-7xl p-4 md:p-8", children: [
329
+ /* @__PURE__ */ jsxs5("div", { className: "mb-6 md:mb-8", children: [
330
+ /* @__PURE__ */ jsx5("h1", { className: "text-2xl font-bold md:text-3xl", children: t("users.list.title") }),
331
+ /* @__PURE__ */ jsx5("p", { className: "mt-1 text-sm text-muted-foreground", children: t("users.list.description") })
332
+ ] }),
333
+ loading ? /* @__PURE__ */ jsx5("p", { className: "text-muted-foreground", children: t("users.list.loading") }) : loadError ? /* @__PURE__ */ jsxs5("p", { className: "text-sm text-destructive", children: [
334
+ t("users.list.error"),
335
+ ": ",
336
+ loadError
337
+ ] }) : !users || users.length === 0 ? /* @__PURE__ */ jsx5("p", { className: "text-muted-foreground", children: t("users.list.empty") }) : /* @__PURE__ */ jsx5("div", { className: "overflow-x-auto rounded-md border", children: /* @__PURE__ */ jsxs5(Table2, { children: [
338
+ /* @__PURE__ */ jsx5(TableHeader2, { children: /* @__PURE__ */ jsxs5(TableRow2, { children: [
339
+ /* @__PURE__ */ jsx5(TableHead2, { children: t("users.list.columnEmail") }),
340
+ /* @__PURE__ */ jsx5(TableHead2, { children: t("users.list.columnRole") }),
341
+ /* @__PURE__ */ jsx5(TableHead2, { className: "w-[1%] whitespace-nowrap", children: t("users.list.columnActions") })
342
+ ] }) }),
343
+ /* @__PURE__ */ jsx5(TableBody2, { children: users.map((u) => {
344
+ const row = rows[u.userId];
345
+ if (!row) return null;
346
+ const isSelf = u.userId === currentUserId;
347
+ const dirty = row.selected !== u.role;
348
+ return /* @__PURE__ */ jsxs5(TableRow2, { children: [
349
+ /* @__PURE__ */ jsx5(TableCell2, { className: "font-medium", children: u.email || u.userId }),
350
+ /* @__PURE__ */ jsxs5(TableCell2, { children: [
351
+ /* @__PURE__ */ jsxs5(
352
+ "select",
353
+ {
354
+ className: "rounded-md border bg-background px-2 py-1.5 text-sm disabled:cursor-not-allowed disabled:opacity-60",
355
+ value: row.selected,
356
+ disabled: isSelf || row.saving,
357
+ onChange: (e) => {
358
+ const next = e.target.value;
359
+ if (isAdminRole(next)) {
360
+ updateRow(u.userId, { selected: next });
361
+ }
362
+ },
363
+ children: [
364
+ /* @__PURE__ */ jsx5("option", { value: "admin", children: t("users.list.roleAdmin") }),
365
+ /* @__PURE__ */ jsx5("option", { value: "editor", children: t("users.list.roleEditor") }),
366
+ /* @__PURE__ */ jsx5("option", { value: "none", children: t("users.list.roleNone") })
367
+ ]
368
+ }
369
+ ),
370
+ isSelf && /* @__PURE__ */ jsx5("p", { className: "mt-1 text-xs text-muted-foreground", children: t("users.list.cannotEditSelf") }),
371
+ row.error && /* @__PURE__ */ jsx5("p", { className: "mt-1 text-xs text-destructive", children: row.error })
372
+ ] }),
373
+ /* @__PURE__ */ jsx5(TableCell2, { children: /* @__PURE__ */ jsx5(
374
+ Button2,
375
+ {
376
+ size: "sm",
377
+ disabled: isSelf || row.saving || !dirty,
378
+ onClick: () => save(u.userId),
379
+ children: row.saving ? t("users.list.saving") : t("users.list.save")
380
+ }
381
+ ) })
382
+ ] }, u.userId);
383
+ }) })
384
+ ] }) })
385
+ ] });
386
+ }
387
+
388
+ // src/pages/users-list.tsx
389
+ import { jsx as jsx6 } from "react/jsx-runtime";
390
+ function createUsersListPage(admin) {
391
+ async function UsersPage() {
392
+ const session = await admin.getServerSession();
393
+ if (!admin.isAdmin(session)) {
394
+ redirect2("/admin");
395
+ }
396
+ return /* @__PURE__ */ jsx6(UsersListView, { currentUserId: session.userId });
397
+ }
398
+ return UsersPage;
399
+ }
400
+
235
401
  // src/pages/login.tsx
236
402
  function createLoginPage(_admin) {
237
403
  return LoginPage;
@@ -246,5 +412,6 @@ export {
246
412
  createPostsListPage,
247
413
  createSiteEditPage,
248
414
  createSiteThemePage,
249
- createSitesListPage
415
+ createSitesListPage,
416
+ createUsersListPage
250
417
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ampless/admin",
3
- "version": "0.2.0-alpha.6",
3
+ "version": "0.2.0-alpha.7",
4
4
  "description": "Admin UI for ampless: post editor, media manager, site/theme settings",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -50,8 +50,8 @@
50
50
  "lucide-react": "^1.16.0",
51
51
  "react-image-crop": "^11.0.7",
52
52
  "tailwind-merge": "^3.6.0",
53
- "ampless": "0.2.0-alpha.1",
54
- "@ampless/runtime": "0.2.0-alpha.3"
53
+ "ampless": "0.2.0-alpha.2",
54
+ "@ampless/runtime": "0.2.0-alpha.4"
55
55
  },
56
56
  "peerDependencies": {
57
57
  "next": "^15 || ^16",