@ampless/admin 0.2.0-alpha.1 → 0.2.0-alpha.11

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.
Files changed (44) hide show
  1. package/dist/api/index.d.ts +1 -1
  2. package/dist/chunk-2ITWLRYF.js +38 -0
  3. package/dist/chunk-4YEBIBFG.js +48 -0
  4. package/dist/chunk-5OIPGVGG.js +198 -0
  5. package/dist/chunk-7IJDOT2K.js +1197 -0
  6. package/dist/chunk-7IR4F7GA.js +6 -0
  7. package/dist/chunk-BFZODSUT.js +71 -0
  8. package/dist/chunk-BYLCQYEQ.js +21 -0
  9. package/dist/chunk-GDQC5X46.js +250 -0
  10. package/dist/chunk-OFHKZNZS.js +33 -0
  11. package/dist/{chunk-TJR3ALRJ.js → chunk-OPQ3SAZJ.js} +75 -66
  12. package/dist/chunk-QVUTNQZH.js +21 -0
  13. package/dist/chunk-TZWSXAHD.js +32 -0
  14. package/dist/chunk-UOU7KQLR.js +149 -0
  15. package/dist/chunk-VXEVLHGL.js +10 -0
  16. package/dist/chunk-XHWECTED.js +1125 -0
  17. package/dist/chunk-XXJDT6FF.js +335 -0
  18. package/dist/chunk-XZQRPXKN.js +41 -0
  19. package/dist/components/admin-dashboard.d.ts +10 -0
  20. package/dist/components/admin-dashboard.js +9 -0
  21. package/dist/components/edit-post-view.d.ts +9 -0
  22. package/dist/components/edit-post-view.js +14 -0
  23. package/dist/components/index.d.ts +32 -19
  24. package/dist/components/index.js +33 -15
  25. package/dist/components/login-view.d.ts +5 -0
  26. package/dist/components/login-view.js +9 -0
  27. package/dist/components/media-view.d.ts +5 -0
  28. package/dist/components/media-view.js +12 -0
  29. package/dist/components/new-post-view.d.ts +5 -0
  30. package/dist/components/new-post-view.js +14 -0
  31. package/dist/components/posts-list-view.d.ts +5 -0
  32. package/dist/components/posts-list-view.js +11 -0
  33. package/dist/components/users-list-view.d.ts +7 -0
  34. package/dist/components/users-list-view.js +9 -0
  35. package/dist/{i18n-ByHM_Bho.d.ts → i18n-DzXXcIQQ.d.ts} +110 -1
  36. package/dist/index.d.ts +2 -2
  37. package/dist/index.js +7 -3
  38. package/dist/lib/theme-actions.d.ts +17 -0
  39. package/dist/lib/theme-actions.js +7 -0
  40. package/dist/metafile-esm.json +1 -0
  41. package/dist/pages/index.d.ts +49 -15
  42. package/dist/pages/index.js +114 -658
  43. package/package.json +10 -9
  44. package/dist/chunk-T2RSMFOI.js +0 -2074
@@ -1,296 +1,54 @@
1
1
  import {
2
- I18nProvider,
3
- MediaUploader,
4
- PostForm,
2
+ PostsList
3
+ } from "../chunk-BFZODSUT.js";
4
+ import {
5
+ UsersListView
6
+ } from "../chunk-UOU7KQLR.js";
7
+ import {
8
+ AdminDashboard
9
+ } from "../chunk-4YEBIBFG.js";
10
+ import {
11
+ EditPostPage
12
+ } from "../chunk-XZQRPXKN.js";
13
+ import {
14
+ AdminProviders,
5
15
  Sidebar,
6
16
  SiteSelector,
7
17
  SiteSettingsForm,
8
- ThemeSettingsForm,
9
- setAdminCmsConfigClient,
10
- useT
11
- } from "../chunk-T2RSMFOI.js";
18
+ ThemeSettingsForm
19
+ } from "../chunk-XHWECTED.js";
20
+ import "../chunk-VXEVLHGL.js";
21
+ import {
22
+ LoginPage
23
+ } from "../chunk-5OIPGVGG.js";
24
+ import {
25
+ MediaPage
26
+ } from "../chunk-BYLCQYEQ.js";
27
+ import "../chunk-GDQC5X46.js";
28
+ import {
29
+ NewPostPage
30
+ } from "../chunk-QVUTNQZH.js";
31
+ import "../chunk-7IJDOT2K.js";
32
+ import "../chunk-TZWSXAHD.js";
33
+ import "../chunk-7IR4F7GA.js";
34
+ import "../chunk-XXJDT6FF.js";
35
+ import "../chunk-2ITWLRYF.js";
12
36
  import {
13
- readAdminSiteIdFromCookie,
14
- setAdminCmsConfig,
15
- setAdminMediaContext
16
- } from "../chunk-TJR3ALRJ.js";
37
+ I18nProvider
38
+ } from "../chunk-OFHKZNZS.js";
39
+ import "../chunk-OPQ3SAZJ.js";
17
40
 
18
41
  // src/pages/admin-layout.tsx
19
42
  import { redirect } from "next/navigation";
20
-
21
- // src/pages/admin-providers.tsx
22
- import { useEffect } from "react";
23
-
24
- // src/lib/amplify-client.ts
25
- import { Amplify } from "aws-amplify";
26
- var configured = false;
27
- function configureAmplify(outputs) {
28
- if (configured) return;
29
- Amplify.configure(outputs, { ssr: true });
30
- configured = true;
31
- }
32
-
33
- // src/lib/posts-provider.ts
34
- import { generateClient } from "aws-amplify/api";
35
- import {
36
- setPostsProvider,
37
- composeSiteIdStatus,
38
- composeSiteIdSlug
39
- } from "ampless";
40
- function encodeBody(value) {
41
- if (typeof value === "string") return value;
42
- return JSON.stringify(value ?? null);
43
- }
44
- function decodeBody(value) {
45
- if (typeof value !== "string") return value;
46
- try {
47
- return JSON.parse(value);
48
- } catch {
49
- return value;
50
- }
51
- }
52
- function toCorePost(p) {
43
+ import { jsx, jsxs } from "react/jsx-runtime";
44
+ function sanitizeCmsConfigForClient(config) {
53
45
  return {
54
- postId: p.postId,
55
- siteId: p.siteId,
56
- slug: p.slug,
57
- title: p.title,
58
- excerpt: p.excerpt ?? void 0,
59
- format: p.format ?? "markdown",
60
- body: decodeBody(p.body),
61
- status: p.status ?? "draft",
62
- publishedAt: p.publishedAt ?? void 0,
63
- tags: (p.tags ?? []).filter((t) => typeof t === "string")
46
+ ...config,
47
+ plugins: (config.plugins ?? []).map(
48
+ (p) => typeof p === "string" ? p : { name: p.name, apiVersion: p.apiVersion, trust_level: p.trust_level }
49
+ )
64
50
  };
65
51
  }
66
- function postTagEntries(post) {
67
- if (post.status !== "published" || !post.publishedAt || !post.tags?.length) return [];
68
- return post.tags.map((tag) => ({
69
- siteIdTag: `${post.siteId}#${tag}`,
70
- publishedAtPostId: `${post.publishedAt}#${post.postId}`
71
- }));
72
- }
73
- function entryKey(e) {
74
- return `${e.siteIdTag}|${e.publishedAtPostId}`;
75
- }
76
- var installed = false;
77
- function installAdminPostsProvider() {
78
- if (installed) return;
79
- installed = true;
80
- const client = generateClient();
81
- async function syncPostTags(post, oldPost) {
82
- const oldEntries = oldPost ? postTagEntries(oldPost) : [];
83
- const newEntries = postTagEntries(post);
84
- const oldKeys = new Set(oldEntries.map(entryKey));
85
- const newKeys = new Set(newEntries.map(entryKey));
86
- await Promise.all(
87
- oldEntries.filter((e) => !newKeys.has(entryKey(e))).map((e) => client.models.PostTag.delete(e))
88
- );
89
- await Promise.all(
90
- newEntries.filter((e) => !oldKeys.has(entryKey(e))).map(
91
- (e) => client.models.PostTag.create({
92
- siteIdTag: e.siteIdTag,
93
- publishedAtPostId: e.publishedAtPostId,
94
- siteId: post.siteId,
95
- tag: e.siteIdTag.slice(post.siteId.length + 1),
96
- postId: post.postId,
97
- publishedAt: post.publishedAt,
98
- slug: post.slug,
99
- title: post.title,
100
- excerpt: post.excerpt,
101
- tags: post.tags ?? []
102
- })
103
- )
104
- );
105
- await Promise.all(
106
- newEntries.filter((e) => oldKeys.has(entryKey(e))).map(
107
- (e) => client.models.PostTag.update({
108
- siteIdTag: e.siteIdTag,
109
- publishedAtPostId: e.publishedAtPostId,
110
- slug: post.slug,
111
- title: post.title,
112
- excerpt: post.excerpt,
113
- tags: post.tags ?? []
114
- })
115
- )
116
- );
117
- }
118
- const provider = {
119
- async list(opts = {}) {
120
- const siteId = opts.siteId ?? "default";
121
- const status = opts.status ?? "published";
122
- const filter = { siteId: { eq: siteId } };
123
- if (status !== "all") filter.status = { eq: status };
124
- const { data } = await client.models.Post.list({ filter, limit: opts.limit ?? 100 });
125
- return data.map(toCorePost);
126
- },
127
- async get(slug, opts = {}) {
128
- const siteId = opts.siteId ?? "default";
129
- const { data } = await client.models.Post.list({
130
- filter: { siteId: { eq: siteId }, slug: { eq: slug } },
131
- limit: 1
132
- });
133
- return data[0] ? toCorePost(data[0]) : null;
134
- },
135
- async getById(postId, opts = {}) {
136
- const siteId = opts.siteId ?? "default";
137
- const { data } = await client.models.Post.get({ siteId, postId });
138
- return data ? toCorePost(data) : null;
139
- },
140
- async create(input) {
141
- const postId = input.postId ?? `post-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
142
- const { data, errors } = await client.models.Post.create({
143
- siteId: input.siteId,
144
- postId,
145
- slug: input.slug,
146
- title: input.title,
147
- excerpt: input.excerpt,
148
- format: input.format,
149
- body: encodeBody(input.body),
150
- status: input.status,
151
- publishedAt: input.publishedAt,
152
- tags: input.tags,
153
- // Denormalized GSI keys. Must match every change to slug /
154
- // status — see the update() branch below.
155
- siteIdStatus: composeSiteIdStatus(input.siteId, input.status),
156
- siteIdSlug: composeSiteIdSlug(input.siteId, input.slug)
157
- });
158
- if (errors || !data) throw new Error(errors?.[0]?.message ?? "Failed to create post");
159
- const created = toCorePost(data);
160
- await syncPostTags(created, null);
161
- return created;
162
- },
163
- async update(postId, patch, opts = {}) {
164
- const siteId = opts.siteId ?? "default";
165
- const oldPost = await this.getById(postId, { siteId });
166
- const nextStatus = patch.status ?? oldPost?.status;
167
- const nextSlug = patch.slug ?? oldPost?.slug;
168
- const { data, errors } = await client.models.Post.update({
169
- siteId,
170
- postId,
171
- ...patch.slug !== void 0 && { slug: patch.slug },
172
- ...patch.title !== void 0 && { title: patch.title },
173
- ...patch.excerpt !== void 0 && { excerpt: patch.excerpt },
174
- ...patch.format !== void 0 && { format: patch.format },
175
- ...patch.body !== void 0 && { body: encodeBody(patch.body) },
176
- ...patch.status !== void 0 && { status: patch.status },
177
- ...patch.publishedAt !== void 0 && { publishedAt: patch.publishedAt },
178
- ...patch.tags !== void 0 && { tags: patch.tags },
179
- ...patch.status !== void 0 && nextStatus && { siteIdStatus: composeSiteIdStatus(siteId, nextStatus) },
180
- ...patch.slug !== void 0 && nextSlug && { siteIdSlug: composeSiteIdSlug(siteId, nextSlug) }
181
- });
182
- if (errors || !data) throw new Error(errors?.[0]?.message ?? "Failed to update post");
183
- const updated = toCorePost(data);
184
- await syncPostTags(updated, oldPost);
185
- return updated;
186
- },
187
- async remove(postId, opts = {}) {
188
- const siteId = opts.siteId ?? "default";
189
- const oldPost = await this.getById(postId, { siteId });
190
- if (oldPost) {
191
- await syncPostTags({ ...oldPost, status: "draft" }, oldPost);
192
- }
193
- const { errors } = await client.models.Post.delete({ siteId, postId });
194
- if (errors) throw new Error(errors[0]?.message ?? "Failed to delete post");
195
- }
196
- };
197
- setPostsProvider(provider);
198
- }
199
-
200
- // src/lib/kv-provider.ts
201
- import { generateClient as generateClient2 } from "aws-amplify/api";
202
- import { setKvStore } from "ampless";
203
- var installed2 = false;
204
- function installAdminKvProvider() {
205
- if (installed2) return;
206
- installed2 = true;
207
- const client = generateClient2();
208
- function requireModel() {
209
- const m = client.models.KvStore;
210
- if (!m) {
211
- throw new Error(
212
- "KvStore model is not available on the AppSync client. Did you redeploy the sandbox? Run `npx ampx sandbox` and wait for it to finish, then reload this page."
213
- );
214
- }
215
- return m;
216
- }
217
- function encodeValue(value) {
218
- return JSON.stringify(value ?? null);
219
- }
220
- function decodeValue(raw) {
221
- if (typeof raw !== "string") return raw;
222
- try {
223
- return JSON.parse(raw);
224
- } catch {
225
- return raw;
226
- }
227
- }
228
- const store = {
229
- async get(pk, sk) {
230
- const model = requireModel();
231
- const { data } = await model.get({ pk, sk });
232
- return data ? decodeValue(data.value) : null;
233
- },
234
- async query(pk) {
235
- const model = requireModel();
236
- const { data } = await model.list({
237
- filter: { pk: { eq: pk } },
238
- limit: 1e3
239
- });
240
- return (data ?? []).map((row) => ({
241
- pk: row.pk,
242
- sk: row.sk,
243
- value: decodeValue(row.value),
244
- ttl: row.ttl ?? void 0
245
- }));
246
- },
247
- async put(pk, sk, value, opts) {
248
- const model = requireModel();
249
- const ttl = opts?.ttlSeconds ? Math.floor(Date.now() / 1e3) + opts.ttlSeconds : void 0;
250
- const existing = await model.get({ pk, sk });
251
- if (existing.data) {
252
- const { errors } = await model.update({
253
- pk,
254
- sk,
255
- value: encodeValue(value),
256
- ttl: ttl ?? null
257
- });
258
- if (errors) throw new Error(errors[0]?.message ?? "KvStore.update failed");
259
- } else {
260
- const { errors } = await model.create({
261
- pk,
262
- sk,
263
- value: encodeValue(value),
264
- ttl
265
- });
266
- if (errors) throw new Error(errors[0]?.message ?? "KvStore.create failed");
267
- }
268
- },
269
- async remove(pk, sk) {
270
- const model = requireModel();
271
- const { errors } = await model.delete({ pk, sk });
272
- if (errors) throw new Error(errors[0]?.message ?? "KvStore.delete failed");
273
- }
274
- };
275
- setKvStore(store);
276
- }
277
-
278
- // src/pages/admin-providers.tsx
279
- import { Fragment, jsx } from "react/jsx-runtime";
280
- function AdminProviders({ outputs, cmsConfig, children }) {
281
- configureAmplify(outputs);
282
- setAdminCmsConfig(cmsConfig);
283
- setAdminCmsConfigClient(cmsConfig);
284
- setAdminMediaContext(outputs, cmsConfig);
285
- useEffect(() => {
286
- installAdminPostsProvider();
287
- installAdminKvProvider();
288
- }, []);
289
- return /* @__PURE__ */ jsx(Fragment, { children });
290
- }
291
-
292
- // src/pages/admin-layout.tsx
293
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
294
52
  function createAdminLayout(admin) {
295
53
  async function AdminLayout({ children }) {
296
54
  const session = await admin.getServerSession();
@@ -299,216 +57,87 @@ function createAdminLayout(admin) {
299
57
  }
300
58
  const sites = admin.adminSiteOptions();
301
59
  const currentSiteId = await admin.currentAdminSiteId();
302
- const selector = sites.length > 0 ? /* @__PURE__ */ jsx2(SiteSelector, { current: currentSiteId, sites }) : null;
303
- return /* @__PURE__ */ jsx2(AdminProviders, { outputs: admin.outputs, cmsConfig: admin.cmsConfig, children: /* @__PURE__ */ jsx2(I18nProvider, { locale: admin.locale, dict: admin.dict, children: /* @__PURE__ */ jsxs("div", { className: "flex min-h-screen", children: [
304
- /* @__PURE__ */ jsx2(Sidebar, { email: session.email, siteSelector: selector }),
305
- /* @__PURE__ */ jsx2("main", { className: "flex-1 overflow-auto", children })
60
+ const selector = sites.length > 0 ? /* @__PURE__ */ jsx(SiteSelector, { current: currentSiteId, sites }) : null;
61
+ 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: [
62
+ /* @__PURE__ */ jsx(
63
+ Sidebar,
64
+ {
65
+ email: session.email,
66
+ siteSelector: selector,
67
+ isAdmin: admin.isAdmin(session)
68
+ }
69
+ ),
70
+ /* @__PURE__ */ jsx("main", { className: "min-w-0 flex-1", children })
306
71
  ] }) }) });
307
72
  }
308
73
  return AdminLayout;
309
74
  }
310
75
 
311
76
  // src/pages/dashboard.tsx
312
- import { useEffect as useEffect2, useState } from "react";
313
- import Link from "next/link";
314
- import { listPosts } from "ampless";
315
- import { Button, Card, CardContent, CardHeader, CardTitle } from "@ampless/runtime/ui";
316
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
317
- function AdminDashboard() {
318
- const t = useT();
319
- const [posts, setPosts] = useState([]);
320
- const [loading, setLoading] = useState(true);
321
- useEffect2(() => {
322
- listPosts({ status: "all" }).then(setPosts).finally(() => setLoading(false));
323
- }, []);
324
- const published = posts.filter((p) => p.status === "published").length;
325
- const drafts = posts.filter((p) => p.status === "draft").length;
326
- return /* @__PURE__ */ jsxs2("div", { className: "p-8", children: [
327
- /* @__PURE__ */ jsxs2("div", { className: "mb-8 flex items-center justify-between", children: [
328
- /* @__PURE__ */ jsx3("h1", { className: "text-3xl font-bold", children: t("dashboard.title") }),
329
- /* @__PURE__ */ jsx3(Button, { asChild: true, children: /* @__PURE__ */ jsx3(Link, { href: "/admin/posts/new", children: t("dashboard.newPost") }) })
330
- ] }),
331
- /* @__PURE__ */ jsxs2("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-3", children: [
332
- /* @__PURE__ */ jsxs2(Card, { children: [
333
- /* @__PURE__ */ jsx3(CardHeader, { children: /* @__PURE__ */ jsx3(CardTitle, { children: t("dashboard.totalPosts") }) }),
334
- /* @__PURE__ */ jsxs2(CardContent, { children: [
335
- /* @__PURE__ */ jsx3("p", { className: "text-3xl font-bold", children: loading ? "\u2014" : posts.length }),
336
- /* @__PURE__ */ jsx3("p", { className: "text-sm text-muted-foreground", children: t("dashboard.totalLabel") })
337
- ] })
338
- ] }),
339
- /* @__PURE__ */ jsxs2(Card, { children: [
340
- /* @__PURE__ */ jsx3(CardHeader, { children: /* @__PURE__ */ jsx3(CardTitle, { children: t("dashboard.published") }) }),
341
- /* @__PURE__ */ jsx3(CardContent, { children: /* @__PURE__ */ jsx3("p", { className: "text-3xl font-bold", children: loading ? "\u2014" : published }) })
342
- ] }),
343
- /* @__PURE__ */ jsxs2(Card, { children: [
344
- /* @__PURE__ */ jsx3(CardHeader, { children: /* @__PURE__ */ jsx3(CardTitle, { children: t("dashboard.drafts") }) }),
345
- /* @__PURE__ */ jsx3(CardContent, { children: /* @__PURE__ */ jsx3("p", { className: "text-3xl font-bold", children: loading ? "\u2014" : drafts }) })
346
- ] })
347
- ] })
348
- ] });
349
- }
350
77
  function createAdminDashboardPage(_admin) {
351
78
  return AdminDashboard;
352
79
  }
353
80
 
354
81
  // src/pages/posts-list.tsx
355
- import { useEffect as useEffect3, useState as useState2 } from "react";
356
- import Link2 from "next/link";
357
- import { listPosts as listPosts2 } from "ampless";
358
- import {
359
- Button as Button2,
360
- Table,
361
- TableBody,
362
- TableCell,
363
- TableHead,
364
- TableHeader,
365
- TableRow
366
- } from "@ampless/runtime/ui";
367
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
368
- function PostsList() {
369
- const t = useT();
370
- const [posts, setPosts] = useState2([]);
371
- const [loading, setLoading] = useState2(true);
372
- useEffect3(() => {
373
- const siteId = readAdminSiteIdFromCookie();
374
- listPosts2({ status: "all", siteId }).then(setPosts).finally(() => setLoading(false));
375
- }, []);
376
- return /* @__PURE__ */ jsxs3("div", { className: "p-8", children: [
377
- /* @__PURE__ */ jsxs3("div", { className: "mb-8 flex items-center justify-between", children: [
378
- /* @__PURE__ */ jsx4("h1", { className: "text-3xl font-bold", children: t("posts.list.title") }),
379
- /* @__PURE__ */ jsx4(Button2, { asChild: true, children: /* @__PURE__ */ jsx4(Link2, { href: "/admin/posts/new", children: t("posts.list.newButton") }) })
380
- ] }),
381
- loading ? /* @__PURE__ */ jsx4("p", { className: "text-muted-foreground", children: t("common.loading") }) : posts.length === 0 ? /* @__PURE__ */ jsxs3("div", { className: "rounded-md border p-12 text-center", children: [
382
- /* @__PURE__ */ jsx4("p", { className: "text-muted-foreground", children: t("posts.list.empty") }),
383
- /* @__PURE__ */ jsx4(Button2, { asChild: true, className: "mt-4", children: /* @__PURE__ */ jsx4(Link2, { href: "/admin/posts/new", children: t("posts.list.createFirst") }) })
384
- ] }) : /* @__PURE__ */ jsx4("div", { className: "rounded-md border", children: /* @__PURE__ */ jsxs3(Table, { children: [
385
- /* @__PURE__ */ jsx4(TableHeader, { children: /* @__PURE__ */ jsxs3(TableRow, { children: [
386
- /* @__PURE__ */ jsx4(TableHead, { children: t("posts.list.columnTitle") }),
387
- /* @__PURE__ */ jsx4(TableHead, { children: t("posts.list.columnStatus") }),
388
- /* @__PURE__ */ jsx4(TableHead, { children: t("posts.list.columnSlug") }),
389
- /* @__PURE__ */ jsx4(TableHead, { children: t("posts.list.columnUpdated") })
390
- ] }) }),
391
- /* @__PURE__ */ jsx4(TableBody, { children: posts.map((post) => /* @__PURE__ */ jsxs3(TableRow, { children: [
392
- /* @__PURE__ */ jsx4(TableCell, { children: /* @__PURE__ */ jsx4(
393
- Link2,
394
- {
395
- href: `/admin/posts/${post.postId}`,
396
- className: "font-medium hover:underline",
397
- children: post.title
398
- }
399
- ) }),
400
- /* @__PURE__ */ jsx4(TableCell, { children: /* @__PURE__ */ jsx4(
401
- "span",
402
- {
403
- className: post.status === "published" ? "inline-block rounded-full bg-green-100 px-2 py-0.5 text-xs text-green-700" : "inline-block rounded-full bg-gray-100 px-2 py-0.5 text-xs text-gray-700",
404
- children: t(`common.${post.status}`)
405
- }
406
- ) }),
407
- /* @__PURE__ */ jsx4(TableCell, { className: "font-mono text-xs text-muted-foreground", children: post.slug }),
408
- /* @__PURE__ */ jsx4(TableCell, { className: "text-sm text-muted-foreground", children: post.publishedAt ? new Date(post.publishedAt).toLocaleDateString() : "\u2014" })
409
- ] }, post.postId)) })
410
- ] }) })
411
- ] });
412
- }
413
82
  function createPostsListPage(_admin) {
414
83
  return PostsList;
415
84
  }
416
85
 
417
86
  // src/pages/post-new.tsx
418
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
419
- function NewPostPage() {
420
- const t = useT();
421
- return /* @__PURE__ */ jsxs4("div", { className: "p-8", children: [
422
- /* @__PURE__ */ jsx5("h1", { className: "mb-8 text-3xl font-bold", children: t("posts.form.newTitle") }),
423
- /* @__PURE__ */ jsx5(PostForm, {})
424
- ] });
425
- }
426
87
  function createNewPostPage(_admin) {
427
88
  return NewPostPage;
428
89
  }
429
90
 
430
91
  // src/pages/post-edit.tsx
431
- import { useEffect as useEffect4, useState as useState3, use } from "react";
432
- import { notFound } from "next/navigation";
433
- import { getPostById } from "ampless";
434
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
435
- function EditPostPage({ params }) {
436
- const t = useT();
437
- const { postId } = use(params);
438
- const [post, setPost] = useState3(null);
439
- const [loading, setLoading] = useState3(true);
440
- const [missing, setMissing] = useState3(false);
441
- useEffect4(() => {
442
- const siteId = readAdminSiteIdFromCookie();
443
- getPostById(postId, { siteId }).then((p) => {
444
- if (!p) setMissing(true);
445
- else setPost(p);
446
- }).finally(() => setLoading(false));
447
- }, [postId]);
448
- if (loading) return /* @__PURE__ */ jsx6("div", { className: "p-8", children: t("common.loading") });
449
- if (missing) notFound();
450
- return /* @__PURE__ */ jsxs5("div", { className: "p-8", children: [
451
- /* @__PURE__ */ jsx6("h1", { className: "mb-8 text-3xl font-bold", children: t("posts.form.editTitle") }),
452
- post && /* @__PURE__ */ jsx6(PostForm, { post })
453
- ] });
454
- }
455
92
  function createEditPostPage(_admin) {
456
93
  return EditPostPage;
457
94
  }
458
95
 
459
96
  // src/pages/media.tsx
460
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
461
- function MediaPage() {
462
- const t = useT();
463
- return /* @__PURE__ */ jsxs6("div", { className: "p-8", children: [
464
- /* @__PURE__ */ jsx7("h1", { className: "mb-8 text-3xl font-bold", children: t("media.title") }),
465
- /* @__PURE__ */ jsx7(MediaUploader, {})
466
- ] });
467
- }
468
97
  function createMediaPage(_admin) {
469
98
  return MediaPage;
470
99
  }
471
100
 
472
101
  // src/pages/sites-list.tsx
473
- import Link3 from "next/link";
102
+ import Link from "next/link";
474
103
  import { DEFAULT_SITE_ID, isMultiSite, siteFor } from "ampless";
475
104
  import {
476
- Button as Button3,
477
- Table as Table2,
478
- TableBody as TableBody2,
479
- TableCell as TableCell2,
480
- TableHead as TableHead2,
481
- TableHeader as TableHeader2,
482
- TableRow as TableRow2
105
+ Button,
106
+ Table,
107
+ TableBody,
108
+ TableCell,
109
+ TableHead,
110
+ TableHeader,
111
+ TableRow
483
112
  } from "@ampless/runtime/ui";
484
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
113
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
485
114
  function createSitesListPage(admin) {
486
115
  const { cmsConfig, t } = admin;
487
116
  async function SitesPage() {
488
117
  const multi = isMultiSite(cmsConfig);
489
118
  const ids = multi ? Object.keys(cmsConfig.sites ?? {}) : [DEFAULT_SITE_ID];
490
- return /* @__PURE__ */ jsxs7("div", { className: "p-8", children: [
491
- /* @__PURE__ */ jsx8("div", { className: "mb-8 flex items-center justify-between", children: /* @__PURE__ */ jsxs7("div", { children: [
492
- /* @__PURE__ */ jsx8("h1", { className: "text-3xl font-bold", children: t("sites.list.title") }),
493
- /* @__PURE__ */ jsx8("p", { className: "mt-1 text-sm text-muted-foreground", children: t("sites.list.description") })
119
+ return /* @__PURE__ */ jsxs2("div", { className: "mx-auto max-w-7xl p-4 md:p-8", children: [
120
+ /* @__PURE__ */ jsx2("div", { className: "mb-6 flex flex-wrap items-center justify-between gap-3 md:mb-8", children: /* @__PURE__ */ jsxs2("div", { children: [
121
+ /* @__PURE__ */ jsx2("h1", { className: "text-2xl font-bold md:text-3xl", children: t("sites.list.title") }),
122
+ /* @__PURE__ */ jsx2("p", { className: "mt-1 text-sm text-muted-foreground", children: t("sites.list.description") })
494
123
  ] }) }),
495
- /* @__PURE__ */ jsx8("div", { className: "rounded-md border", children: /* @__PURE__ */ jsxs7(Table2, { children: [
496
- /* @__PURE__ */ jsx8(TableHeader2, { children: /* @__PURE__ */ jsxs7(TableRow2, { children: [
497
- /* @__PURE__ */ jsx8(TableHead2, { children: t("sites.list.columnSiteId") }),
498
- /* @__PURE__ */ jsx8(TableHead2, { children: t("sites.list.columnName") }),
499
- /* @__PURE__ */ jsx8(TableHead2, { children: t("sites.list.columnUrl") }),
500
- /* @__PURE__ */ jsx8(TableHead2, { children: t("sites.list.columnDomains") }),
501
- /* @__PURE__ */ jsx8(TableHead2, {})
124
+ /* @__PURE__ */ jsx2("div", { className: "overflow-x-auto rounded-md border", children: /* @__PURE__ */ jsxs2(Table, { children: [
125
+ /* @__PURE__ */ jsx2(TableHeader, { children: /* @__PURE__ */ jsxs2(TableRow, { children: [
126
+ /* @__PURE__ */ jsx2(TableHead, { children: t("sites.list.columnSiteId") }),
127
+ /* @__PURE__ */ jsx2(TableHead, { children: t("sites.list.columnName") }),
128
+ /* @__PURE__ */ jsx2(TableHead, { children: t("sites.list.columnUrl") }),
129
+ /* @__PURE__ */ jsx2(TableHead, { children: t("sites.list.columnDomains") }),
130
+ /* @__PURE__ */ jsx2(TableHead, {})
502
131
  ] }) }),
503
- /* @__PURE__ */ jsx8(TableBody2, { children: ids.map((id) => {
132
+ /* @__PURE__ */ jsx2(TableBody, { children: ids.map((id) => {
504
133
  const site = siteFor(id, cmsConfig);
505
134
  const domains = cmsConfig.sites?.[id]?.domains ?? [];
506
- return /* @__PURE__ */ jsxs7(TableRow2, { children: [
507
- /* @__PURE__ */ jsx8(TableCell2, { className: "font-mono text-xs", children: id }),
508
- /* @__PURE__ */ jsx8(TableCell2, { className: "font-medium", children: site.name }),
509
- /* @__PURE__ */ jsx8(TableCell2, { className: "text-sm text-muted-foreground", children: site.url }),
510
- /* @__PURE__ */ jsx8(TableCell2, { className: "text-sm text-muted-foreground", children: domains.length > 0 ? domains.join(", ") : "\u2014" }),
511
- /* @__PURE__ */ jsx8(TableCell2, { children: /* @__PURE__ */ jsx8(Button3, { asChild: true, variant: "outline", size: "sm", children: /* @__PURE__ */ jsx8(Link3, { href: `/admin/sites/${id}`, children: t("sites.list.edit") }) }) })
135
+ return /* @__PURE__ */ jsxs2(TableRow, { children: [
136
+ /* @__PURE__ */ jsx2(TableCell, { className: "font-mono text-xs", children: id }),
137
+ /* @__PURE__ */ jsx2(TableCell, { className: "font-medium", children: site.name }),
138
+ /* @__PURE__ */ jsx2(TableCell, { className: "text-sm text-muted-foreground", children: site.url }),
139
+ /* @__PURE__ */ jsx2(TableCell, { className: "text-sm text-muted-foreground", children: domains.length > 0 ? domains.join(", ") : "\u2014" }),
140
+ /* @__PURE__ */ jsx2(TableCell, { children: /* @__PURE__ */ jsx2(Button, { asChild: true, variant: "outline", size: "sm", children: /* @__PURE__ */ jsx2(Link, { href: `/admin/sites/${id}`, children: t("sites.list.edit") }) }) })
512
141
  ] }, id);
513
142
  }) })
514
143
  ] }) })
@@ -518,9 +147,9 @@ function createSitesListPage(admin) {
518
147
  }
519
148
 
520
149
  // src/pages/site-edit.tsx
521
- import Link4 from "next/link";
150
+ import Link2 from "next/link";
522
151
  import { siteFor as siteFor2 } from "ampless";
523
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
152
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
524
153
  function createSiteEditPage(admin) {
525
154
  const { cmsConfig, t, loadSiteSettings } = admin;
526
155
  async function EditSitePage({ params }) {
@@ -545,10 +174,10 @@ function createSiteEditPage(admin) {
545
174
  dateFormat: settings.dateFormat,
546
175
  timezone: settings.timezone
547
176
  };
548
- return /* @__PURE__ */ jsxs8("div", { className: "p-8", children: [
549
- /* @__PURE__ */ jsxs8("div", { className: "mb-8", children: [
550
- /* @__PURE__ */ jsxs8(
551
- Link4,
177
+ return /* @__PURE__ */ jsxs3("div", { className: "mx-auto max-w-7xl p-4 md:p-8", children: [
178
+ /* @__PURE__ */ jsxs3("div", { className: "mb-6 md:mb-8", children: [
179
+ /* @__PURE__ */ jsxs3(
180
+ Link2,
552
181
  {
553
182
  href: "/admin/sites",
554
183
  className: "text-sm text-muted-foreground hover:underline",
@@ -558,14 +187,14 @@ function createSiteEditPage(admin) {
558
187
  ]
559
188
  }
560
189
  ),
561
- /* @__PURE__ */ jsx9("h1", { className: "mt-2 text-3xl font-bold", children: settings.site.name }),
562
- /* @__PURE__ */ jsxs8("p", { className: "text-sm text-muted-foreground", children: [
190
+ /* @__PURE__ */ jsx3("h1", { className: "mt-2 text-2xl font-bold md:text-3xl", children: settings.site.name }),
191
+ /* @__PURE__ */ jsxs3("p", { className: "text-sm text-muted-foreground", children: [
563
192
  t("common.siteId"),
564
193
  ": ",
565
- /* @__PURE__ */ jsx9("code", { className: "font-mono", children: siteId })
194
+ /* @__PURE__ */ jsx3("code", { className: "font-mono", children: siteId })
566
195
  ] }),
567
- /* @__PURE__ */ jsx9("div", { className: "mt-4", children: /* @__PURE__ */ jsx9(
568
- Link4,
196
+ /* @__PURE__ */ jsx3("div", { className: "mt-4", children: /* @__PURE__ */ jsx3(
197
+ Link2,
569
198
  {
570
199
  href: `/admin/sites/${siteId}/theme`,
571
200
  className: "text-sm font-medium underline",
@@ -573,16 +202,16 @@ function createSiteEditPage(admin) {
573
202
  }
574
203
  ) })
575
204
  ] }),
576
- /* @__PURE__ */ jsx9(SiteSettingsForm, { siteId, initial, fallback })
205
+ /* @__PURE__ */ jsx3(SiteSettingsForm, { siteId, initial, fallback })
577
206
  ] });
578
207
  }
579
208
  return EditSitePage;
580
209
  }
581
210
 
582
211
  // src/pages/site-theme.tsx
583
- import Link5 from "next/link";
212
+ import Link3 from "next/link";
584
213
  import { siteFor as siteFor3, resolveLocalized } from "ampless";
585
- import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
214
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
586
215
  function createSiteThemePage(admin, themeList) {
587
216
  const { cmsConfig, t, locale, loadThemeConfig } = admin;
588
217
  async function ThemePage({ params }) {
@@ -594,10 +223,10 @@ function createSiteThemePage(admin, themeList) {
594
223
  label: m.manifest.label,
595
224
  description: m.manifest.description
596
225
  }));
597
- return /* @__PURE__ */ jsxs9("div", { className: "p-8", children: [
598
- /* @__PURE__ */ jsxs9("div", { className: "mb-8", children: [
599
- /* @__PURE__ */ jsxs9(
600
- Link5,
226
+ return /* @__PURE__ */ jsxs4("div", { className: "mx-auto max-w-7xl p-4 md:p-8", children: [
227
+ /* @__PURE__ */ jsxs4("div", { className: "mb-6 md:mb-8", children: [
228
+ /* @__PURE__ */ jsxs4(
229
+ Link3,
601
230
  {
602
231
  href: `/admin/sites/${siteId}`,
603
232
  className: "text-sm text-muted-foreground hover:underline",
@@ -607,18 +236,18 @@ function createSiteThemePage(admin, themeList) {
607
236
  ]
608
237
  }
609
238
  ),
610
- /* @__PURE__ */ jsx10("h1", { className: "mt-2 text-3xl font-bold", children: t("theme.title") }),
611
- /* @__PURE__ */ jsxs9("p", { className: "text-sm text-muted-foreground", children: [
239
+ /* @__PURE__ */ jsx4("h1", { className: "mt-2 text-2xl font-bold md:text-3xl", children: t("theme.title") }),
240
+ /* @__PURE__ */ jsxs4("p", { className: "text-sm text-muted-foreground", children: [
612
241
  t("common.active"),
613
242
  ":",
614
243
  " ",
615
- /* @__PURE__ */ jsx10("strong", { children: resolveLocalized(theme.manifest.label, locale) }),
244
+ /* @__PURE__ */ jsx4("strong", { children: resolveLocalized(theme.manifest.label, locale) }),
616
245
  " (",
617
246
  theme.activeTheme,
618
247
  ")"
619
248
  ] })
620
249
  ] }),
621
- /* @__PURE__ */ jsx10(
250
+ /* @__PURE__ */ jsx4(
622
251
  ThemeSettingsForm,
623
252
  {
624
253
  siteId,
@@ -633,195 +262,21 @@ function createSiteThemePage(admin, themeList) {
633
262
  return ThemePage;
634
263
  }
635
264
 
636
- // src/pages/login.tsx
637
- import { useState as useState4 } from "react";
638
- import { useRouter } from "next/navigation";
639
- import {
640
- signIn,
641
- signUp,
642
- confirmSignUp,
643
- resetPassword,
644
- confirmResetPassword
645
- } from "aws-amplify/auth";
646
- import {
647
- Button as Button4,
648
- Input,
649
- Label,
650
- Card as Card2,
651
- CardContent as CardContent2,
652
- CardHeader as CardHeader2,
653
- CardTitle as CardTitle2,
654
- CardDescription
655
- } from "@ampless/runtime/ui";
656
- import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
657
- function LoginPage() {
658
- const router = useRouter();
659
- const t = useT();
660
- const [mode, setMode] = useState4("signIn");
661
- const [email, setEmail] = useState4("");
662
- const [password, setPassword] = useState4("");
663
- const [code, setCode] = useState4("");
664
- const [error, setError] = useState4(null);
665
- const [info, setInfo] = useState4(null);
666
- const [loading, setLoading] = useState4(false);
667
- function go(next) {
668
- setMode(next);
669
- setError(null);
670
- setInfo(null);
671
- setCode("");
672
- if (next === "signIn" || next === "signUp" || next === "forgot") setPassword("");
673
- }
674
- async function handleSubmit(e) {
675
- e.preventDefault();
676
- setError(null);
677
- setInfo(null);
678
- setLoading(true);
679
- try {
680
- if (mode === "signIn") {
681
- const result = await signIn({ username: email, password });
682
- if (result.isSignedIn) {
683
- router.push("/admin");
684
- router.refresh();
685
- } else {
686
- setError(t("auth.additionalStep", { step: result.nextStep.signInStep }));
687
- }
688
- } else if (mode === "signUp") {
689
- await signUp({
690
- username: email,
691
- password,
692
- options: { userAttributes: { email } }
693
- });
694
- go("confirm");
695
- } else if (mode === "confirm") {
696
- await confirmSignUp({ username: email, confirmationCode: code });
697
- const result = await signIn({ username: email, password });
698
- if (result.isSignedIn) {
699
- router.push("/admin");
700
- router.refresh();
701
- }
702
- } else if (mode === "forgot") {
703
- await resetPassword({ username: email });
704
- setMode("reset");
705
- setInfo(t("auth.forgot.codeSent"));
706
- } else if (mode === "reset") {
707
- await confirmResetPassword({
708
- username: email,
709
- confirmationCode: code,
710
- newPassword: password
711
- });
712
- const result = await signIn({ username: email, password });
713
- if (result.isSignedIn) {
714
- router.push("/admin");
715
- router.refresh();
716
- } else {
717
- setMode("signIn");
718
- setInfo(t("auth.reset.passwordUpdated"));
719
- }
720
- }
721
- } catch (err) {
722
- setError(err instanceof Error ? err.message : String(err));
723
- } finally {
724
- setLoading(false);
265
+ // src/pages/users-list.tsx
266
+ import { redirect as redirect2 } from "next/navigation";
267
+ import { jsx as jsx5 } from "react/jsx-runtime";
268
+ function createUsersListPage(admin) {
269
+ async function UsersPage() {
270
+ const session = await admin.getServerSession();
271
+ if (!admin.isAdmin(session)) {
272
+ redirect2("/admin");
725
273
  }
274
+ return /* @__PURE__ */ jsx5(UsersListView, { currentUserId: session.userId });
726
275
  }
727
- const showEmail = mode !== "confirm" && mode !== "reset";
728
- const showPassword = mode === "signIn" || mode === "signUp" || mode === "reset";
729
- const showCode = mode === "confirm" || mode === "reset";
730
- return /* @__PURE__ */ jsx11("main", { className: "flex min-h-screen items-center justify-center bg-muted/30 p-4", children: /* @__PURE__ */ jsxs10(Card2, { className: "w-full max-w-md", children: [
731
- /* @__PURE__ */ jsxs10(CardHeader2, { children: [
732
- /* @__PURE__ */ jsx11(CardTitle2, { children: t(`auth.${mode}.title`) }),
733
- /* @__PURE__ */ jsx11(CardDescription, { children: t(`auth.${mode}.description`) })
734
- ] }),
735
- /* @__PURE__ */ jsx11(CardContent2, { children: /* @__PURE__ */ jsxs10("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
736
- showEmail && /* @__PURE__ */ jsxs10("div", { className: "space-y-2", children: [
737
- /* @__PURE__ */ jsx11(Label, { htmlFor: "email", children: t("auth.common.email") }),
738
- /* @__PURE__ */ jsx11(
739
- Input,
740
- {
741
- id: "email",
742
- type: "email",
743
- required: true,
744
- value: email,
745
- onChange: (e) => setEmail(e.target.value),
746
- autoComplete: "email"
747
- }
748
- )
749
- ] }),
750
- showCode && /* @__PURE__ */ jsxs10("div", { className: "space-y-2", children: [
751
- /* @__PURE__ */ jsx11(Label, { htmlFor: "code", children: t("auth.common.code") }),
752
- /* @__PURE__ */ jsx11(
753
- Input,
754
- {
755
- id: "code",
756
- required: true,
757
- value: code,
758
- onChange: (e) => setCode(e.target.value),
759
- autoComplete: "one-time-code"
760
- }
761
- )
762
- ] }),
763
- showPassword && /* @__PURE__ */ jsxs10("div", { className: "space-y-2", children: [
764
- /* @__PURE__ */ jsx11(Label, { htmlFor: "password", children: mode === "reset" ? t("auth.common.newPassword") : t("auth.common.password") }),
765
- /* @__PURE__ */ jsx11(
766
- Input,
767
- {
768
- id: "password",
769
- type: "password",
770
- required: true,
771
- minLength: 8,
772
- value: password,
773
- onChange: (e) => setPassword(e.target.value),
774
- autoComplete: mode === "signIn" ? "current-password" : "new-password"
775
- }
776
- ),
777
- (mode === "signUp" || mode === "reset") && /* @__PURE__ */ jsx11("p", { className: "text-xs text-muted-foreground", children: t("auth.common.passwordHint") })
778
- ] }),
779
- info && /* @__PURE__ */ jsx11("p", { className: "text-sm text-muted-foreground", children: info }),
780
- error && /* @__PURE__ */ jsx11("p", { className: "text-sm text-destructive", children: error }),
781
- /* @__PURE__ */ jsx11(Button4, { type: "submit", className: "w-full", disabled: loading, children: loading ? t("auth.common.working") : t(`auth.${mode}.submit`) }),
782
- /* @__PURE__ */ jsxs10("div", { className: "space-y-1 text-center text-sm", children: [
783
- mode === "signIn" && /* @__PURE__ */ jsxs10(Fragment2, { children: [
784
- /* @__PURE__ */ jsx11("p", { children: /* @__PURE__ */ jsx11(
785
- "button",
786
- {
787
- type: "button",
788
- className: "text-primary hover:underline",
789
- onClick: () => go("forgot"),
790
- children: t("auth.signIn.forgotPassword")
791
- }
792
- ) }),
793
- /* @__PURE__ */ jsx11("p", { children: /* @__PURE__ */ jsx11(
794
- "button",
795
- {
796
- type: "button",
797
- className: "text-primary hover:underline",
798
- onClick: () => go("signUp"),
799
- children: t("auth.signIn.createAccount")
800
- }
801
- ) })
802
- ] }),
803
- (mode === "signUp" || mode === "forgot" || mode === "reset") && /* @__PURE__ */ jsx11("p", { children: /* @__PURE__ */ jsx11(
804
- "button",
805
- {
806
- type: "button",
807
- className: "text-primary hover:underline",
808
- onClick: () => go("signIn"),
809
- children: t("auth.signUp.backToSignIn")
810
- }
811
- ) }),
812
- mode === "reset" && /* @__PURE__ */ jsx11("p", { children: /* @__PURE__ */ jsx11(
813
- "button",
814
- {
815
- type: "button",
816
- className: "text-primary hover:underline",
817
- onClick: () => go("forgot"),
818
- children: t("auth.reset.resendCode")
819
- }
820
- ) })
821
- ] })
822
- ] }) })
823
- ] }) });
276
+ return UsersPage;
824
277
  }
278
+
279
+ // src/pages/login.tsx
825
280
  function createLoginPage(_admin) {
826
281
  return LoginPage;
827
282
  }
@@ -835,5 +290,6 @@ export {
835
290
  createPostsListPage,
836
291
  createSiteEditPage,
837
292
  createSiteThemePage,
838
- createSitesListPage
293
+ createSitesListPage,
294
+ createUsersListPage
839
295
  };