convex-cms 0.0.9-alpha.3 → 0.0.9-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin/src/embed/index.tsx +57 -67
- package/admin/src/embed/pages/Entry.tsx +4 -5
- package/admin/src/embed/pages/NewEntry.tsx +2 -1
- package/admin/src/embed/types.ts +0 -1
- package/admin-dist/nitro.json +1 -1
- package/admin-dist/public/assets/{CmsEmptyState-BM4e6N83.js → CmsEmptyState-la_kLGHv.js} +1 -1
- package/admin-dist/public/assets/{CmsPageHeader-uor3DPIk.js → CmsPageHeader-Cf0SafVG.js} +1 -1
- package/admin-dist/public/assets/{CmsStatusBadge-D8N18LJx.js → CmsStatusBadge-BovugIHH.js} +1 -1
- package/admin-dist/public/assets/{CmsSurface-BEcY-WpI.js → CmsSurface-Dp1TegU4.js} +1 -1
- package/admin-dist/public/assets/{CmsToolbar-DE-bu3W8.js → CmsToolbar-B1zdfMu0.js} +1 -1
- package/admin-dist/public/assets/{ContentEntryEditor-BdkIMCUk.js → ContentEntryEditor-CB--8SC3.js} +1 -1
- package/admin-dist/public/assets/{TaxonomyFilter-a1-O9DPs.js → TaxonomyFilter-fmZxxbdA.js} +1 -1
- package/admin-dist/public/assets/{_contentTypeId-XIkYOLyY.js → _contentTypeId-vT09P77i.js} +1 -1
- package/admin-dist/public/assets/{_entryId-DyP15QpI.js → _entryId-Dhq6ybYt.js} +1 -1
- package/admin-dist/public/assets/{alert-DHBQuuib.js → alert-DfdkD-ZZ.js} +1 -1
- package/admin-dist/public/assets/{badge-BOhWFWzb.js → badge-Cmc3T9vs.js} +1 -1
- package/admin-dist/public/assets/{circle-check-big-DjTNapen.js → circle-check-big-B8AJAcBi.js} +1 -1
- package/admin-dist/public/assets/{command-BIc5_8gL.js → command-BsmkQ6_j.js} +1 -1
- package/admin-dist/public/assets/{content-C3N8Ugra.js → content-DdQ1u7T4.js} +1 -1
- package/admin-dist/public/assets/{content-types-D0wh1eUF.js → content-types-QYcwm0Sy.js} +1 -1
- package/admin-dist/public/assets/{index-B-g3F_ri.js → index-kxLB3e43.js} +1 -1
- package/admin-dist/public/assets/main-BrFRroF1.js +107 -0
- package/admin-dist/public/assets/{media-8uh1MwDi.js → media-Cx9IyZvU.js} +1 -1
- package/admin-dist/public/assets/{new._contentTypeId-S96rFbgY.js → new._contentTypeId-2HsDwzy_.js} +1 -1
- package/admin-dist/public/assets/{pencil-DgaZav4e.js → pencil-PaJhpDeC.js} +1 -1
- package/admin-dist/public/assets/{refresh-cw-BBut4hAU.js → refresh-cw-D_KNzBUN.js} +1 -1
- package/admin-dist/public/assets/{rotate-ccw-DVCkojZZ.js → rotate-ccw-I3wzW1RQ.js} +1 -1
- package/admin-dist/public/assets/{scroll-area-DPC4uXzf.js → scroll-area-t72-FBB4.js} +1 -1
- package/admin-dist/public/assets/{search-CSyHHglh.js → search-BsuImjd-.js} +1 -1
- package/admin-dist/public/assets/{settings-cEqPsoJ0.js → settings-DB6TvceQ.js} +1 -1
- package/admin-dist/public/assets/{switch-O2BviO8Q.js → switch-DWN_fx2n.js} +1 -1
- package/admin-dist/public/assets/{tabs-p1MWhOqY.js → tabs-BG0vukFH.js} +1 -1
- package/admin-dist/public/assets/{tanstack-adapter-CDrxoPZD.js → tanstack-adapter-DHsy8Fjs.js} +1 -1
- package/admin-dist/public/assets/{taxonomies-DJ9UbjXW.js → taxonomies-WG8YV2pR.js} +1 -1
- package/admin-dist/public/assets/{trash-RnpP6lXF.js → trash-C3Lt5m9d.js} +1 -1
- package/admin-dist/public/assets/{useBreadcrumbLabel-zbIWXlkc.js → useBreadcrumbLabel-BC8wl3jQ.js} +1 -1
- package/admin-dist/public/assets/{usePermissions-4CTlK-vU.js → usePermissions-BM1Vv1YJ.js} +1 -1
- package/admin-dist/server/_libs/convex-helpers.mjs +37 -0
- package/admin-dist/server/_libs/convex.mjs +1357 -168
- package/admin-dist/server/_ssr/{CmsEmptyState-BA0Lc5xs.mjs → CmsEmptyState-NKmyUWD9.mjs} +1 -1
- package/admin-dist/server/_ssr/{CmsPageHeader-PMyecILZ.mjs → CmsPageHeader-CngxPIOg.mjs} +1 -1
- package/admin-dist/server/_ssr/{CmsStatusBadge-CInuN2bZ.mjs → CmsStatusBadge-D4fiHjJD.mjs} +2 -2
- package/admin-dist/server/_ssr/{CmsSurface-CH1PIfcS.mjs → CmsSurface-DN9I2iuX.mjs} +1 -1
- package/admin-dist/server/_ssr/{CmsToolbar-IuhSA7gR.mjs → CmsToolbar-ux-veU96.mjs} +1 -1
- package/admin-dist/server/_ssr/{ContentEntryEditor-Bzhir4fQ.mjs → ContentEntryEditor-BiY9bJr9.mjs} +9 -9
- package/admin-dist/server/_ssr/{TaxonomyFilter-r4izSMBh.mjs → TaxonomyFilter-BdtKJie2.mjs} +3 -3
- package/admin-dist/server/_ssr/{_contentTypeId-BWEbjqxY.mjs → _contentTypeId-CRo5WQVu.mjs} +12 -11
- package/admin-dist/server/_ssr/{_entryId-B5xoXoJf.mjs → _entryId-FnG3uc_Y.mjs} +12 -11
- package/admin-dist/server/_ssr/_tanstack-start-manifest_v-BbSNqRIw.mjs +4 -0
- package/admin-dist/server/_ssr/{badge-DXrjBRqZ.mjs → badge-D3SGS0Jp.mjs} +1 -1
- package/admin-dist/server/_ssr/{command-Cj90OdCX.mjs → command-2t7uTBKt.mjs} +1 -1
- package/admin-dist/server/_ssr/{content-DKRI-YqL.mjs → content-CCsSzXeb.mjs} +10 -9
- package/admin-dist/server/_ssr/{content-types-BzgRcS8K.mjs → content-types-CuVG3uSt.mjs} +7 -6
- package/admin-dist/server/_ssr/{index-BPf6_agY.mjs → index-CLwCLoco.mjs} +5 -4
- package/admin-dist/server/_ssr/index.mjs +2 -2
- package/admin-dist/server/_ssr/{media-MpjxOZL8.mjs → media-D3duVWkk.mjs} +11 -10
- package/admin-dist/server/_ssr/{new._contentTypeId-DSb4qR9j.mjs → new._contentTypeId-6uKYdgGO.mjs} +11 -10
- package/admin-dist/server/_ssr/router-D9Zk56-q.mjs +7979 -0
- package/admin-dist/server/_ssr/{scroll-area-JwVD_6MZ.mjs → scroll-area-vbjKsfFu.mjs} +1 -1
- package/admin-dist/server/_ssr/{settings-KVJNe0GM.mjs → settings-D7-vwjqD.mjs} +9 -8
- package/admin-dist/server/_ssr/{switch-DvREvRv4.mjs → switch-CbKuV4Qh.mjs} +1 -1
- package/admin-dist/server/_ssr/{tabs-B0h57pFf.mjs → tabs-C_IfqLiu.mjs} +2 -2
- package/admin-dist/server/_ssr/{tanstack-adapter-gmM64LnW.mjs → tanstack-adapter-yhyAcBi-.mjs} +1 -1
- package/admin-dist/server/_ssr/{taxonomies-BbBNx260.mjs → taxonomies-C15s_nvM.mjs} +9 -8
- package/admin-dist/server/_ssr/{trash-JAzYGh7A.mjs → trash-BPIjmAUh.mjs} +9 -8
- package/admin-dist/server/_ssr/{useBreadcrumbLabel-BWIujj97.mjs → useBreadcrumbLabel-BC7plG0L.mjs} +1 -1
- package/admin-dist/server/_ssr/{usePermissions-CcLDCSwa.mjs → usePermissions-DJ8a7bZU.mjs} +1 -1
- package/admin-dist/server/index.mjs +157 -157
- package/dist/client/index.d.ts +0 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +6 -15
- package/dist/client/index.js.map +1 -1
- package/package.json +6 -1
- package/admin-dist/public/assets/main-BapBJgQD.js +0 -102
- package/admin-dist/server/_ssr/_tanstack-start-manifest_v-CBTan6ii.mjs +0 -4
- package/admin-dist/server/_ssr/router-Dk9ikPNc.mjs +0 -3055
|
@@ -1,3055 +0,0 @@
|
|
|
1
|
-
import { c as createRouter, a as createRootRoute, b as createFileRoute, l as lazyRouteComponent, O as Outlet, H as HeadContent, S as Scripts, u as useRouterState, L as Link, d as useNavigate } from "../_chunks/_libs/@tanstack/react-router.mjs";
|
|
2
|
-
import { r as reactExports, j as jsxRuntimeExports } from "../_chunks/_libs/react.mjs";
|
|
3
|
-
import { c as clsx } from "../_libs/clsx.mjs";
|
|
4
|
-
import { t as twMerge } from "../_libs/tailwind-merge.mjs";
|
|
5
|
-
import { R as Root$1, C as CollapsibleTrigger$1, a as CollapsibleContent$1 } from "../_chunks/_libs/@radix-ui/react-collapsible.mjs";
|
|
6
|
-
import { R as Root$3, C as Content, a as Close, T as Title, D as Description, P as Portal$2, O as Overlay } from "../_chunks/_libs/@radix-ui/react-dialog.mjs";
|
|
7
|
-
import { S as Slot } from "../_chunks/_libs/@radix-ui/react-slot.mjs";
|
|
8
|
-
import { c as cva } from "../_libs/class-variance-authority.mjs";
|
|
9
|
-
import { R as Root$2 } from "../_chunks/_libs/@radix-ui/react-label.mjs";
|
|
10
|
-
import { C as Checkbox$1, a as CheckboxIndicator } from "../_chunks/_libs/@radix-ui/react-checkbox.mjs";
|
|
11
|
-
import { R as Root2$2, T as Trigger$2, I as Icon$1, V as Value, P as Portal$1, C as Content2$2, a as Viewport, b as Item, c as ItemIndicator, d as ItemText, S as ScrollUpButton, e as ScrollDownButton } from "../_chunks/_libs/@radix-ui/react-select.mjs";
|
|
12
|
-
import { R as Root2$1, T as Trigger$1, P as Portal2, C as Content2$1, L as Label2, S as Separator2, I as Item2 } from "../_chunks/_libs/@radix-ui/react-dropdown-menu.mjs";
|
|
13
|
-
import { R as Root, I as Image, F as Fallback } from "../_chunks/_libs/@radix-ui/react-avatar.mjs";
|
|
14
|
-
import { R as Root2, T as Trigger, P as Portal, C as Content2 } from "../_chunks/_libs/@radix-ui/react-popover.mjs";
|
|
15
|
-
import { c as createServerFn, T as TSS_SERVER_FUNCTION, g as getServerFnById } from "./index.mjs";
|
|
16
|
-
import { v, c as componentsGeneric, C as ConvexReactClient, a as ConvexProvider, b as anyApi, u as useQuery, d as useMutation } from "../_libs/convex.mjs";
|
|
17
|
-
import { L as LoaderCircle, a as Layers, H as House, B as Bell, C as CircleQuestionMark, b as Book, c as Code, M as MessageSquare, E as ExternalLink, d as ChevronDown, U as User, e as LogOut, f as CodeXml, P as Plus, g as ChevronUp, X, h as ChevronRight, i as Check, j as CircleAlert, k as Eye, S as SquarePen, A as Archive, l as Bookmark, F as Flag, m as Heart, n as Star, o as Package, p as Phone, q as Mail, D as DollarSign, r as MapPin, s as Folder, t as Braces, u as SquareCheckBig, T as ToggleLeft, v as Hash, w as Link$1, x as Clock, y as Calendar, G as Globe, z as Lock, I as Trash2, J as Settings, K as FileCode, N as Tags, O as Image$1, Q as FileText, R as LayoutDashboard, V as FolderOpen, W as Tag, Y as List, Z as Link2, _ as FileType, $ as TextAlignStart, a0 as TriangleAlert } from "../_libs/lucide-react.mjs";
|
|
18
|
-
import { o as object, b as boolean, s as string, _ as _enum, n as number, a as array, r as record } from "../_libs/zod.mjs";
|
|
19
|
-
import "../_libs/tiny-warning.mjs";
|
|
20
|
-
import "../_chunks/_libs/@tanstack/router-core.mjs";
|
|
21
|
-
import "../_chunks/_libs/@tanstack/history.mjs";
|
|
22
|
-
import "../_libs/tiny-invariant.mjs";
|
|
23
|
-
import "node:stream/web";
|
|
24
|
-
import "node:stream";
|
|
25
|
-
import "../_chunks/_libs/react-dom.mjs";
|
|
26
|
-
import "util";
|
|
27
|
-
import "crypto";
|
|
28
|
-
import "async_hooks";
|
|
29
|
-
import "stream";
|
|
30
|
-
import "../_libs/isbot.mjs";
|
|
31
|
-
import "../_chunks/_libs/@radix-ui/primitive.mjs";
|
|
32
|
-
import "../_chunks/_libs/@radix-ui/react-context.mjs";
|
|
33
|
-
import "../_chunks/_libs/@radix-ui/react-use-controllable-state.mjs";
|
|
34
|
-
import "../_chunks/_libs/@radix-ui/react-use-layout-effect.mjs";
|
|
35
|
-
import "../_chunks/_libs/@radix-ui/react-compose-refs.mjs";
|
|
36
|
-
import "../_chunks/_libs/@radix-ui/react-primitive.mjs";
|
|
37
|
-
import "../_chunks/_libs/@radix-ui/react-presence.mjs";
|
|
38
|
-
import "../_chunks/_libs/@radix-ui/react-id.mjs";
|
|
39
|
-
import "../_chunks/_libs/@radix-ui/react-dismissable-layer.mjs";
|
|
40
|
-
import "../_chunks/_libs/@radix-ui/react-use-callback-ref.mjs";
|
|
41
|
-
import "../_chunks/_libs/@radix-ui/react-use-escape-keydown.mjs";
|
|
42
|
-
import "../_chunks/_libs/@radix-ui/react-focus-scope.mjs";
|
|
43
|
-
import "../_chunks/_libs/@radix-ui/react-portal.mjs";
|
|
44
|
-
import "../_chunks/_libs/@radix-ui/react-focus-guards.mjs";
|
|
45
|
-
import "../_libs/react-remove-scroll.mjs";
|
|
46
|
-
import "../_libs/tslib.mjs";
|
|
47
|
-
import "../_libs/react-remove-scroll-bar.mjs";
|
|
48
|
-
import "../_libs/react-style-singleton.mjs";
|
|
49
|
-
import "../_libs/get-nonce.mjs";
|
|
50
|
-
import "../_libs/use-sidecar.mjs";
|
|
51
|
-
import "../_libs/use-callback-ref.mjs";
|
|
52
|
-
import "../_libs/aria-hidden.mjs";
|
|
53
|
-
import "../_chunks/_libs/@radix-ui/react-use-previous.mjs";
|
|
54
|
-
import "../_chunks/_libs/@radix-ui/react-use-size.mjs";
|
|
55
|
-
import "../_chunks/_libs/@radix-ui/number.mjs";
|
|
56
|
-
import "../_chunks/_libs/@radix-ui/react-collection.mjs";
|
|
57
|
-
import "../_chunks/_libs/@radix-ui/react-direction.mjs";
|
|
58
|
-
import "../_chunks/_libs/@radix-ui/react-popper.mjs";
|
|
59
|
-
import "../_chunks/_libs/@floating-ui/react-dom.mjs";
|
|
60
|
-
import "../_chunks/_libs/@floating-ui/dom.mjs";
|
|
61
|
-
import "../_chunks/_libs/@floating-ui/core.mjs";
|
|
62
|
-
import "../_chunks/_libs/@floating-ui/utils.mjs";
|
|
63
|
-
import "../_chunks/_libs/@radix-ui/react-arrow.mjs";
|
|
64
|
-
import "../_chunks/_libs/@radix-ui/react-visually-hidden.mjs";
|
|
65
|
-
import "../_chunks/_libs/@radix-ui/react-menu.mjs";
|
|
66
|
-
import "../_chunks/_libs/@radix-ui/react-roving-focus.mjs";
|
|
67
|
-
import "../_chunks/_libs/@radix-ui/react-use-is-hydrated.mjs";
|
|
68
|
-
import "../_libs/use-sync-external-store.mjs";
|
|
69
|
-
import "node:async_hooks";
|
|
70
|
-
const globalsCss = "/assets/globals-CoCRjt0K.css";
|
|
71
|
-
function cn(...inputs) {
|
|
72
|
-
return twMerge(clsx(inputs));
|
|
73
|
-
}
|
|
74
|
-
v.union(
|
|
75
|
-
v.literal("admin"),
|
|
76
|
-
v.literal("editor"),
|
|
77
|
-
v.literal("author"),
|
|
78
|
-
v.literal("viewer")
|
|
79
|
-
);
|
|
80
|
-
const resourceValidator = v.union(
|
|
81
|
-
v.literal("contentTypes"),
|
|
82
|
-
v.literal("contentEntries"),
|
|
83
|
-
v.literal("mediaItems"),
|
|
84
|
-
v.literal("settings")
|
|
85
|
-
);
|
|
86
|
-
const actionValidator = v.union(
|
|
87
|
-
v.literal("create"),
|
|
88
|
-
v.literal("read"),
|
|
89
|
-
v.literal("update"),
|
|
90
|
-
v.literal("delete"),
|
|
91
|
-
v.literal("publish"),
|
|
92
|
-
v.literal("unpublish"),
|
|
93
|
-
v.literal("restore"),
|
|
94
|
-
v.literal("manage"),
|
|
95
|
-
v.literal("move")
|
|
96
|
-
);
|
|
97
|
-
v.object({
|
|
98
|
-
resource: resourceValidator,
|
|
99
|
-
action: actionValidator,
|
|
100
|
-
scope: v.optional(v.union(v.literal("all"), v.literal("own")))
|
|
101
|
-
});
|
|
102
|
-
function fullCrud(resource, scope = "all") {
|
|
103
|
-
return [
|
|
104
|
-
{ resource, action: "create", scope },
|
|
105
|
-
{ resource, action: "read", scope },
|
|
106
|
-
{ resource, action: "update", scope },
|
|
107
|
-
{ resource, action: "delete", scope }
|
|
108
|
-
];
|
|
109
|
-
}
|
|
110
|
-
function readOnly(resource, scope = "all") {
|
|
111
|
-
return [{ resource, action: "read", scope }];
|
|
112
|
-
}
|
|
113
|
-
function publishPermissions(scope = "all") {
|
|
114
|
-
return [
|
|
115
|
-
{ resource: "contentEntries", action: "publish", scope },
|
|
116
|
-
{ resource: "contentEntries", action: "unpublish", scope }
|
|
117
|
-
];
|
|
118
|
-
}
|
|
119
|
-
const ADMIN_ROLE = {
|
|
120
|
-
name: "admin",
|
|
121
|
-
displayName: "Administrator",
|
|
122
|
-
description: "Full access to all CMS features including settings and content type management",
|
|
123
|
-
isSystem: true,
|
|
124
|
-
permissions: [
|
|
125
|
-
// Content types - full management
|
|
126
|
-
...fullCrud("contentTypes"),
|
|
127
|
-
// Content entries - full CRUD + publish
|
|
128
|
-
...fullCrud("contentEntries"),
|
|
129
|
-
...publishPermissions(),
|
|
130
|
-
{ resource: "contentEntries", action: "restore" },
|
|
131
|
-
// Media - full management
|
|
132
|
-
...fullCrud("mediaItems"),
|
|
133
|
-
// Settings - full access
|
|
134
|
-
{ resource: "settings", action: "manage" },
|
|
135
|
-
...readOnly("settings")
|
|
136
|
-
]
|
|
137
|
-
};
|
|
138
|
-
const EDITOR_ROLE = {
|
|
139
|
-
name: "editor",
|
|
140
|
-
displayName: "Editor",
|
|
141
|
-
description: "Can manage all content and media, but cannot modify settings or content types",
|
|
142
|
-
isSystem: true,
|
|
143
|
-
permissions: [
|
|
144
|
-
// Content types - read only
|
|
145
|
-
...readOnly("contentTypes"),
|
|
146
|
-
// Content entries - full CRUD + publish
|
|
147
|
-
...fullCrud("contentEntries"),
|
|
148
|
-
...publishPermissions(),
|
|
149
|
-
{ resource: "contentEntries", action: "restore" },
|
|
150
|
-
// Media - full management
|
|
151
|
-
...fullCrud("mediaItems")
|
|
152
|
-
]
|
|
153
|
-
};
|
|
154
|
-
const AUTHOR_ROLE = {
|
|
155
|
-
name: "author",
|
|
156
|
-
displayName: "Author",
|
|
157
|
-
description: "Can create and manage own content and media",
|
|
158
|
-
isSystem: true,
|
|
159
|
-
permissions: [
|
|
160
|
-
// Content types - read only
|
|
161
|
-
...readOnly("contentTypes"),
|
|
162
|
-
// Content entries - own content only
|
|
163
|
-
{ resource: "contentEntries", action: "create" },
|
|
164
|
-
{ resource: "contentEntries", action: "read", scope: "own" },
|
|
165
|
-
{ resource: "contentEntries", action: "update", scope: "own" },
|
|
166
|
-
{ resource: "contentEntries", action: "delete", scope: "own" },
|
|
167
|
-
// Authors can publish/unpublish their own content
|
|
168
|
-
{ resource: "contentEntries", action: "publish", scope: "own" },
|
|
169
|
-
{ resource: "contentEntries", action: "unpublish", scope: "own" },
|
|
170
|
-
// Media - can create and manage own, read all (for embedding)
|
|
171
|
-
{ resource: "mediaItems", action: "create" },
|
|
172
|
-
{ resource: "mediaItems", action: "read", scope: "all" },
|
|
173
|
-
// Can read all for embedding
|
|
174
|
-
{ resource: "mediaItems", action: "update", scope: "own" },
|
|
175
|
-
{ resource: "mediaItems", action: "delete", scope: "own" }
|
|
176
|
-
]
|
|
177
|
-
};
|
|
178
|
-
const VIEWER_ROLE = {
|
|
179
|
-
name: "viewer",
|
|
180
|
-
displayName: "Viewer",
|
|
181
|
-
description: "Read-only access to published content and media",
|
|
182
|
-
isSystem: true,
|
|
183
|
-
permissions: [
|
|
184
|
-
// Content types - read only
|
|
185
|
-
...readOnly("contentTypes"),
|
|
186
|
-
// Content entries - read published only (scope: "all" means all published)
|
|
187
|
-
...readOnly("contentEntries"),
|
|
188
|
-
// Media - read only
|
|
189
|
-
...readOnly("mediaItems")
|
|
190
|
-
]
|
|
191
|
-
};
|
|
192
|
-
const DEFAULT_ROLES = {
|
|
193
|
-
admin: ADMIN_ROLE,
|
|
194
|
-
editor: EDITOR_ROLE,
|
|
195
|
-
author: AUTHOR_ROLE,
|
|
196
|
-
viewer: VIEWER_ROLE
|
|
197
|
-
};
|
|
198
|
-
function permissionMatches(granted, requested) {
|
|
199
|
-
if (granted.resource !== requested.resource || granted.action !== requested.action) {
|
|
200
|
-
return false;
|
|
201
|
-
}
|
|
202
|
-
const grantedScope = granted.scope ?? "all";
|
|
203
|
-
const requestedScope = requested.scope ?? "all";
|
|
204
|
-
if (grantedScope === "all") {
|
|
205
|
-
return true;
|
|
206
|
-
}
|
|
207
|
-
return requestedScope === "own";
|
|
208
|
-
}
|
|
209
|
-
function hasPermission(roleName, permission, customRoles) {
|
|
210
|
-
const role = DEFAULT_ROLES[roleName] ?? customRoles?.[roleName];
|
|
211
|
-
if (!role) {
|
|
212
|
-
return false;
|
|
213
|
-
}
|
|
214
|
-
return role.permissions.some((p) => permissionMatches(p, permission));
|
|
215
|
-
}
|
|
216
|
-
function getRolePermissions(roleName, customRoles) {
|
|
217
|
-
const role = DEFAULT_ROLES[roleName] ?? customRoles?.[roleName];
|
|
218
|
-
return role?.permissions ?? [];
|
|
219
|
-
}
|
|
220
|
-
function getRole(roleName, customRoles) {
|
|
221
|
-
return DEFAULT_ROLES[roleName] ?? customRoles?.[roleName];
|
|
222
|
-
}
|
|
223
|
-
function getResourcePermissions(roleName, resource, customRoles) {
|
|
224
|
-
return getRolePermissions(roleName, customRoles).filter(
|
|
225
|
-
(p) => p.resource === resource
|
|
226
|
-
);
|
|
227
|
-
}
|
|
228
|
-
function canAccessResource(roleName, resource, customRoles) {
|
|
229
|
-
return getResourcePermissions(roleName, resource, customRoles).length > 0;
|
|
230
|
-
}
|
|
231
|
-
const AuthContext = reactExports.createContext(null);
|
|
232
|
-
function AuthProvider({
|
|
233
|
-
children,
|
|
234
|
-
getUser,
|
|
235
|
-
getUserRole,
|
|
236
|
-
onLogout,
|
|
237
|
-
autoRedirectToLogin = false,
|
|
238
|
-
loginUrl = "/login"
|
|
239
|
-
}) {
|
|
240
|
-
const [user, setUser] = reactExports.useState(null);
|
|
241
|
-
const [role, setRole] = reactExports.useState(null);
|
|
242
|
-
const [authState, setAuthState] = reactExports.useState("loading");
|
|
243
|
-
const [error, setError] = reactExports.useState(null);
|
|
244
|
-
const loadAuth = reactExports.useCallback(async () => {
|
|
245
|
-
setAuthState("loading");
|
|
246
|
-
setError(null);
|
|
247
|
-
try {
|
|
248
|
-
const currentUser = await getUser();
|
|
249
|
-
if (!currentUser) {
|
|
250
|
-
setUser(null);
|
|
251
|
-
setRole(null);
|
|
252
|
-
setAuthState("unauthenticated");
|
|
253
|
-
if (autoRedirectToLogin && typeof window !== "undefined") {
|
|
254
|
-
window.location.href = loginUrl;
|
|
255
|
-
}
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
const userRole = await getUserRole({ userId: currentUser.id });
|
|
259
|
-
setUser(currentUser);
|
|
260
|
-
setRole(userRole);
|
|
261
|
-
setAuthState("authenticated");
|
|
262
|
-
} catch (err) {
|
|
263
|
-
console.error("Authentication error:", err);
|
|
264
|
-
setUser(null);
|
|
265
|
-
setRole(null);
|
|
266
|
-
setError(err instanceof Error ? err.message : "Authentication failed");
|
|
267
|
-
setAuthState("error");
|
|
268
|
-
}
|
|
269
|
-
}, [getUser, getUserRole, autoRedirectToLogin, loginUrl]);
|
|
270
|
-
reactExports.useEffect(() => {
|
|
271
|
-
loadAuth();
|
|
272
|
-
}, [loadAuth]);
|
|
273
|
-
const checkPermission = reactExports.useCallback(
|
|
274
|
-
(permission) => {
|
|
275
|
-
if (!role) {
|
|
276
|
-
return false;
|
|
277
|
-
}
|
|
278
|
-
return hasPermission(role, permission);
|
|
279
|
-
},
|
|
280
|
-
[role]
|
|
281
|
-
);
|
|
282
|
-
const logout = reactExports.useCallback(async () => {
|
|
283
|
-
try {
|
|
284
|
-
if (onLogout) {
|
|
285
|
-
await onLogout();
|
|
286
|
-
}
|
|
287
|
-
setUser(null);
|
|
288
|
-
setRole(null);
|
|
289
|
-
setAuthState("unauthenticated");
|
|
290
|
-
if (autoRedirectToLogin && typeof window !== "undefined") {
|
|
291
|
-
window.location.href = loginUrl;
|
|
292
|
-
}
|
|
293
|
-
} catch (err) {
|
|
294
|
-
console.error("Logout error:", err);
|
|
295
|
-
setError(err instanceof Error ? err.message : "Logout failed");
|
|
296
|
-
}
|
|
297
|
-
}, [onLogout, autoRedirectToLogin, loginUrl]);
|
|
298
|
-
const refresh = reactExports.useCallback(async () => {
|
|
299
|
-
await loadAuth();
|
|
300
|
-
}, [loadAuth]);
|
|
301
|
-
const value = {
|
|
302
|
-
user,
|
|
303
|
-
role,
|
|
304
|
-
authState,
|
|
305
|
-
isLoading: authState === "loading",
|
|
306
|
-
isAuthenticated: authState === "authenticated",
|
|
307
|
-
error,
|
|
308
|
-
checkPermission,
|
|
309
|
-
logout,
|
|
310
|
-
refresh
|
|
311
|
-
};
|
|
312
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(AuthContext.Provider, { value, children });
|
|
313
|
-
}
|
|
314
|
-
function useAuth() {
|
|
315
|
-
const context = reactExports.useContext(AuthContext);
|
|
316
|
-
if (!context) {
|
|
317
|
-
throw new Error("useAuth must be used within an AuthProvider");
|
|
318
|
-
}
|
|
319
|
-
return context;
|
|
320
|
-
}
|
|
321
|
-
const ThemeContext = reactExports.createContext(null);
|
|
322
|
-
const STORAGE_KEY = "convex-cms-theme";
|
|
323
|
-
function getSystemTheme() {
|
|
324
|
-
if (typeof window === "undefined") return "light";
|
|
325
|
-
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
326
|
-
}
|
|
327
|
-
function getStoredTheme() {
|
|
328
|
-
if (typeof window === "undefined") return "system";
|
|
329
|
-
const stored = localStorage.getItem(STORAGE_KEY);
|
|
330
|
-
if (stored === "light" || stored === "dark" || stored === "system") {
|
|
331
|
-
return stored;
|
|
332
|
-
}
|
|
333
|
-
return "system";
|
|
334
|
-
}
|
|
335
|
-
function ThemeProvider({ children }) {
|
|
336
|
-
const [theme, setThemeState] = reactExports.useState(() => getStoredTheme());
|
|
337
|
-
const [resolvedTheme, setResolvedTheme] = reactExports.useState(() => {
|
|
338
|
-
const stored = getStoredTheme();
|
|
339
|
-
return stored === "system" ? getSystemTheme() : stored;
|
|
340
|
-
});
|
|
341
|
-
const applyTheme = reactExports.useCallback((newTheme) => {
|
|
342
|
-
const resolved = newTheme === "system" ? getSystemTheme() : newTheme;
|
|
343
|
-
setResolvedTheme(resolved);
|
|
344
|
-
const root = document.documentElement;
|
|
345
|
-
root.classList.remove("light", "dark");
|
|
346
|
-
root.classList.add(resolved);
|
|
347
|
-
}, []);
|
|
348
|
-
const setTheme = reactExports.useCallback(
|
|
349
|
-
(newTheme) => {
|
|
350
|
-
setThemeState(newTheme);
|
|
351
|
-
localStorage.setItem(STORAGE_KEY, newTheme);
|
|
352
|
-
applyTheme(newTheme);
|
|
353
|
-
},
|
|
354
|
-
[applyTheme]
|
|
355
|
-
);
|
|
356
|
-
reactExports.useEffect(() => {
|
|
357
|
-
applyTheme(theme);
|
|
358
|
-
}, [theme, applyTheme]);
|
|
359
|
-
reactExports.useEffect(() => {
|
|
360
|
-
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
361
|
-
const handleChange = () => {
|
|
362
|
-
if (theme === "system") {
|
|
363
|
-
applyTheme("system");
|
|
364
|
-
}
|
|
365
|
-
};
|
|
366
|
-
mediaQuery.addEventListener("change", handleChange);
|
|
367
|
-
return () => mediaQuery.removeEventListener("change", handleChange);
|
|
368
|
-
}, [theme, applyTheme]);
|
|
369
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(ThemeContext.Provider, { value: { theme, resolvedTheme, setTheme }, children });
|
|
370
|
-
}
|
|
371
|
-
function useTheme() {
|
|
372
|
-
const context = reactExports.useContext(ThemeContext);
|
|
373
|
-
if (!context) {
|
|
374
|
-
throw new Error("useTheme must be used within a ThemeProvider");
|
|
375
|
-
}
|
|
376
|
-
return context;
|
|
377
|
-
}
|
|
378
|
-
const navItemSchema = object({
|
|
379
|
-
id: string(),
|
|
380
|
-
path: string(),
|
|
381
|
-
label: string(),
|
|
382
|
-
icon: string(),
|
|
383
|
-
visible: boolean().default(true),
|
|
384
|
-
section: _enum(["main", "config"]).default("main"),
|
|
385
|
-
badge: string().optional(),
|
|
386
|
-
exact: boolean().optional()
|
|
387
|
-
});
|
|
388
|
-
const brandingSchema = object({
|
|
389
|
-
appName: string().default("Convex CMS"),
|
|
390
|
-
logo: string().optional(),
|
|
391
|
-
favicon: string().optional()
|
|
392
|
-
});
|
|
393
|
-
const layoutSchema = object({
|
|
394
|
-
sidebarWidth: number().min(200).max(400).default(256),
|
|
395
|
-
sidebarCollapsible: boolean().default(false)
|
|
396
|
-
});
|
|
397
|
-
const navigationSchema = object({
|
|
398
|
-
showDashboard: boolean().default(true),
|
|
399
|
-
showContent: boolean().default(true),
|
|
400
|
-
showMedia: boolean().default(true),
|
|
401
|
-
showTaxonomies: boolean().default(true),
|
|
402
|
-
showContentTypes: boolean().default(true),
|
|
403
|
-
showTrash: boolean().default(true),
|
|
404
|
-
showSettings: boolean().default(true),
|
|
405
|
-
customItems: array(navItemSchema).default([])
|
|
406
|
-
});
|
|
407
|
-
const themeSchema = object({
|
|
408
|
-
mode: _enum(["light", "dark", "system"]).default("system"),
|
|
409
|
-
allowModeSwitch: boolean().default(true),
|
|
410
|
-
tokens: record(string(), string()).optional()
|
|
411
|
-
});
|
|
412
|
-
const adminConfigSchema = object({
|
|
413
|
-
branding: brandingSchema.default(() => brandingSchema.parse({})),
|
|
414
|
-
layout: layoutSchema.default(() => layoutSchema.parse({})),
|
|
415
|
-
navigation: navigationSchema.default(() => navigationSchema.parse({})),
|
|
416
|
-
theme: themeSchema.default(() => themeSchema.parse({}))
|
|
417
|
-
});
|
|
418
|
-
const DEFAULT_NAV_ITEMS = [
|
|
419
|
-
{
|
|
420
|
-
id: "dashboard",
|
|
421
|
-
path: "/",
|
|
422
|
-
label: "Dashboard",
|
|
423
|
-
icon: "LayoutDashboard",
|
|
424
|
-
section: "main",
|
|
425
|
-
exact: true
|
|
426
|
-
},
|
|
427
|
-
{ id: "content", path: "/content", label: "Content", icon: "FileText", section: "main" },
|
|
428
|
-
{ id: "media", path: "/media", label: "Media", icon: "Image", section: "main" },
|
|
429
|
-
{ id: "taxonomies", path: "/taxonomies", label: "Taxonomies", icon: "Tags", section: "main" },
|
|
430
|
-
{
|
|
431
|
-
id: "content-types",
|
|
432
|
-
path: "/content-types",
|
|
433
|
-
label: "Content Types",
|
|
434
|
-
icon: "Layers",
|
|
435
|
-
section: "config"
|
|
436
|
-
},
|
|
437
|
-
{ id: "trash", path: "/trash", label: "Trash", icon: "Trash2", section: "config" },
|
|
438
|
-
{ id: "settings", path: "/settings", label: "Settings", icon: "Settings", section: "config" }
|
|
439
|
-
];
|
|
440
|
-
function resolveAdminConfig(input) {
|
|
441
|
-
return adminConfigSchema.parse(input ?? {});
|
|
442
|
-
}
|
|
443
|
-
function getVisibleNavItems(config) {
|
|
444
|
-
const visibilityMap = {
|
|
445
|
-
dashboard: config.navigation.showDashboard,
|
|
446
|
-
content: config.navigation.showContent,
|
|
447
|
-
media: config.navigation.showMedia,
|
|
448
|
-
taxonomies: config.navigation.showTaxonomies,
|
|
449
|
-
"content-types": config.navigation.showContentTypes,
|
|
450
|
-
trash: config.navigation.showTrash,
|
|
451
|
-
settings: config.navigation.showSettings
|
|
452
|
-
};
|
|
453
|
-
const filtered = DEFAULT_NAV_ITEMS.filter((item) => visibilityMap[item.id] !== false);
|
|
454
|
-
const allItems = [...filtered, ...config.navigation.customItems.filter((i) => i.visible !== false)];
|
|
455
|
-
return {
|
|
456
|
-
main: allItems.filter((i) => i.section === "main"),
|
|
457
|
-
config: allItems.filter((i) => i.section === "config")
|
|
458
|
-
};
|
|
459
|
-
}
|
|
460
|
-
const AdminConfigContext = reactExports.createContext(null);
|
|
461
|
-
function AdminConfigProvider({
|
|
462
|
-
config,
|
|
463
|
-
children
|
|
464
|
-
}) {
|
|
465
|
-
const navItems = getVisibleNavItems(config);
|
|
466
|
-
const value = { ...config, navItems };
|
|
467
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(AdminConfigContext.Provider, { value, children });
|
|
468
|
-
}
|
|
469
|
-
function useAdminConfig() {
|
|
470
|
-
const config = reactExports.useContext(AdminConfigContext);
|
|
471
|
-
if (!config) {
|
|
472
|
-
throw new Error("useAdminConfig must be used within AdminConfigProvider");
|
|
473
|
-
}
|
|
474
|
-
return config;
|
|
475
|
-
}
|
|
476
|
-
const SettingsConfigContext = reactExports.createContext(null);
|
|
477
|
-
function SettingsConfigProvider({
|
|
478
|
-
baseConfig,
|
|
479
|
-
children,
|
|
480
|
-
api: api2
|
|
481
|
-
}) {
|
|
482
|
-
const settings = useQuery(api2?.getSettings ?? "skip");
|
|
483
|
-
const mergedConfig = reactExports.useMemo(() => {
|
|
484
|
-
if (!settings) return baseConfig;
|
|
485
|
-
return {
|
|
486
|
-
...baseConfig,
|
|
487
|
-
navigation: {
|
|
488
|
-
...baseConfig.navigation,
|
|
489
|
-
showMedia: baseConfig.navigation.showMedia && settings.features.mediaManagement
|
|
490
|
-
}
|
|
491
|
-
};
|
|
492
|
-
}, [baseConfig, settings]);
|
|
493
|
-
const contextValue = reactExports.useMemo(
|
|
494
|
-
() => ({ baseConfig, settings }),
|
|
495
|
-
[baseConfig, settings]
|
|
496
|
-
);
|
|
497
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsConfigContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntimeExports.jsx(AdminConfigProvider, { config: mergedConfig, children }) });
|
|
498
|
-
}
|
|
499
|
-
function useSettingsConfig() {
|
|
500
|
-
const ctx = reactExports.useContext(SettingsConfigContext);
|
|
501
|
-
if (!ctx) {
|
|
502
|
-
throw new Error("useSettingsConfig must be used within SettingsConfigProvider");
|
|
503
|
-
}
|
|
504
|
-
return ctx;
|
|
505
|
-
}
|
|
506
|
-
const BreadcrumbContext = reactExports.createContext(null);
|
|
507
|
-
function BreadcrumbProvider({ children }) {
|
|
508
|
-
const [overrides, setOverrides] = reactExports.useState(/* @__PURE__ */ new Map());
|
|
509
|
-
const setOverride = reactExports.useCallback((path, label) => {
|
|
510
|
-
setOverrides((prev) => {
|
|
511
|
-
const next = new Map(prev);
|
|
512
|
-
next.set(path, label);
|
|
513
|
-
return next;
|
|
514
|
-
});
|
|
515
|
-
}, []);
|
|
516
|
-
const clearOverride = reactExports.useCallback((path) => {
|
|
517
|
-
setOverrides((prev) => {
|
|
518
|
-
const next = new Map(prev);
|
|
519
|
-
next.delete(path);
|
|
520
|
-
return next;
|
|
521
|
-
});
|
|
522
|
-
}, []);
|
|
523
|
-
const value = reactExports.useMemo(
|
|
524
|
-
() => ({ overrides, setOverride, clearOverride }),
|
|
525
|
-
[overrides, setOverride, clearOverride]
|
|
526
|
-
);
|
|
527
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(BreadcrumbContext.Provider, { value, children });
|
|
528
|
-
}
|
|
529
|
-
function useBreadcrumbContext() {
|
|
530
|
-
const context = reactExports.useContext(BreadcrumbContext);
|
|
531
|
-
if (!context) {
|
|
532
|
-
throw new Error("useBreadcrumbContext must be used within a BreadcrumbProvider");
|
|
533
|
-
}
|
|
534
|
-
return context;
|
|
535
|
-
}
|
|
536
|
-
const iconRegistry = {
|
|
537
|
-
LayoutDashboard,
|
|
538
|
-
FileText,
|
|
539
|
-
Image: Image$1,
|
|
540
|
-
Layers,
|
|
541
|
-
Tags,
|
|
542
|
-
FileCode,
|
|
543
|
-
Settings,
|
|
544
|
-
Trash2,
|
|
545
|
-
HelpCircle: CircleQuestionMark,
|
|
546
|
-
Home: House,
|
|
547
|
-
User,
|
|
548
|
-
Bell,
|
|
549
|
-
Lock,
|
|
550
|
-
Globe,
|
|
551
|
-
Calendar,
|
|
552
|
-
Clock,
|
|
553
|
-
Link: Link$1,
|
|
554
|
-
Hash,
|
|
555
|
-
ToggleLeft,
|
|
556
|
-
ChevronDown,
|
|
557
|
-
CheckSquare: SquareCheckBig,
|
|
558
|
-
Braces,
|
|
559
|
-
Folder,
|
|
560
|
-
MapPin,
|
|
561
|
-
DollarSign,
|
|
562
|
-
Mail,
|
|
563
|
-
Phone,
|
|
564
|
-
Package,
|
|
565
|
-
Star,
|
|
566
|
-
Heart,
|
|
567
|
-
Flag,
|
|
568
|
-
Bookmark,
|
|
569
|
-
Archive,
|
|
570
|
-
Edit: SquarePen,
|
|
571
|
-
Eye,
|
|
572
|
-
AlertCircle: CircleAlert
|
|
573
|
-
};
|
|
574
|
-
function Icon({
|
|
575
|
-
name,
|
|
576
|
-
className = "size-5"
|
|
577
|
-
}) {
|
|
578
|
-
const IconComponent = iconRegistry[name];
|
|
579
|
-
if (!IconComponent) {
|
|
580
|
-
return null;
|
|
581
|
-
}
|
|
582
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(IconComponent, { className });
|
|
583
|
-
}
|
|
584
|
-
const ApiContext = reactExports.createContext(null);
|
|
585
|
-
function ApiProvider({
|
|
586
|
-
api: api2,
|
|
587
|
-
children
|
|
588
|
-
}) {
|
|
589
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(ApiContext.Provider, { value: api2, children });
|
|
590
|
-
}
|
|
591
|
-
function useApi() {
|
|
592
|
-
const api2 = reactExports.useContext(ApiContext);
|
|
593
|
-
if (!api2) {
|
|
594
|
-
throw new Error("useApi must be used within ApiProvider");
|
|
595
|
-
}
|
|
596
|
-
return api2;
|
|
597
|
-
}
|
|
598
|
-
const Collapsible = Root$1;
|
|
599
|
-
const CollapsibleTrigger = CollapsibleTrigger$1;
|
|
600
|
-
const CollapsibleContent = CollapsibleContent$1;
|
|
601
|
-
function Dialog({
|
|
602
|
-
...props
|
|
603
|
-
}) {
|
|
604
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(Root$3, { "data-slot": "dialog", ...props });
|
|
605
|
-
}
|
|
606
|
-
function DialogPortal({
|
|
607
|
-
...props
|
|
608
|
-
}) {
|
|
609
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(Portal$2, { "data-slot": "dialog-portal", ...props });
|
|
610
|
-
}
|
|
611
|
-
function DialogOverlay({
|
|
612
|
-
className,
|
|
613
|
-
...props
|
|
614
|
-
}) {
|
|
615
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
616
|
-
Overlay,
|
|
617
|
-
{
|
|
618
|
-
"data-slot": "dialog-overlay",
|
|
619
|
-
className: cn(
|
|
620
|
-
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
|
621
|
-
className
|
|
622
|
-
),
|
|
623
|
-
...props
|
|
624
|
-
}
|
|
625
|
-
);
|
|
626
|
-
}
|
|
627
|
-
function DialogContent({
|
|
628
|
-
className,
|
|
629
|
-
children,
|
|
630
|
-
showCloseButton = true,
|
|
631
|
-
...props
|
|
632
|
-
}) {
|
|
633
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogPortal, { "data-slot": "dialog-portal", children: [
|
|
634
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(DialogOverlay, {}),
|
|
635
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
636
|
-
Content,
|
|
637
|
-
{
|
|
638
|
-
"data-slot": "dialog-content",
|
|
639
|
-
className: cn(
|
|
640
|
-
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 outline-none sm:max-w-lg",
|
|
641
|
-
className
|
|
642
|
-
),
|
|
643
|
-
...props,
|
|
644
|
-
children: [
|
|
645
|
-
children,
|
|
646
|
-
showCloseButton && /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
647
|
-
Close,
|
|
648
|
-
{
|
|
649
|
-
"data-slot": "dialog-close",
|
|
650
|
-
className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
651
|
-
children: [
|
|
652
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(X, {}),
|
|
653
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sr-only", children: "Close" })
|
|
654
|
-
]
|
|
655
|
-
}
|
|
656
|
-
)
|
|
657
|
-
]
|
|
658
|
-
}
|
|
659
|
-
)
|
|
660
|
-
] });
|
|
661
|
-
}
|
|
662
|
-
function DialogHeader({ className, ...props }) {
|
|
663
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
664
|
-
"div",
|
|
665
|
-
{
|
|
666
|
-
"data-slot": "dialog-header",
|
|
667
|
-
className: cn("flex flex-col gap-2 text-center sm:text-left", className),
|
|
668
|
-
...props
|
|
669
|
-
}
|
|
670
|
-
);
|
|
671
|
-
}
|
|
672
|
-
function DialogFooter({ className, ...props }) {
|
|
673
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
674
|
-
"div",
|
|
675
|
-
{
|
|
676
|
-
"data-slot": "dialog-footer",
|
|
677
|
-
className: cn(
|
|
678
|
-
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
|
679
|
-
className
|
|
680
|
-
),
|
|
681
|
-
...props
|
|
682
|
-
}
|
|
683
|
-
);
|
|
684
|
-
}
|
|
685
|
-
function DialogTitle({
|
|
686
|
-
className,
|
|
687
|
-
...props
|
|
688
|
-
}) {
|
|
689
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
690
|
-
Title,
|
|
691
|
-
{
|
|
692
|
-
"data-slot": "dialog-title",
|
|
693
|
-
className: cn("text-lg leading-none font-semibold", className),
|
|
694
|
-
...props
|
|
695
|
-
}
|
|
696
|
-
);
|
|
697
|
-
}
|
|
698
|
-
function DialogDescription({
|
|
699
|
-
className,
|
|
700
|
-
...props
|
|
701
|
-
}) {
|
|
702
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
703
|
-
Description,
|
|
704
|
-
{
|
|
705
|
-
"data-slot": "dialog-description",
|
|
706
|
-
className: cn("text-muted-foreground text-sm", className),
|
|
707
|
-
...props
|
|
708
|
-
}
|
|
709
|
-
);
|
|
710
|
-
}
|
|
711
|
-
const buttonVariants = cva(
|
|
712
|
-
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
713
|
-
{
|
|
714
|
-
variants: {
|
|
715
|
-
variant: {
|
|
716
|
-
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
717
|
-
destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
718
|
-
outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
|
719
|
-
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
720
|
-
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
721
|
-
link: "text-primary underline-offset-4 hover:underline"
|
|
722
|
-
},
|
|
723
|
-
size: {
|
|
724
|
-
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
725
|
-
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
726
|
-
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
727
|
-
icon: "size-9",
|
|
728
|
-
"icon-sm": "size-8",
|
|
729
|
-
"icon-lg": "size-10"
|
|
730
|
-
}
|
|
731
|
-
},
|
|
732
|
-
defaultVariants: {
|
|
733
|
-
variant: "default",
|
|
734
|
-
size: "default"
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
);
|
|
738
|
-
function Button({
|
|
739
|
-
className,
|
|
740
|
-
variant = "default",
|
|
741
|
-
size = "default",
|
|
742
|
-
asChild = false,
|
|
743
|
-
...props
|
|
744
|
-
}) {
|
|
745
|
-
const Comp = asChild ? Slot : "button";
|
|
746
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
747
|
-
Comp,
|
|
748
|
-
{
|
|
749
|
-
"data-slot": "button",
|
|
750
|
-
"data-variant": variant,
|
|
751
|
-
"data-size": size,
|
|
752
|
-
className: cn(buttonVariants({ variant, size, className })),
|
|
753
|
-
...props
|
|
754
|
-
}
|
|
755
|
-
);
|
|
756
|
-
}
|
|
757
|
-
const motion = {
|
|
758
|
-
fast: "duration-100 ease-out"
|
|
759
|
-
};
|
|
760
|
-
const variantMap = {
|
|
761
|
-
primary: "default",
|
|
762
|
-
secondary: "secondary",
|
|
763
|
-
danger: "destructive",
|
|
764
|
-
ghost: "ghost",
|
|
765
|
-
outline: "outline",
|
|
766
|
-
link: "link",
|
|
767
|
-
success: "default",
|
|
768
|
-
warning: "default"
|
|
769
|
-
};
|
|
770
|
-
const customVariantClasses = {
|
|
771
|
-
success: "bg-success text-success-foreground hover:bg-success/90 focus-visible:ring-success",
|
|
772
|
-
warning: "bg-warning text-warning-foreground hover:bg-warning/90 focus-visible:ring-warning"
|
|
773
|
-
};
|
|
774
|
-
const LoadingSpinner = () => /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
775
|
-
"svg",
|
|
776
|
-
{
|
|
777
|
-
className: "size-4 animate-spin",
|
|
778
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
779
|
-
fill: "none",
|
|
780
|
-
viewBox: "0 0 24 24",
|
|
781
|
-
children: [
|
|
782
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
783
|
-
"circle",
|
|
784
|
-
{
|
|
785
|
-
className: "opacity-25",
|
|
786
|
-
cx: "12",
|
|
787
|
-
cy: "12",
|
|
788
|
-
r: "10",
|
|
789
|
-
stroke: "currentColor",
|
|
790
|
-
strokeWidth: "4"
|
|
791
|
-
}
|
|
792
|
-
),
|
|
793
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
794
|
-
"path",
|
|
795
|
-
{
|
|
796
|
-
className: "opacity-75",
|
|
797
|
-
fill: "currentColor",
|
|
798
|
-
d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
799
|
-
}
|
|
800
|
-
)
|
|
801
|
-
]
|
|
802
|
-
}
|
|
803
|
-
);
|
|
804
|
-
function CmsButton({
|
|
805
|
-
variant = "primary",
|
|
806
|
-
loading,
|
|
807
|
-
disabled,
|
|
808
|
-
children,
|
|
809
|
-
className,
|
|
810
|
-
asChild,
|
|
811
|
-
...props
|
|
812
|
-
}) {
|
|
813
|
-
const mappedVariant = variantMap[variant];
|
|
814
|
-
const customClass = customVariantClasses[variant];
|
|
815
|
-
if (asChild) {
|
|
816
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
817
|
-
Button,
|
|
818
|
-
{
|
|
819
|
-
variant: mappedVariant,
|
|
820
|
-
disabled: disabled || loading,
|
|
821
|
-
className: cn(motion.fast, customClass, className),
|
|
822
|
-
asChild: true,
|
|
823
|
-
...props,
|
|
824
|
-
children
|
|
825
|
-
}
|
|
826
|
-
);
|
|
827
|
-
}
|
|
828
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
829
|
-
Button,
|
|
830
|
-
{
|
|
831
|
-
variant: mappedVariant,
|
|
832
|
-
disabled: disabled || loading,
|
|
833
|
-
className: cn(motion.fast, customClass, className),
|
|
834
|
-
...props,
|
|
835
|
-
children: [
|
|
836
|
-
loading && /* @__PURE__ */ jsxRuntimeExports.jsx(LoadingSpinner, {}),
|
|
837
|
-
children
|
|
838
|
-
]
|
|
839
|
-
}
|
|
840
|
-
);
|
|
841
|
-
}
|
|
842
|
-
const sizeClasses = {
|
|
843
|
-
sm: "max-w-sm",
|
|
844
|
-
md: "max-w-md",
|
|
845
|
-
lg: "max-w-lg",
|
|
846
|
-
xl: "max-w-xl",
|
|
847
|
-
"2xl": "max-w-2xl"
|
|
848
|
-
};
|
|
849
|
-
function CmsDialog({
|
|
850
|
-
open,
|
|
851
|
-
onOpenChange,
|
|
852
|
-
title,
|
|
853
|
-
description,
|
|
854
|
-
children,
|
|
855
|
-
footer,
|
|
856
|
-
size = "md",
|
|
857
|
-
className
|
|
858
|
-
}) {
|
|
859
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
860
|
-
DialogContent,
|
|
861
|
-
{
|
|
862
|
-
className: cn(
|
|
863
|
-
"flex max-h-[85vh] flex-col overflow-hidden",
|
|
864
|
-
sizeClasses[size],
|
|
865
|
-
className
|
|
866
|
-
),
|
|
867
|
-
children: [
|
|
868
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(DialogHeader, { className: "shrink-0", children: [
|
|
869
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(DialogTitle, { children: title }),
|
|
870
|
-
description && /* @__PURE__ */ jsxRuntimeExports.jsx(DialogDescription, { children: description })
|
|
871
|
-
] }),
|
|
872
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
873
|
-
"div",
|
|
874
|
-
{
|
|
875
|
-
className: "min-h-0 flex-1 overflow-y-auto py-4 [&::-webkit-scrollbar]:hidden",
|
|
876
|
-
style: { scrollbarWidth: "none", msOverflowStyle: "none" },
|
|
877
|
-
children
|
|
878
|
-
}
|
|
879
|
-
),
|
|
880
|
-
footer && /* @__PURE__ */ jsxRuntimeExports.jsx(DialogFooter, { className: "shrink-0 border-t pt-4", children: footer })
|
|
881
|
-
]
|
|
882
|
-
}
|
|
883
|
-
) });
|
|
884
|
-
}
|
|
885
|
-
function CmsConfirmDialog({
|
|
886
|
-
open,
|
|
887
|
-
onOpenChange,
|
|
888
|
-
title,
|
|
889
|
-
description,
|
|
890
|
-
confirmLabel = "Confirm",
|
|
891
|
-
cancelLabel = "Cancel",
|
|
892
|
-
onConfirm,
|
|
893
|
-
onCancel,
|
|
894
|
-
variant = "default",
|
|
895
|
-
loading,
|
|
896
|
-
isLoading,
|
|
897
|
-
error
|
|
898
|
-
}) {
|
|
899
|
-
const isLoadingState = loading ?? isLoading ?? false;
|
|
900
|
-
const handleCancel = () => {
|
|
901
|
-
onCancel?.();
|
|
902
|
-
onOpenChange(false);
|
|
903
|
-
};
|
|
904
|
-
const handleConfirm = () => {
|
|
905
|
-
onConfirm();
|
|
906
|
-
};
|
|
907
|
-
const getButtonVariant = () => {
|
|
908
|
-
if (variant === "danger") return "danger";
|
|
909
|
-
if (variant === "warning") return "warning";
|
|
910
|
-
return "primary";
|
|
911
|
-
};
|
|
912
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
913
|
-
CmsDialog,
|
|
914
|
-
{
|
|
915
|
-
open,
|
|
916
|
-
onOpenChange,
|
|
917
|
-
title,
|
|
918
|
-
size: "sm",
|
|
919
|
-
footer: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
920
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(CmsButton, { variant: "outline", onClick: handleCancel, disabled: isLoadingState, children: cancelLabel }),
|
|
921
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
922
|
-
CmsButton,
|
|
923
|
-
{
|
|
924
|
-
variant: getButtonVariant(),
|
|
925
|
-
onClick: handleConfirm,
|
|
926
|
-
loading: isLoadingState,
|
|
927
|
-
children: confirmLabel
|
|
928
|
-
}
|
|
929
|
-
)
|
|
930
|
-
] }),
|
|
931
|
-
children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3", children: [
|
|
932
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-muted-foreground", children: description }),
|
|
933
|
-
error && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-destructive", children: error })
|
|
934
|
-
] })
|
|
935
|
-
}
|
|
936
|
-
);
|
|
937
|
-
}
|
|
938
|
-
function Input({ className, type, ...props }) {
|
|
939
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
940
|
-
"input",
|
|
941
|
-
{
|
|
942
|
-
type,
|
|
943
|
-
"data-slot": "input",
|
|
944
|
-
className: cn(
|
|
945
|
-
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
946
|
-
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
947
|
-
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
948
|
-
className
|
|
949
|
-
),
|
|
950
|
-
...props
|
|
951
|
-
}
|
|
952
|
-
);
|
|
953
|
-
}
|
|
954
|
-
function Label({
|
|
955
|
-
className,
|
|
956
|
-
...props
|
|
957
|
-
}) {
|
|
958
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
959
|
-
Root$2,
|
|
960
|
-
{
|
|
961
|
-
"data-slot": "label",
|
|
962
|
-
className: cn(
|
|
963
|
-
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
|
964
|
-
className
|
|
965
|
-
),
|
|
966
|
-
...props
|
|
967
|
-
}
|
|
968
|
-
);
|
|
969
|
-
}
|
|
970
|
-
function Textarea({ className, ...props }) {
|
|
971
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
972
|
-
"textarea",
|
|
973
|
-
{
|
|
974
|
-
"data-slot": "textarea",
|
|
975
|
-
className: cn(
|
|
976
|
-
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
977
|
-
className
|
|
978
|
-
),
|
|
979
|
-
...props
|
|
980
|
-
}
|
|
981
|
-
);
|
|
982
|
-
}
|
|
983
|
-
function Checkbox({
|
|
984
|
-
className,
|
|
985
|
-
...props
|
|
986
|
-
}) {
|
|
987
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
988
|
-
Checkbox$1,
|
|
989
|
-
{
|
|
990
|
-
"data-slot": "checkbox",
|
|
991
|
-
className: cn(
|
|
992
|
-
"peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
|
993
|
-
className
|
|
994
|
-
),
|
|
995
|
-
...props,
|
|
996
|
-
children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
997
|
-
CheckboxIndicator,
|
|
998
|
-
{
|
|
999
|
-
"data-slot": "checkbox-indicator",
|
|
1000
|
-
className: "grid place-content-center text-current transition-none",
|
|
1001
|
-
children: /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5" })
|
|
1002
|
-
}
|
|
1003
|
-
)
|
|
1004
|
-
}
|
|
1005
|
-
);
|
|
1006
|
-
}
|
|
1007
|
-
function Select({
|
|
1008
|
-
...props
|
|
1009
|
-
}) {
|
|
1010
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(Root2$2, { "data-slot": "select", ...props });
|
|
1011
|
-
}
|
|
1012
|
-
function SelectValue({
|
|
1013
|
-
...props
|
|
1014
|
-
}) {
|
|
1015
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(Value, { "data-slot": "select-value", ...props });
|
|
1016
|
-
}
|
|
1017
|
-
function SelectTrigger({
|
|
1018
|
-
className,
|
|
1019
|
-
size = "default",
|
|
1020
|
-
children,
|
|
1021
|
-
...props
|
|
1022
|
-
}) {
|
|
1023
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
1024
|
-
Trigger$2,
|
|
1025
|
-
{
|
|
1026
|
-
"data-slot": "select-trigger",
|
|
1027
|
-
"data-size": size,
|
|
1028
|
-
className: cn(
|
|
1029
|
-
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
1030
|
-
className
|
|
1031
|
-
),
|
|
1032
|
-
...props,
|
|
1033
|
-
children: [
|
|
1034
|
-
children,
|
|
1035
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Icon$1, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4 opacity-50" }) })
|
|
1036
|
-
]
|
|
1037
|
-
}
|
|
1038
|
-
);
|
|
1039
|
-
}
|
|
1040
|
-
function SelectContent({
|
|
1041
|
-
className,
|
|
1042
|
-
children,
|
|
1043
|
-
position = "item-aligned",
|
|
1044
|
-
align = "center",
|
|
1045
|
-
...props
|
|
1046
|
-
}) {
|
|
1047
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(Portal$1, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
1048
|
-
Content2$2,
|
|
1049
|
-
{
|
|
1050
|
-
"data-slot": "select-content",
|
|
1051
|
-
className: cn(
|
|
1052
|
-
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
|
|
1053
|
-
position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
1054
|
-
className
|
|
1055
|
-
),
|
|
1056
|
-
position,
|
|
1057
|
-
align,
|
|
1058
|
-
...props,
|
|
1059
|
-
children: [
|
|
1060
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(SelectScrollUpButton, {}),
|
|
1061
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1062
|
-
Viewport,
|
|
1063
|
-
{
|
|
1064
|
-
className: cn(
|
|
1065
|
-
"p-1",
|
|
1066
|
-
position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
|
|
1067
|
-
),
|
|
1068
|
-
children
|
|
1069
|
-
}
|
|
1070
|
-
),
|
|
1071
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(SelectScrollDownButton, {})
|
|
1072
|
-
]
|
|
1073
|
-
}
|
|
1074
|
-
) });
|
|
1075
|
-
}
|
|
1076
|
-
function SelectItem({
|
|
1077
|
-
className,
|
|
1078
|
-
children,
|
|
1079
|
-
...props
|
|
1080
|
-
}) {
|
|
1081
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
1082
|
-
Item,
|
|
1083
|
-
{
|
|
1084
|
-
"data-slot": "select-item",
|
|
1085
|
-
className: cn(
|
|
1086
|
-
"focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
|
1087
|
-
className
|
|
1088
|
-
),
|
|
1089
|
-
...props,
|
|
1090
|
-
children: [
|
|
1091
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1092
|
-
"span",
|
|
1093
|
-
{
|
|
1094
|
-
"data-slot": "select-item-indicator",
|
|
1095
|
-
className: "absolute right-2 flex size-3.5 items-center justify-center",
|
|
1096
|
-
children: /* @__PURE__ */ jsxRuntimeExports.jsx(ItemIndicator, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-4" }) })
|
|
1097
|
-
}
|
|
1098
|
-
),
|
|
1099
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(ItemText, { children })
|
|
1100
|
-
]
|
|
1101
|
-
}
|
|
1102
|
-
);
|
|
1103
|
-
}
|
|
1104
|
-
function SelectScrollUpButton({
|
|
1105
|
-
className,
|
|
1106
|
-
...props
|
|
1107
|
-
}) {
|
|
1108
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1109
|
-
ScrollUpButton,
|
|
1110
|
-
{
|
|
1111
|
-
"data-slot": "select-scroll-up-button",
|
|
1112
|
-
className: cn(
|
|
1113
|
-
"flex cursor-default items-center justify-center py-1",
|
|
1114
|
-
className
|
|
1115
|
-
),
|
|
1116
|
-
...props,
|
|
1117
|
-
children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { className: "size-4" })
|
|
1118
|
-
}
|
|
1119
|
-
);
|
|
1120
|
-
}
|
|
1121
|
-
function SelectScrollDownButton({
|
|
1122
|
-
className,
|
|
1123
|
-
...props
|
|
1124
|
-
}) {
|
|
1125
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1126
|
-
ScrollDownButton,
|
|
1127
|
-
{
|
|
1128
|
-
"data-slot": "select-scroll-down-button",
|
|
1129
|
-
className: cn(
|
|
1130
|
-
"flex cursor-default items-center justify-center py-1",
|
|
1131
|
-
className
|
|
1132
|
-
),
|
|
1133
|
-
...props,
|
|
1134
|
-
children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4" })
|
|
1135
|
-
}
|
|
1136
|
-
);
|
|
1137
|
-
}
|
|
1138
|
-
function BreakingChangesWarningDialog({
|
|
1139
|
-
isOpen,
|
|
1140
|
-
onClose,
|
|
1141
|
-
breakingChanges,
|
|
1142
|
-
onForceUpdate,
|
|
1143
|
-
onCancel,
|
|
1144
|
-
isLoading
|
|
1145
|
-
}) {
|
|
1146
|
-
const handleCancel = () => {
|
|
1147
|
-
onCancel();
|
|
1148
|
-
onClose();
|
|
1149
|
-
};
|
|
1150
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1151
|
-
CmsDialog,
|
|
1152
|
-
{
|
|
1153
|
-
open: isOpen,
|
|
1154
|
-
onOpenChange: (open) => !open && !isLoading && handleCancel(),
|
|
1155
|
-
title: "Breaking Changes Detected",
|
|
1156
|
-
size: "lg",
|
|
1157
|
-
footer: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
1158
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(CmsButton, { variant: "outline", onClick: handleCancel, disabled: isLoading, children: "Cancel" }),
|
|
1159
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(CmsButton, { variant: "danger", onClick: onForceUpdate, loading: isLoading, children: "Force Update" })
|
|
1160
|
-
] }),
|
|
1161
|
-
children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
|
|
1162
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "diff-modified flex items-start gap-3 rounded-lg border p-3", children: [
|
|
1163
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "mt-0.5 size-5 shrink-0 text-diff-modified" }),
|
|
1164
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
|
|
1165
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-medium text-diff-modified", children: "These changes may affect existing content" }),
|
|
1166
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-diff-modified-foreground", children: "The following changes could cause data loss or validation errors for existing entries. Review carefully before proceeding." })
|
|
1167
|
-
] })
|
|
1168
|
-
] }),
|
|
1169
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
1170
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-sm font-medium text-foreground", children: [
|
|
1171
|
-
breakingChanges.length,
|
|
1172
|
-
" breaking change",
|
|
1173
|
-
breakingChanges.length !== 1 ? "s" : "",
|
|
1174
|
-
" detected:"
|
|
1175
|
-
] }),
|
|
1176
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("ul", { className: "space-y-2", children: breakingChanges.map((change, index) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
1177
|
-
"li",
|
|
1178
|
-
{
|
|
1179
|
-
className: "flex items-start gap-2 rounded-md border bg-muted/30 px-3 py-2 text-sm",
|
|
1180
|
-
children: [
|
|
1181
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "mt-0.5 size-1.5 shrink-0 rounded-full bg-warning" }),
|
|
1182
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground", children: change })
|
|
1183
|
-
]
|
|
1184
|
-
},
|
|
1185
|
-
index
|
|
1186
|
-
)) })
|
|
1187
|
-
] }),
|
|
1188
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: 'Click "Force Update" to apply these changes anyway, or "Cancel" to go back and modify your changes.' })
|
|
1189
|
-
] })
|
|
1190
|
-
}
|
|
1191
|
-
);
|
|
1192
|
-
}
|
|
1193
|
-
const FIELD_TYPE_INFO = {
|
|
1194
|
-
text: {
|
|
1195
|
-
label: "Text",
|
|
1196
|
-
icon: /* @__PURE__ */ jsxRuntimeExports.jsx(TextAlignStart, { className: "size-4" }),
|
|
1197
|
-
description: "Single line text input"
|
|
1198
|
-
},
|
|
1199
|
-
richText: {
|
|
1200
|
-
label: "Rich Text",
|
|
1201
|
-
icon: /* @__PURE__ */ jsxRuntimeExports.jsx(FileType, { className: "size-4" }),
|
|
1202
|
-
description: "Multi-line formatted text"
|
|
1203
|
-
},
|
|
1204
|
-
number: {
|
|
1205
|
-
label: "Number",
|
|
1206
|
-
icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Hash, { className: "size-4" }),
|
|
1207
|
-
description: "Numeric value"
|
|
1208
|
-
},
|
|
1209
|
-
boolean: {
|
|
1210
|
-
label: "Boolean",
|
|
1211
|
-
icon: /* @__PURE__ */ jsxRuntimeExports.jsx(ToggleLeft, { className: "size-4" }),
|
|
1212
|
-
description: "True/false toggle"
|
|
1213
|
-
},
|
|
1214
|
-
date: {
|
|
1215
|
-
label: "Date",
|
|
1216
|
-
icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Calendar, { className: "size-4" }),
|
|
1217
|
-
description: "Date picker"
|
|
1218
|
-
},
|
|
1219
|
-
datetime: {
|
|
1220
|
-
label: "Date & Time",
|
|
1221
|
-
icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Calendar, { className: "size-4" }),
|
|
1222
|
-
description: "Date and time picker"
|
|
1223
|
-
},
|
|
1224
|
-
reference: {
|
|
1225
|
-
label: "Reference",
|
|
1226
|
-
icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Link2, { className: "size-4" }),
|
|
1227
|
-
description: "Link to another content entry"
|
|
1228
|
-
},
|
|
1229
|
-
media: {
|
|
1230
|
-
label: "Media",
|
|
1231
|
-
icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Image$1, { className: "size-4" }),
|
|
1232
|
-
description: "Image, video, or file"
|
|
1233
|
-
},
|
|
1234
|
-
json: {
|
|
1235
|
-
label: "JSON",
|
|
1236
|
-
icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Braces, { className: "size-4" }),
|
|
1237
|
-
description: "Custom JSON data"
|
|
1238
|
-
},
|
|
1239
|
-
select: {
|
|
1240
|
-
label: "Select",
|
|
1241
|
-
icon: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4" }),
|
|
1242
|
-
description: "Dropdown selection"
|
|
1243
|
-
},
|
|
1244
|
-
multiSelect: {
|
|
1245
|
-
label: "Multi-Select",
|
|
1246
|
-
icon: /* @__PURE__ */ jsxRuntimeExports.jsx(List, { className: "size-4" }),
|
|
1247
|
-
description: "Multiple selections"
|
|
1248
|
-
},
|
|
1249
|
-
tags: {
|
|
1250
|
-
label: "Tags",
|
|
1251
|
-
icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Tag, { className: "size-4" }),
|
|
1252
|
-
description: "Free-form tag list"
|
|
1253
|
-
},
|
|
1254
|
-
category: {
|
|
1255
|
-
label: "Category",
|
|
1256
|
-
icon: /* @__PURE__ */ jsxRuntimeExports.jsx(FolderOpen, { className: "size-4" }),
|
|
1257
|
-
description: "Taxonomy category selection"
|
|
1258
|
-
}
|
|
1259
|
-
};
|
|
1260
|
-
function generateMachineName(displayName) {
|
|
1261
|
-
return displayName.toLowerCase().trim().replace(/[^a-z0-9\s]/g, "").replace(/\s+/g, "_").replace(/^[0-9]/, "_$&").slice(0, 64);
|
|
1262
|
-
}
|
|
1263
|
-
function isValidMachineName(name) {
|
|
1264
|
-
return /^[a-z][a-z0-9_]{0,63}$/.test(name);
|
|
1265
|
-
}
|
|
1266
|
-
function ContentTypeFormModal({
|
|
1267
|
-
isOpen,
|
|
1268
|
-
onClose,
|
|
1269
|
-
onCreated,
|
|
1270
|
-
onUpdated,
|
|
1271
|
-
contentType
|
|
1272
|
-
}) {
|
|
1273
|
-
const isEditing = !!contentType;
|
|
1274
|
-
const isCodeDefined = contentType?.source === "code";
|
|
1275
|
-
const isReadOnly = isCodeDefined;
|
|
1276
|
-
const [displayName, setDisplayName] = reactExports.useState("");
|
|
1277
|
-
const [machineName, setMachineName] = reactExports.useState("");
|
|
1278
|
-
const [machineNameManuallyEdited, setMachineNameManuallyEdited] = reactExports.useState(
|
|
1279
|
-
false
|
|
1280
|
-
);
|
|
1281
|
-
const [description, setDescription] = reactExports.useState("");
|
|
1282
|
-
const [singleton, setSingleton] = reactExports.useState(false);
|
|
1283
|
-
const [fields, setFields] = reactExports.useState([
|
|
1284
|
-
{ name: "title", label: "Title", type: "text", required: true }
|
|
1285
|
-
]);
|
|
1286
|
-
const [titleField, setTitleField] = reactExports.useState("title");
|
|
1287
|
-
const [slugField, setSlugField] = reactExports.useState("title");
|
|
1288
|
-
const [isSubmitting, setIsSubmitting] = reactExports.useState(false);
|
|
1289
|
-
const [submitError, setSubmitError] = reactExports.useState(null);
|
|
1290
|
-
const [activeFieldIndex, setActiveFieldIndex] = reactExports.useState(null);
|
|
1291
|
-
const [showFieldEditor, setShowFieldEditor] = reactExports.useState(false);
|
|
1292
|
-
const [breakingChanges, setBreakingChanges] = reactExports.useState([]);
|
|
1293
|
-
const [showBreakingWarning, setShowBreakingWarning] = reactExports.useState(false);
|
|
1294
|
-
const [isForceUpdating, setIsForceUpdating] = reactExports.useState(false);
|
|
1295
|
-
const api2 = useApi();
|
|
1296
|
-
const createContentType = useMutation(api2.createContentType);
|
|
1297
|
-
const updateContentType = useMutation(api2.updateContentType);
|
|
1298
|
-
reactExports.useEffect(() => {
|
|
1299
|
-
if (contentType && isOpen) {
|
|
1300
|
-
setDisplayName(contentType.displayName);
|
|
1301
|
-
setMachineName(contentType.name);
|
|
1302
|
-
setMachineNameManuallyEdited(true);
|
|
1303
|
-
setDescription(contentType.description || "");
|
|
1304
|
-
setSingleton(contentType.singleton || false);
|
|
1305
|
-
setFields(contentType.fields);
|
|
1306
|
-
setTitleField(contentType.titleField || "");
|
|
1307
|
-
setSlugField(contentType.slugField || "");
|
|
1308
|
-
}
|
|
1309
|
-
}, [contentType, isOpen]);
|
|
1310
|
-
const resetForm = reactExports.useCallback(() => {
|
|
1311
|
-
setDisplayName("");
|
|
1312
|
-
setMachineName("");
|
|
1313
|
-
setMachineNameManuallyEdited(false);
|
|
1314
|
-
setDescription("");
|
|
1315
|
-
setSingleton(false);
|
|
1316
|
-
setFields([
|
|
1317
|
-
{ name: "title", label: "Title", type: "text", required: true }
|
|
1318
|
-
]);
|
|
1319
|
-
setTitleField("title");
|
|
1320
|
-
setSlugField("title");
|
|
1321
|
-
setIsSubmitting(false);
|
|
1322
|
-
setSubmitError(null);
|
|
1323
|
-
setActiveFieldIndex(null);
|
|
1324
|
-
setShowFieldEditor(false);
|
|
1325
|
-
setBreakingChanges([]);
|
|
1326
|
-
setShowBreakingWarning(false);
|
|
1327
|
-
setIsForceUpdating(false);
|
|
1328
|
-
}, []);
|
|
1329
|
-
const handleDisplayNameChange = reactExports.useCallback(
|
|
1330
|
-
(value) => {
|
|
1331
|
-
setDisplayName(value);
|
|
1332
|
-
if (!machineNameManuallyEdited) {
|
|
1333
|
-
setMachineName(generateMachineName(value));
|
|
1334
|
-
}
|
|
1335
|
-
},
|
|
1336
|
-
[machineNameManuallyEdited]
|
|
1337
|
-
);
|
|
1338
|
-
const handleMachineNameChange = reactExports.useCallback((value) => {
|
|
1339
|
-
setMachineNameManuallyEdited(true);
|
|
1340
|
-
setMachineName(value.toLowerCase().replace(/[^a-z0-9_]/g, ""));
|
|
1341
|
-
}, []);
|
|
1342
|
-
const addField = reactExports.useCallback(() => {
|
|
1343
|
-
const newFieldName = `field_${fields.length + 1}`;
|
|
1344
|
-
setFields((prev) => [
|
|
1345
|
-
...prev,
|
|
1346
|
-
{
|
|
1347
|
-
name: newFieldName,
|
|
1348
|
-
label: `Field ${prev.length + 1}`,
|
|
1349
|
-
type: "text",
|
|
1350
|
-
required: false
|
|
1351
|
-
}
|
|
1352
|
-
]);
|
|
1353
|
-
setActiveFieldIndex(fields.length);
|
|
1354
|
-
setShowFieldEditor(true);
|
|
1355
|
-
}, [fields.length]);
|
|
1356
|
-
const removeField = reactExports.useCallback(
|
|
1357
|
-
(index) => {
|
|
1358
|
-
const fieldToRemove = fields[index];
|
|
1359
|
-
setFields((prev) => prev.filter((_, i) => i !== index));
|
|
1360
|
-
if (titleField === fieldToRemove.name) {
|
|
1361
|
-
const firstTextField = fields.find(
|
|
1362
|
-
(f, i) => i !== index && f.type === "text"
|
|
1363
|
-
);
|
|
1364
|
-
setTitleField(firstTextField?.name || "");
|
|
1365
|
-
}
|
|
1366
|
-
if (slugField === fieldToRemove.name) {
|
|
1367
|
-
const firstTextField = fields.find(
|
|
1368
|
-
(f, i) => i !== index && f.type === "text"
|
|
1369
|
-
);
|
|
1370
|
-
setSlugField(firstTextField?.name || "");
|
|
1371
|
-
}
|
|
1372
|
-
if (activeFieldIndex === index) {
|
|
1373
|
-
setActiveFieldIndex(null);
|
|
1374
|
-
setShowFieldEditor(false);
|
|
1375
|
-
} else if (activeFieldIndex !== null && activeFieldIndex > index) {
|
|
1376
|
-
setActiveFieldIndex(activeFieldIndex - 1);
|
|
1377
|
-
}
|
|
1378
|
-
},
|
|
1379
|
-
[fields, activeFieldIndex, titleField, slugField]
|
|
1380
|
-
);
|
|
1381
|
-
const updateField = reactExports.useCallback(
|
|
1382
|
-
(index, updates) => {
|
|
1383
|
-
setFields(
|
|
1384
|
-
(prev) => prev.map(
|
|
1385
|
-
(field, i) => i === index ? { ...field, ...updates } : field
|
|
1386
|
-
)
|
|
1387
|
-
);
|
|
1388
|
-
},
|
|
1389
|
-
[]
|
|
1390
|
-
);
|
|
1391
|
-
const moveField = reactExports.useCallback((fromIndex, toIndex) => {
|
|
1392
|
-
setFields((prev) => {
|
|
1393
|
-
const newFields = [...prev];
|
|
1394
|
-
const [movedField] = newFields.splice(fromIndex, 1);
|
|
1395
|
-
newFields.splice(toIndex, 0, movedField);
|
|
1396
|
-
return newFields;
|
|
1397
|
-
});
|
|
1398
|
-
setActiveFieldIndex(toIndex);
|
|
1399
|
-
}, []);
|
|
1400
|
-
const validationErrors = reactExports.useMemo(() => {
|
|
1401
|
-
const errors = [];
|
|
1402
|
-
if (!displayName.trim()) {
|
|
1403
|
-
errors.push("Display name is required");
|
|
1404
|
-
}
|
|
1405
|
-
if (!machineName.trim()) {
|
|
1406
|
-
errors.push("System Name is required");
|
|
1407
|
-
} else if (!isValidMachineName(machineName)) {
|
|
1408
|
-
errors.push(
|
|
1409
|
-
"System Name must start with a letter and contain only lowercase letters, numbers, and underscores"
|
|
1410
|
-
);
|
|
1411
|
-
}
|
|
1412
|
-
if (fields.length === 0) {
|
|
1413
|
-
errors.push("At least one field is required");
|
|
1414
|
-
}
|
|
1415
|
-
const fieldNames = fields.map((f) => f.name);
|
|
1416
|
-
const duplicates = fieldNames.filter(
|
|
1417
|
-
(name, index) => fieldNames.indexOf(name) !== index
|
|
1418
|
-
);
|
|
1419
|
-
if (duplicates.length > 0) {
|
|
1420
|
-
errors.push(
|
|
1421
|
-
`Duplicate field names: ${[...new Set(duplicates)].join(", ")}`
|
|
1422
|
-
);
|
|
1423
|
-
}
|
|
1424
|
-
for (const field of fields) {
|
|
1425
|
-
if (!field.name.trim()) {
|
|
1426
|
-
errors.push(`Field "${field.label}" has an empty name`);
|
|
1427
|
-
} else if (!/^[a-z][a-z0-9_]{0,63}$/.test(field.name)) {
|
|
1428
|
-
errors.push(`Field "${field.name}" has an invalid name format`);
|
|
1429
|
-
}
|
|
1430
|
-
if (!field.label.trim()) {
|
|
1431
|
-
errors.push(`Field with name "${field.name}" has an empty label`);
|
|
1432
|
-
}
|
|
1433
|
-
if ((field.type === "select" || field.type === "multiSelect") && (!field.options?.options || field.options.options.length === 0)) {
|
|
1434
|
-
errors.push(
|
|
1435
|
-
`${field.type} field "${field.label}" requires at least one option`
|
|
1436
|
-
);
|
|
1437
|
-
}
|
|
1438
|
-
}
|
|
1439
|
-
return errors;
|
|
1440
|
-
}, [displayName, machineName, fields]);
|
|
1441
|
-
const textFields = reactExports.useMemo(() => fields.filter((f) => f.type === "text"), [
|
|
1442
|
-
fields
|
|
1443
|
-
]);
|
|
1444
|
-
const parseBreakingChanges = (errorMessage) => {
|
|
1445
|
-
const lines = errorMessage.split("\n");
|
|
1446
|
-
return lines.filter((line) => line.trim().startsWith("-")).map((line) => line.trim().substring(2));
|
|
1447
|
-
};
|
|
1448
|
-
const handleSubmit = reactExports.useCallback(
|
|
1449
|
-
async (e, force = false) => {
|
|
1450
|
-
e.preventDefault();
|
|
1451
|
-
if (validationErrors.length > 0) {
|
|
1452
|
-
setSubmitError(validationErrors.join(". "));
|
|
1453
|
-
return;
|
|
1454
|
-
}
|
|
1455
|
-
setIsSubmitting(true);
|
|
1456
|
-
setSubmitError(null);
|
|
1457
|
-
try {
|
|
1458
|
-
if (isEditing && contentType) {
|
|
1459
|
-
const result = await updateContentType({
|
|
1460
|
-
id: contentType._id,
|
|
1461
|
-
displayName: displayName.trim(),
|
|
1462
|
-
description: description.trim() || void 0,
|
|
1463
|
-
fields,
|
|
1464
|
-
singleton,
|
|
1465
|
-
titleField: titleField || void 0,
|
|
1466
|
-
slugField: slugField || void 0,
|
|
1467
|
-
force
|
|
1468
|
-
});
|
|
1469
|
-
onUpdated?.(result);
|
|
1470
|
-
resetForm();
|
|
1471
|
-
onClose();
|
|
1472
|
-
} else {
|
|
1473
|
-
const result = await createContentType({
|
|
1474
|
-
name: machineName,
|
|
1475
|
-
displayName: displayName.trim(),
|
|
1476
|
-
description: description.trim() || void 0,
|
|
1477
|
-
fields,
|
|
1478
|
-
singleton,
|
|
1479
|
-
titleField: titleField || void 0,
|
|
1480
|
-
slugField: slugField || void 0
|
|
1481
|
-
});
|
|
1482
|
-
onCreated?.(result);
|
|
1483
|
-
resetForm();
|
|
1484
|
-
onClose();
|
|
1485
|
-
}
|
|
1486
|
-
} catch (error) {
|
|
1487
|
-
const message = error instanceof Error ? error.message : isEditing ? "Failed to update content type" : "Failed to create content type";
|
|
1488
|
-
if (isEditing && !force && message.includes("breaking change")) {
|
|
1489
|
-
const changes = parseBreakingChanges(message);
|
|
1490
|
-
setBreakingChanges(changes);
|
|
1491
|
-
setShowBreakingWarning(true);
|
|
1492
|
-
} else {
|
|
1493
|
-
setSubmitError(message);
|
|
1494
|
-
}
|
|
1495
|
-
} finally {
|
|
1496
|
-
setIsSubmitting(false);
|
|
1497
|
-
}
|
|
1498
|
-
},
|
|
1499
|
-
[
|
|
1500
|
-
validationErrors,
|
|
1501
|
-
isEditing,
|
|
1502
|
-
contentType,
|
|
1503
|
-
createContentType,
|
|
1504
|
-
updateContentType,
|
|
1505
|
-
machineName,
|
|
1506
|
-
displayName,
|
|
1507
|
-
description,
|
|
1508
|
-
fields,
|
|
1509
|
-
singleton,
|
|
1510
|
-
titleField,
|
|
1511
|
-
slugField,
|
|
1512
|
-
onCreated,
|
|
1513
|
-
onUpdated,
|
|
1514
|
-
resetForm,
|
|
1515
|
-
onClose
|
|
1516
|
-
]
|
|
1517
|
-
);
|
|
1518
|
-
const handleForceUpdate = reactExports.useCallback(async () => {
|
|
1519
|
-
setIsForceUpdating(true);
|
|
1520
|
-
try {
|
|
1521
|
-
if (contentType) {
|
|
1522
|
-
const result = await updateContentType({
|
|
1523
|
-
id: contentType._id,
|
|
1524
|
-
displayName: displayName.trim(),
|
|
1525
|
-
description: description.trim() || void 0,
|
|
1526
|
-
fields,
|
|
1527
|
-
singleton,
|
|
1528
|
-
titleField: titleField || void 0,
|
|
1529
|
-
slugField: slugField || void 0,
|
|
1530
|
-
force: true
|
|
1531
|
-
});
|
|
1532
|
-
onUpdated?.(result);
|
|
1533
|
-
resetForm();
|
|
1534
|
-
setShowBreakingWarning(false);
|
|
1535
|
-
onClose();
|
|
1536
|
-
}
|
|
1537
|
-
} catch (error) {
|
|
1538
|
-
const message = error instanceof Error ? error.message : "Failed to update content type";
|
|
1539
|
-
setSubmitError(message);
|
|
1540
|
-
setShowBreakingWarning(false);
|
|
1541
|
-
} finally {
|
|
1542
|
-
setIsForceUpdating(false);
|
|
1543
|
-
}
|
|
1544
|
-
}, [
|
|
1545
|
-
contentType,
|
|
1546
|
-
updateContentType,
|
|
1547
|
-
displayName,
|
|
1548
|
-
description,
|
|
1549
|
-
fields,
|
|
1550
|
-
singleton,
|
|
1551
|
-
titleField,
|
|
1552
|
-
slugField,
|
|
1553
|
-
onUpdated,
|
|
1554
|
-
resetForm,
|
|
1555
|
-
onClose
|
|
1556
|
-
]);
|
|
1557
|
-
const handleClose = reactExports.useCallback(() => {
|
|
1558
|
-
if (isSubmitting) return;
|
|
1559
|
-
resetForm();
|
|
1560
|
-
onClose();
|
|
1561
|
-
}, [isSubmitting, resetForm, onClose]);
|
|
1562
|
-
if (!isOpen) return null;
|
|
1563
|
-
const activeField = activeFieldIndex !== null ? fields[activeFieldIndex] : null;
|
|
1564
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
1565
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1566
|
-
CmsDialog,
|
|
1567
|
-
{
|
|
1568
|
-
open: isOpen,
|
|
1569
|
-
onOpenChange: (open) => !open && handleClose(),
|
|
1570
|
-
title: isCodeDefined ? "View Content Type" : isEditing ? "Edit Content Type" : "Create Content Type",
|
|
1571
|
-
size: "2xl",
|
|
1572
|
-
footer: isReadOnly ? /* @__PURE__ */ jsxRuntimeExports.jsx(CmsButton, { variant: "outline", onClick: handleClose, children: "Close" }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
1573
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1574
|
-
CmsButton,
|
|
1575
|
-
{
|
|
1576
|
-
variant: "outline",
|
|
1577
|
-
onClick: handleClose,
|
|
1578
|
-
disabled: isSubmitting,
|
|
1579
|
-
children: "Cancel"
|
|
1580
|
-
}
|
|
1581
|
-
),
|
|
1582
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1583
|
-
CmsButton,
|
|
1584
|
-
{
|
|
1585
|
-
variant: "primary",
|
|
1586
|
-
onClick: handleSubmit,
|
|
1587
|
-
disabled: validationErrors.length > 0,
|
|
1588
|
-
loading: isSubmitting,
|
|
1589
|
-
"data-testid": isEditing ? "update-content-type-submit" : "create-content-type-submit",
|
|
1590
|
-
children: isEditing ? "Save Changes" : "Create Content Type"
|
|
1591
|
-
}
|
|
1592
|
-
)
|
|
1593
|
-
] }),
|
|
1594
|
-
children: /* @__PURE__ */ jsxRuntimeExports.jsxs("form", { onSubmit: handleSubmit, className: "space-y-6", children: [
|
|
1595
|
-
isCodeDefined && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start gap-3 rounded-lg border border-violet-200 bg-violet-50 p-3", children: [
|
|
1596
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(CodeXml, { className: "mt-0.5 size-5 shrink-0 text-violet-600" }),
|
|
1597
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
|
|
1598
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-medium text-violet-900", children: "Managed by Code" }),
|
|
1599
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-violet-700", children: "This content type is defined in your codebase and cannot be edited through the admin interface. To make changes, update the definition in your code." })
|
|
1600
|
-
] })
|
|
1601
|
-
] }),
|
|
1602
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
|
|
1603
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "text-sm font-semibold text-foreground", children: "Basic Information" }),
|
|
1604
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
1605
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(Label, { htmlFor: "displayName", children: [
|
|
1606
|
-
"Display Name ",
|
|
1607
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
|
|
1608
|
-
] }),
|
|
1609
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1610
|
-
Input,
|
|
1611
|
-
{
|
|
1612
|
-
id: "displayName",
|
|
1613
|
-
value: displayName,
|
|
1614
|
-
onChange: (e) => handleDisplayNameChange(e.target.value),
|
|
1615
|
-
placeholder: "e.g., Blog Post",
|
|
1616
|
-
disabled: isSubmitting || isReadOnly,
|
|
1617
|
-
autoFocus: !isReadOnly,
|
|
1618
|
-
"data-testid": "display-name-input"
|
|
1619
|
-
}
|
|
1620
|
-
)
|
|
1621
|
-
] }),
|
|
1622
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
1623
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(Label, { htmlFor: "machineName", children: [
|
|
1624
|
-
"System Name ",
|
|
1625
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
|
|
1626
|
-
] }),
|
|
1627
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1628
|
-
Input,
|
|
1629
|
-
{
|
|
1630
|
-
id: "machineName",
|
|
1631
|
-
value: machineName,
|
|
1632
|
-
onChange: (e) => handleMachineNameChange(e.target.value),
|
|
1633
|
-
placeholder: "e.g., blog_post",
|
|
1634
|
-
disabled: isSubmitting || isEditing || isReadOnly,
|
|
1635
|
-
className: cn(
|
|
1636
|
-
!isValidMachineName(machineName) && machineName && "border-destructive"
|
|
1637
|
-
),
|
|
1638
|
-
"data-testid": "machine-name-input"
|
|
1639
|
-
}
|
|
1640
|
-
),
|
|
1641
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: isEditing ? "System name cannot be changed after creation" : "Lowercase letters, numbers, and underscores only. Used in API queries." })
|
|
1642
|
-
] }),
|
|
1643
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
1644
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Label, { htmlFor: "description", children: "Description" }),
|
|
1645
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1646
|
-
Textarea,
|
|
1647
|
-
{
|
|
1648
|
-
id: "description",
|
|
1649
|
-
value: description,
|
|
1650
|
-
onChange: (e) => setDescription(e.target.value),
|
|
1651
|
-
placeholder: "Optional description of this content type",
|
|
1652
|
-
disabled: isSubmitting || isReadOnly,
|
|
1653
|
-
rows: 2
|
|
1654
|
-
}
|
|
1655
|
-
)
|
|
1656
|
-
] }),
|
|
1657
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1658
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1659
|
-
Checkbox,
|
|
1660
|
-
{
|
|
1661
|
-
id: "singleton",
|
|
1662
|
-
checked: singleton,
|
|
1663
|
-
onCheckedChange: (checked) => setSingleton(checked),
|
|
1664
|
-
disabled: isSubmitting || isReadOnly
|
|
1665
|
-
}
|
|
1666
|
-
),
|
|
1667
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Label, { htmlFor: "singleton", className: "cursor-pointer", children: "Singleton (only one entry allowed)" })
|
|
1668
|
-
] })
|
|
1669
|
-
] }),
|
|
1670
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
|
|
1671
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1672
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "text-sm font-semibold text-foreground", children: "Fields" }),
|
|
1673
|
-
!isReadOnly && /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
1674
|
-
CmsButton,
|
|
1675
|
-
{
|
|
1676
|
-
type: "button",
|
|
1677
|
-
variant: "secondary",
|
|
1678
|
-
size: "sm",
|
|
1679
|
-
onClick: addField,
|
|
1680
|
-
disabled: isSubmitting,
|
|
1681
|
-
"data-testid": "add-field-button",
|
|
1682
|
-
children: [
|
|
1683
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { className: "size-3.5" }),
|
|
1684
|
-
"Add Field"
|
|
1685
|
-
]
|
|
1686
|
-
}
|
|
1687
|
-
)
|
|
1688
|
-
] }),
|
|
1689
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-2", children: fields.map((field, index) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
1690
|
-
"div",
|
|
1691
|
-
{
|
|
1692
|
-
className: cn(
|
|
1693
|
-
"flex items-center gap-2 rounded-lg border p-2 transition-colors",
|
|
1694
|
-
!isReadOnly && "cursor-pointer hover:bg-muted/50",
|
|
1695
|
-
activeFieldIndex === index && "border-primary bg-primary/5"
|
|
1696
|
-
),
|
|
1697
|
-
onClick: () => {
|
|
1698
|
-
if (!isReadOnly) {
|
|
1699
|
-
setActiveFieldIndex(index);
|
|
1700
|
-
setShowFieldEditor(true);
|
|
1701
|
-
}
|
|
1702
|
-
},
|
|
1703
|
-
"data-testid": `field-item-${index}`,
|
|
1704
|
-
children: [
|
|
1705
|
-
!isReadOnly && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-0.5", children: [
|
|
1706
|
-
index > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1707
|
-
"button",
|
|
1708
|
-
{
|
|
1709
|
-
type: "button",
|
|
1710
|
-
onClick: (e) => {
|
|
1711
|
-
e.stopPropagation();
|
|
1712
|
-
moveField(index, index - 1);
|
|
1713
|
-
},
|
|
1714
|
-
className: "rounded p-0.5 text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
1715
|
-
children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { className: "size-3" })
|
|
1716
|
-
}
|
|
1717
|
-
),
|
|
1718
|
-
index < fields.length - 1 && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1719
|
-
"button",
|
|
1720
|
-
{
|
|
1721
|
-
type: "button",
|
|
1722
|
-
onClick: (e) => {
|
|
1723
|
-
e.stopPropagation();
|
|
1724
|
-
moveField(index, index + 1);
|
|
1725
|
-
},
|
|
1726
|
-
className: "rounded p-0.5 text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
1727
|
-
children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3" })
|
|
1728
|
-
}
|
|
1729
|
-
)
|
|
1730
|
-
] }),
|
|
1731
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex size-8 items-center justify-center rounded bg-muted text-muted-foreground", children: FIELD_TYPE_INFO[field.type].icon }),
|
|
1732
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
1733
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "truncate text-sm font-medium", children: field.label }),
|
|
1734
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
1735
|
-
FIELD_TYPE_INFO[field.type].label,
|
|
1736
|
-
field.required && " *"
|
|
1737
|
-
] })
|
|
1738
|
-
] }),
|
|
1739
|
-
!isReadOnly && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1740
|
-
"button",
|
|
1741
|
-
{
|
|
1742
|
-
type: "button",
|
|
1743
|
-
onClick: (e) => {
|
|
1744
|
-
e.stopPropagation();
|
|
1745
|
-
removeField(index);
|
|
1746
|
-
},
|
|
1747
|
-
disabled: isSubmitting || fields.length === 1,
|
|
1748
|
-
className: "rounded p-1 text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive disabled:opacity-50",
|
|
1749
|
-
children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-4" })
|
|
1750
|
-
}
|
|
1751
|
-
)
|
|
1752
|
-
]
|
|
1753
|
-
},
|
|
1754
|
-
index
|
|
1755
|
-
)) }),
|
|
1756
|
-
!isReadOnly && showFieldEditor && activeField && activeFieldIndex !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
1757
|
-
"div",
|
|
1758
|
-
{
|
|
1759
|
-
className: "rounded-lg border bg-muted/30 p-4",
|
|
1760
|
-
"data-testid": "field-editor",
|
|
1761
|
-
children: [
|
|
1762
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-4 flex items-center justify-between", children: [
|
|
1763
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("h5", { className: "font-medium", children: [
|
|
1764
|
-
"Edit Field: ",
|
|
1765
|
-
activeField.label
|
|
1766
|
-
] }),
|
|
1767
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1768
|
-
"button",
|
|
1769
|
-
{
|
|
1770
|
-
type: "button",
|
|
1771
|
-
onClick: () => {
|
|
1772
|
-
setShowFieldEditor(false);
|
|
1773
|
-
setActiveFieldIndex(null);
|
|
1774
|
-
},
|
|
1775
|
-
className: "rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
1776
|
-
children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-4" })
|
|
1777
|
-
}
|
|
1778
|
-
)
|
|
1779
|
-
] }),
|
|
1780
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
|
|
1781
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
1782
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(Label, { htmlFor: "fieldLabel", children: [
|
|
1783
|
-
"Label ",
|
|
1784
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
|
|
1785
|
-
] }),
|
|
1786
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1787
|
-
Input,
|
|
1788
|
-
{
|
|
1789
|
-
id: "fieldLabel",
|
|
1790
|
-
value: activeField.label,
|
|
1791
|
-
onChange: (e) => updateField(activeFieldIndex, {
|
|
1792
|
-
label: e.target.value,
|
|
1793
|
-
name: machineNameManuallyEdited ? activeField.name : generateMachineName(e.target.value) || activeField.name
|
|
1794
|
-
}),
|
|
1795
|
-
disabled: isSubmitting,
|
|
1796
|
-
"data-testid": "field-label-input"
|
|
1797
|
-
}
|
|
1798
|
-
)
|
|
1799
|
-
] }),
|
|
1800
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
1801
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(Label, { htmlFor: "fieldName", children: [
|
|
1802
|
-
"Name ",
|
|
1803
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
|
|
1804
|
-
] }),
|
|
1805
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1806
|
-
Input,
|
|
1807
|
-
{
|
|
1808
|
-
id: "fieldName",
|
|
1809
|
-
value: activeField.name,
|
|
1810
|
-
onChange: (e) => updateField(activeFieldIndex, {
|
|
1811
|
-
name: e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, "")
|
|
1812
|
-
}),
|
|
1813
|
-
disabled: isSubmitting,
|
|
1814
|
-
"data-testid": "field-name-input"
|
|
1815
|
-
}
|
|
1816
|
-
)
|
|
1817
|
-
] }),
|
|
1818
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
1819
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(Label, { htmlFor: "fieldType", children: [
|
|
1820
|
-
"Type ",
|
|
1821
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
|
|
1822
|
-
] }),
|
|
1823
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
1824
|
-
Select,
|
|
1825
|
-
{
|
|
1826
|
-
value: activeField.type,
|
|
1827
|
-
onValueChange: (value) => updateField(activeFieldIndex, {
|
|
1828
|
-
type: value,
|
|
1829
|
-
options: void 0
|
|
1830
|
-
}),
|
|
1831
|
-
disabled: isSubmitting,
|
|
1832
|
-
children: [
|
|
1833
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { "data-testid": "field-type-select", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, {}) }),
|
|
1834
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(SelectContent, { children: Object.entries(FIELD_TYPE_INFO).map(([type, info]) => /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: type, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1835
|
-
info.icon,
|
|
1836
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: info.label }),
|
|
1837
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground", children: [
|
|
1838
|
-
"- ",
|
|
1839
|
-
info.description
|
|
1840
|
-
] })
|
|
1841
|
-
] }) }, type)) })
|
|
1842
|
-
]
|
|
1843
|
-
}
|
|
1844
|
-
)
|
|
1845
|
-
] }),
|
|
1846
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1847
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1848
|
-
Checkbox,
|
|
1849
|
-
{
|
|
1850
|
-
id: "fieldRequired",
|
|
1851
|
-
checked: activeField.required,
|
|
1852
|
-
onCheckedChange: (checked) => updateField(activeFieldIndex, {
|
|
1853
|
-
required: checked
|
|
1854
|
-
}),
|
|
1855
|
-
disabled: isSubmitting
|
|
1856
|
-
}
|
|
1857
|
-
),
|
|
1858
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Label, { htmlFor: "fieldRequired", className: "cursor-pointer", children: "Required" })
|
|
1859
|
-
] }),
|
|
1860
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
1861
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Label, { htmlFor: "fieldDescription", children: "Help Text" }),
|
|
1862
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1863
|
-
Input,
|
|
1864
|
-
{
|
|
1865
|
-
id: "fieldDescription",
|
|
1866
|
-
value: activeField.description || "",
|
|
1867
|
-
onChange: (e) => updateField(activeFieldIndex, {
|
|
1868
|
-
description: e.target.value || void 0
|
|
1869
|
-
}),
|
|
1870
|
-
placeholder: "Help text shown below the field",
|
|
1871
|
-
disabled: isSubmitting
|
|
1872
|
-
}
|
|
1873
|
-
)
|
|
1874
|
-
] }),
|
|
1875
|
-
(activeField.type === "select" || activeField.type === "multiSelect") && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1876
|
-
SelectOptionsEditor,
|
|
1877
|
-
{
|
|
1878
|
-
options: activeField.options?.options || [],
|
|
1879
|
-
onChange: (options) => updateField(activeFieldIndex, {
|
|
1880
|
-
options: { ...activeField.options, options }
|
|
1881
|
-
}),
|
|
1882
|
-
disabled: isSubmitting
|
|
1883
|
-
}
|
|
1884
|
-
)
|
|
1885
|
-
] })
|
|
1886
|
-
]
|
|
1887
|
-
}
|
|
1888
|
-
)
|
|
1889
|
-
] }),
|
|
1890
|
-
textFields.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
|
|
1891
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "text-sm font-semibold text-foreground", children: "Display Settings" }),
|
|
1892
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
1893
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Label, { htmlFor: "titleField", children: "Title Field" }),
|
|
1894
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
1895
|
-
Select,
|
|
1896
|
-
{
|
|
1897
|
-
value: titleField || "none",
|
|
1898
|
-
onValueChange: (v2) => setTitleField(v2 === "none" ? "" : v2),
|
|
1899
|
-
disabled: isSubmitting,
|
|
1900
|
-
children: [
|
|
1901
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "None" }) }),
|
|
1902
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { children: [
|
|
1903
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "none", children: "None" }),
|
|
1904
|
-
textFields.map((field) => /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: field.name, children: field.label }, field.name))
|
|
1905
|
-
] })
|
|
1906
|
-
]
|
|
1907
|
-
}
|
|
1908
|
-
),
|
|
1909
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "Field to display as the entry title in lists" })
|
|
1910
|
-
] }),
|
|
1911
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
1912
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Label, { htmlFor: "slugField", children: "Slug Field" }),
|
|
1913
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
1914
|
-
Select,
|
|
1915
|
-
{
|
|
1916
|
-
value: slugField || "none",
|
|
1917
|
-
onValueChange: (v2) => setSlugField(v2 === "none" ? "" : v2),
|
|
1918
|
-
disabled: isSubmitting,
|
|
1919
|
-
children: [
|
|
1920
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "None (auto-generate)" }) }),
|
|
1921
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { children: [
|
|
1922
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "none", children: "None (auto-generate)" }),
|
|
1923
|
-
textFields.map((field) => /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: field.name, children: field.label }, field.name))
|
|
1924
|
-
] })
|
|
1925
|
-
]
|
|
1926
|
-
}
|
|
1927
|
-
),
|
|
1928
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "Field to use for generating URL-friendly slugs" })
|
|
1929
|
-
] })
|
|
1930
|
-
] }),
|
|
1931
|
-
submitError && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1932
|
-
"div",
|
|
1933
|
-
{
|
|
1934
|
-
className: "diff-removed rounded-lg border px-3 py-2 text-sm",
|
|
1935
|
-
role: "alert",
|
|
1936
|
-
"data-testid": "submit-error",
|
|
1937
|
-
children: submitError
|
|
1938
|
-
}
|
|
1939
|
-
)
|
|
1940
|
-
] })
|
|
1941
|
-
}
|
|
1942
|
-
),
|
|
1943
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1944
|
-
BreakingChangesWarningDialog,
|
|
1945
|
-
{
|
|
1946
|
-
isOpen: showBreakingWarning,
|
|
1947
|
-
onClose: () => setShowBreakingWarning(false),
|
|
1948
|
-
breakingChanges,
|
|
1949
|
-
onForceUpdate: handleForceUpdate,
|
|
1950
|
-
onCancel: () => {
|
|
1951
|
-
setShowBreakingWarning(false);
|
|
1952
|
-
setBreakingChanges([]);
|
|
1953
|
-
},
|
|
1954
|
-
isLoading: isForceUpdating
|
|
1955
|
-
}
|
|
1956
|
-
)
|
|
1957
|
-
] });
|
|
1958
|
-
}
|
|
1959
|
-
function SelectOptionsEditor({
|
|
1960
|
-
options,
|
|
1961
|
-
onChange,
|
|
1962
|
-
disabled
|
|
1963
|
-
}) {
|
|
1964
|
-
const addOption = () => {
|
|
1965
|
-
onChange([
|
|
1966
|
-
...options,
|
|
1967
|
-
{ value: `option_${options.length + 1}`, label: "" }
|
|
1968
|
-
]);
|
|
1969
|
-
};
|
|
1970
|
-
const removeOption = (index) => {
|
|
1971
|
-
onChange(options.filter((_, i) => i !== index));
|
|
1972
|
-
};
|
|
1973
|
-
const updateOption = (index, updates) => {
|
|
1974
|
-
onChange(
|
|
1975
|
-
options.map((opt, i) => i === index ? { ...opt, ...updates } : opt)
|
|
1976
|
-
);
|
|
1977
|
-
};
|
|
1978
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
1979
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(Label, { children: [
|
|
1980
|
-
"Options ",
|
|
1981
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
|
|
1982
|
-
] }),
|
|
1983
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-2", children: options.map((option, index) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1984
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1985
|
-
Input,
|
|
1986
|
-
{
|
|
1987
|
-
value: option.label,
|
|
1988
|
-
onChange: (e) => {
|
|
1989
|
-
const label = e.target.value;
|
|
1990
|
-
const value = label.toLowerCase().replace(/[^a-z0-9]/g, "_").replace(/^_+|_+$/g, "");
|
|
1991
|
-
updateOption(index, { label, value });
|
|
1992
|
-
},
|
|
1993
|
-
placeholder: "Option label",
|
|
1994
|
-
disabled,
|
|
1995
|
-
className: "flex-1"
|
|
1996
|
-
}
|
|
1997
|
-
),
|
|
1998
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1999
|
-
Input,
|
|
2000
|
-
{
|
|
2001
|
-
value: option.value,
|
|
2002
|
-
onChange: (e) => updateOption(index, {
|
|
2003
|
-
value: e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, "")
|
|
2004
|
-
}),
|
|
2005
|
-
placeholder: "value",
|
|
2006
|
-
disabled,
|
|
2007
|
-
className: "w-32"
|
|
2008
|
-
}
|
|
2009
|
-
),
|
|
2010
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2011
|
-
"button",
|
|
2012
|
-
{
|
|
2013
|
-
type: "button",
|
|
2014
|
-
onClick: () => removeOption(index),
|
|
2015
|
-
disabled,
|
|
2016
|
-
className: "rounded p-1 text-muted-foreground hover:bg-destructive/10 hover:text-destructive",
|
|
2017
|
-
children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-4" })
|
|
2018
|
-
}
|
|
2019
|
-
)
|
|
2020
|
-
] }, index)) }),
|
|
2021
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
2022
|
-
CmsButton,
|
|
2023
|
-
{
|
|
2024
|
-
type: "button",
|
|
2025
|
-
variant: "secondary",
|
|
2026
|
-
size: "sm",
|
|
2027
|
-
onClick: addOption,
|
|
2028
|
-
disabled,
|
|
2029
|
-
children: [
|
|
2030
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { className: "size-3.5" }),
|
|
2031
|
-
"Add Option"
|
|
2032
|
-
]
|
|
2033
|
-
}
|
|
2034
|
-
)
|
|
2035
|
-
] });
|
|
2036
|
-
}
|
|
2037
|
-
function Sidebar() {
|
|
2038
|
-
const routerState = useRouterState();
|
|
2039
|
-
const currentPath = routerState.location.pathname;
|
|
2040
|
-
const config = useAdminConfig();
|
|
2041
|
-
const { navItems, branding, layout } = config;
|
|
2042
|
-
const api2 = useApi();
|
|
2043
|
-
const [isCreateModalOpen, setIsCreateModalOpen] = reactExports.useState(false);
|
|
2044
|
-
const contentTypesResult = useQuery(api2.listContentTypes, {
|
|
2045
|
-
isActive: true
|
|
2046
|
-
});
|
|
2047
|
-
const contentTypes = contentTypesResult?.page ?? [];
|
|
2048
|
-
const isActive = (to, exact) => {
|
|
2049
|
-
if (exact) {
|
|
2050
|
-
return currentPath === to;
|
|
2051
|
-
}
|
|
2052
|
-
return currentPath.startsWith(to);
|
|
2053
|
-
};
|
|
2054
|
-
const isContentActive = currentPath === "/content" || currentPath.startsWith("/entries/type/") || currentPath.startsWith("/entries/new/") || currentPath.startsWith("/entries/");
|
|
2055
|
-
const renderNavItem = (item) => {
|
|
2056
|
-
if (item.id === "content") {
|
|
2057
|
-
return renderContentMenu(item);
|
|
2058
|
-
}
|
|
2059
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
2060
|
-
Link,
|
|
2061
|
-
{
|
|
2062
|
-
to: item.path,
|
|
2063
|
-
className: cn(
|
|
2064
|
-
"flex items-center gap-3 rounded-md px-2 py-2 text-sm font-medium transition-colors",
|
|
2065
|
-
isActive(item.path, item.exact) ? "bg-sidebar-accent text-sidebar-accent-foreground" : "text-sidebar-foreground hover:bg-sidebar-accent/50 hover:text-sidebar-accent-foreground"
|
|
2066
|
-
),
|
|
2067
|
-
children: [
|
|
2068
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { name: item.icon, className: "size-5" }),
|
|
2069
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1", children: item.label }),
|
|
2070
|
-
item.badge && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "rounded-full bg-sidebar-primary px-2 py-0.5 text-xs text-sidebar-primary-foreground", children: item.badge })
|
|
2071
|
-
]
|
|
2072
|
-
},
|
|
2073
|
-
item.id
|
|
2074
|
-
);
|
|
2075
|
-
};
|
|
2076
|
-
const renderContentMenu = (item) => /* @__PURE__ */ jsxRuntimeExports.jsxs(Collapsible, { defaultOpen: isContentActive, children: [
|
|
2077
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
2078
|
-
CollapsibleTrigger,
|
|
2079
|
-
{
|
|
2080
|
-
className: cn(
|
|
2081
|
-
"flex w-full items-center gap-3 rounded-md px-2 py-2 text-sm font-medium transition-colors",
|
|
2082
|
-
isContentActive ? "bg-sidebar-accent text-sidebar-accent-foreground" : "text-sidebar-foreground hover:bg-sidebar-accent/50 hover:text-sidebar-accent-foreground",
|
|
2083
|
-
"group"
|
|
2084
|
-
),
|
|
2085
|
-
children: [
|
|
2086
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { name: item.icon, className: "size-5" }),
|
|
2087
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 text-left", children: item.label }),
|
|
2088
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4 transition-transform duration-200 group-data-[state=open]:rotate-180" })
|
|
2089
|
-
]
|
|
2090
|
-
}
|
|
2091
|
-
),
|
|
2092
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(CollapsibleContent, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ml-5 mt-1 space-y-1 border-l border-sidebar-border pl-3", children: [
|
|
2093
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2094
|
-
Link,
|
|
2095
|
-
{
|
|
2096
|
-
to: "/content",
|
|
2097
|
-
className: cn(
|
|
2098
|
-
"flex items-center gap-2 rounded-md px-2 py-1.5 text-sm transition-colors",
|
|
2099
|
-
currentPath === "/content" ? "bg-sidebar-accent/60 text-sidebar-accent-foreground" : "text-sidebar-foreground/80 hover:bg-sidebar-accent/30 hover:text-sidebar-accent-foreground"
|
|
2100
|
-
),
|
|
2101
|
-
children: "All Entries"
|
|
2102
|
-
}
|
|
2103
|
-
),
|
|
2104
|
-
contentTypes.map((type) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2105
|
-
Link,
|
|
2106
|
-
{
|
|
2107
|
-
to: "/entries/type/$contentTypeId",
|
|
2108
|
-
params: { contentTypeId: type._id },
|
|
2109
|
-
className: cn(
|
|
2110
|
-
"flex items-center gap-2 rounded-md px-2 py-1.5 text-sm transition-colors",
|
|
2111
|
-
currentPath === `/entries/type/${type._id}` ? "bg-sidebar-accent/60 text-sidebar-accent-foreground" : "text-sidebar-foreground/80 hover:bg-sidebar-accent/30 hover:text-sidebar-accent-foreground"
|
|
2112
|
-
),
|
|
2113
|
-
children: type.displayName
|
|
2114
|
-
},
|
|
2115
|
-
type._id
|
|
2116
|
-
)),
|
|
2117
|
-
contentTypes.length === 0 && contentTypesResult !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2118
|
-
"button",
|
|
2119
|
-
{
|
|
2120
|
-
type: "button",
|
|
2121
|
-
onClick: () => setIsCreateModalOpen(true),
|
|
2122
|
-
className: "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm text-sidebar-foreground/60 hover:bg-sidebar-accent/30 hover:text-sidebar-accent-foreground",
|
|
2123
|
-
children: "+ Create content type"
|
|
2124
|
-
}
|
|
2125
|
-
)
|
|
2126
|
-
] }) })
|
|
2127
|
-
] }, item.id);
|
|
2128
|
-
const sidebarWidth = layout.sidebarWidth;
|
|
2129
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
2130
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
2131
|
-
"aside",
|
|
2132
|
-
{
|
|
2133
|
-
className: "fixed inset-y-0 left-0 z-50 flex flex-col border-r border-sidebar-border bg-sidebar",
|
|
2134
|
-
style: { width: sidebarWidth },
|
|
2135
|
-
children: [
|
|
2136
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex h-14 items-center gap-2 border-b border-sidebar-border px-4", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Link, { to: "/", className: "flex items-center gap-2 font-semibold text-sidebar-foreground", children: [
|
|
2137
|
-
branding.logo ? /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: branding.logo, alt: branding.appName, className: "size-8" }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Layers, { className: "size-4" }) }),
|
|
2138
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-base", children: branding.appName })
|
|
2139
|
-
] }) }),
|
|
2140
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("nav", { className: "flex-1 space-y-6 overflow-y-auto p-4", children: [
|
|
2141
|
-
navItems.main.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
|
|
2142
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-2 text-xs font-medium uppercase tracking-wider text-sidebar-foreground/60", children: "Main" }),
|
|
2143
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1 pt-2", children: navItems.main.map(renderNavItem) })
|
|
2144
|
-
] }),
|
|
2145
|
-
navItems.config.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
|
|
2146
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-2 text-xs font-medium uppercase tracking-wider text-sidebar-foreground/60", children: "Configuration" }),
|
|
2147
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1 pt-2", children: navItems.config.map(renderNavItem) })
|
|
2148
|
-
] })
|
|
2149
|
-
] }),
|
|
2150
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border-t border-sidebar-border p-4", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between text-xs text-sidebar-foreground/60", children: [
|
|
2151
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Version" }),
|
|
2152
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono", children: "0.1.0" })
|
|
2153
|
-
] }) })
|
|
2154
|
-
]
|
|
2155
|
-
}
|
|
2156
|
-
),
|
|
2157
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2158
|
-
ContentTypeFormModal,
|
|
2159
|
-
{
|
|
2160
|
-
isOpen: isCreateModalOpen,
|
|
2161
|
-
onClose: () => setIsCreateModalOpen(false)
|
|
2162
|
-
}
|
|
2163
|
-
)
|
|
2164
|
-
] });
|
|
2165
|
-
}
|
|
2166
|
-
function Breadcrumb({ ...props }) {
|
|
2167
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx("nav", { "aria-label": "breadcrumb", "data-slot": "breadcrumb", ...props });
|
|
2168
|
-
}
|
|
2169
|
-
function BreadcrumbList({ className, ...props }) {
|
|
2170
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2171
|
-
"ol",
|
|
2172
|
-
{
|
|
2173
|
-
"data-slot": "breadcrumb-list",
|
|
2174
|
-
className: cn(
|
|
2175
|
-
"text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5",
|
|
2176
|
-
className
|
|
2177
|
-
),
|
|
2178
|
-
...props
|
|
2179
|
-
}
|
|
2180
|
-
);
|
|
2181
|
-
}
|
|
2182
|
-
function BreadcrumbItem({ className, ...props }) {
|
|
2183
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2184
|
-
"li",
|
|
2185
|
-
{
|
|
2186
|
-
"data-slot": "breadcrumb-item",
|
|
2187
|
-
className: cn("inline-flex items-center gap-1.5", className),
|
|
2188
|
-
...props
|
|
2189
|
-
}
|
|
2190
|
-
);
|
|
2191
|
-
}
|
|
2192
|
-
function BreadcrumbLink({
|
|
2193
|
-
asChild,
|
|
2194
|
-
className,
|
|
2195
|
-
...props
|
|
2196
|
-
}) {
|
|
2197
|
-
const Comp = asChild ? Slot : "a";
|
|
2198
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2199
|
-
Comp,
|
|
2200
|
-
{
|
|
2201
|
-
"data-slot": "breadcrumb-link",
|
|
2202
|
-
className: cn("hover:text-foreground transition-colors", className),
|
|
2203
|
-
...props
|
|
2204
|
-
}
|
|
2205
|
-
);
|
|
2206
|
-
}
|
|
2207
|
-
function BreadcrumbPage({ className, ...props }) {
|
|
2208
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2209
|
-
"span",
|
|
2210
|
-
{
|
|
2211
|
-
"data-slot": "breadcrumb-page",
|
|
2212
|
-
role: "link",
|
|
2213
|
-
"aria-disabled": "true",
|
|
2214
|
-
"aria-current": "page",
|
|
2215
|
-
className: cn("text-foreground font-normal", className),
|
|
2216
|
-
...props
|
|
2217
|
-
}
|
|
2218
|
-
);
|
|
2219
|
-
}
|
|
2220
|
-
function BreadcrumbSeparator({
|
|
2221
|
-
children,
|
|
2222
|
-
className,
|
|
2223
|
-
...props
|
|
2224
|
-
}) {
|
|
2225
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2226
|
-
"li",
|
|
2227
|
-
{
|
|
2228
|
-
"data-slot": "breadcrumb-separator",
|
|
2229
|
-
role: "presentation",
|
|
2230
|
-
"aria-hidden": "true",
|
|
2231
|
-
className: cn("[&>svg]:size-3.5", className),
|
|
2232
|
-
...props,
|
|
2233
|
-
children: children ?? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, {})
|
|
2234
|
-
}
|
|
2235
|
-
);
|
|
2236
|
-
}
|
|
2237
|
-
function DropdownMenu({
|
|
2238
|
-
...props
|
|
2239
|
-
}) {
|
|
2240
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(Root2$1, { "data-slot": "dropdown-menu", ...props });
|
|
2241
|
-
}
|
|
2242
|
-
function DropdownMenuTrigger({
|
|
2243
|
-
...props
|
|
2244
|
-
}) {
|
|
2245
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2246
|
-
Trigger$1,
|
|
2247
|
-
{
|
|
2248
|
-
"data-slot": "dropdown-menu-trigger",
|
|
2249
|
-
...props
|
|
2250
|
-
}
|
|
2251
|
-
);
|
|
2252
|
-
}
|
|
2253
|
-
function DropdownMenuContent({
|
|
2254
|
-
className,
|
|
2255
|
-
sideOffset = 4,
|
|
2256
|
-
...props
|
|
2257
|
-
}) {
|
|
2258
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(Portal2, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2259
|
-
Content2$1,
|
|
2260
|
-
{
|
|
2261
|
-
"data-slot": "dropdown-menu-content",
|
|
2262
|
-
sideOffset,
|
|
2263
|
-
className: cn(
|
|
2264
|
-
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
|
|
2265
|
-
className
|
|
2266
|
-
),
|
|
2267
|
-
...props
|
|
2268
|
-
}
|
|
2269
|
-
) });
|
|
2270
|
-
}
|
|
2271
|
-
function DropdownMenuItem({
|
|
2272
|
-
className,
|
|
2273
|
-
inset,
|
|
2274
|
-
variant = "default",
|
|
2275
|
-
...props
|
|
2276
|
-
}) {
|
|
2277
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2278
|
-
Item2,
|
|
2279
|
-
{
|
|
2280
|
-
"data-slot": "dropdown-menu-item",
|
|
2281
|
-
"data-inset": inset,
|
|
2282
|
-
"data-variant": variant,
|
|
2283
|
-
className: cn(
|
|
2284
|
-
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
2285
|
-
className
|
|
2286
|
-
),
|
|
2287
|
-
...props
|
|
2288
|
-
}
|
|
2289
|
-
);
|
|
2290
|
-
}
|
|
2291
|
-
function DropdownMenuLabel({
|
|
2292
|
-
className,
|
|
2293
|
-
inset,
|
|
2294
|
-
...props
|
|
2295
|
-
}) {
|
|
2296
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2297
|
-
Label2,
|
|
2298
|
-
{
|
|
2299
|
-
"data-slot": "dropdown-menu-label",
|
|
2300
|
-
"data-inset": inset,
|
|
2301
|
-
className: cn(
|
|
2302
|
-
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
|
|
2303
|
-
className
|
|
2304
|
-
),
|
|
2305
|
-
...props
|
|
2306
|
-
}
|
|
2307
|
-
);
|
|
2308
|
-
}
|
|
2309
|
-
function DropdownMenuSeparator({
|
|
2310
|
-
className,
|
|
2311
|
-
...props
|
|
2312
|
-
}) {
|
|
2313
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2314
|
-
Separator2,
|
|
2315
|
-
{
|
|
2316
|
-
"data-slot": "dropdown-menu-separator",
|
|
2317
|
-
className: cn("bg-border -mx-1 my-1 h-px", className),
|
|
2318
|
-
...props
|
|
2319
|
-
}
|
|
2320
|
-
);
|
|
2321
|
-
}
|
|
2322
|
-
function Avatar({
|
|
2323
|
-
className,
|
|
2324
|
-
...props
|
|
2325
|
-
}) {
|
|
2326
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2327
|
-
Root,
|
|
2328
|
-
{
|
|
2329
|
-
"data-slot": "avatar",
|
|
2330
|
-
className: cn(
|
|
2331
|
-
"relative flex size-8 shrink-0 overflow-hidden rounded-full",
|
|
2332
|
-
className
|
|
2333
|
-
),
|
|
2334
|
-
...props
|
|
2335
|
-
}
|
|
2336
|
-
);
|
|
2337
|
-
}
|
|
2338
|
-
function AvatarImage({
|
|
2339
|
-
className,
|
|
2340
|
-
...props
|
|
2341
|
-
}) {
|
|
2342
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2343
|
-
Image,
|
|
2344
|
-
{
|
|
2345
|
-
"data-slot": "avatar-image",
|
|
2346
|
-
className: cn("aspect-square size-full", className),
|
|
2347
|
-
...props
|
|
2348
|
-
}
|
|
2349
|
-
);
|
|
2350
|
-
}
|
|
2351
|
-
function AvatarFallback({
|
|
2352
|
-
className,
|
|
2353
|
-
...props
|
|
2354
|
-
}) {
|
|
2355
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2356
|
-
Fallback,
|
|
2357
|
-
{
|
|
2358
|
-
"data-slot": "avatar-fallback",
|
|
2359
|
-
className: cn(
|
|
2360
|
-
"bg-muted flex size-full items-center justify-center rounded-full",
|
|
2361
|
-
className
|
|
2362
|
-
),
|
|
2363
|
-
...props
|
|
2364
|
-
}
|
|
2365
|
-
);
|
|
2366
|
-
}
|
|
2367
|
-
function Popover({
|
|
2368
|
-
...props
|
|
2369
|
-
}) {
|
|
2370
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(Root2, { "data-slot": "popover", ...props });
|
|
2371
|
-
}
|
|
2372
|
-
function PopoverTrigger({
|
|
2373
|
-
...props
|
|
2374
|
-
}) {
|
|
2375
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(Trigger, { "data-slot": "popover-trigger", ...props });
|
|
2376
|
-
}
|
|
2377
|
-
function PopoverContent({
|
|
2378
|
-
className,
|
|
2379
|
-
align = "center",
|
|
2380
|
-
sideOffset = 4,
|
|
2381
|
-
...props
|
|
2382
|
-
}) {
|
|
2383
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(Portal, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2384
|
-
Content2,
|
|
2385
|
-
{
|
|
2386
|
-
"data-slot": "popover-content",
|
|
2387
|
-
align,
|
|
2388
|
-
sideOffset,
|
|
2389
|
-
className: cn(
|
|
2390
|
-
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
|
|
2391
|
-
className
|
|
2392
|
-
),
|
|
2393
|
-
...props
|
|
2394
|
-
}
|
|
2395
|
-
) });
|
|
2396
|
-
}
|
|
2397
|
-
const KEYBOARD_SHORTCUTS = [
|
|
2398
|
-
{ keys: ["⌘", "S"], description: "Save entry" },
|
|
2399
|
-
{ keys: ["⌘", "⇧", "P"], description: "Publish entry" },
|
|
2400
|
-
{ keys: ["⌘", "K"], description: "Quick search" },
|
|
2401
|
-
{ keys: ["Esc"], description: "Close modal/panel" }
|
|
2402
|
-
];
|
|
2403
|
-
const HELP_RESOURCES = [
|
|
2404
|
-
{ label: "Documentation", url: "https://docs.convex.dev", icon: Book },
|
|
2405
|
-
{ label: "API Reference", url: "https://docs.convex.dev/api", icon: Code },
|
|
2406
|
-
{ label: "Community Discord", url: "https://discord.gg/convex", icon: MessageSquare }
|
|
2407
|
-
];
|
|
2408
|
-
const routeLabels = {
|
|
2409
|
-
"/": "Dashboard",
|
|
2410
|
-
"/content": "Content",
|
|
2411
|
-
"/media": "Media Library",
|
|
2412
|
-
"/content-types": "Content Types",
|
|
2413
|
-
"/settings": "Settings",
|
|
2414
|
-
"/taxonomies": "Taxonomies",
|
|
2415
|
-
"/trash": "Trash",
|
|
2416
|
-
"/entries": "Entries",
|
|
2417
|
-
"/entries/type": "Content Types",
|
|
2418
|
-
"/entries/new": "New Entry"
|
|
2419
|
-
};
|
|
2420
|
-
function getBreadcrumbs(pathname, appName, overrides) {
|
|
2421
|
-
const breadcrumbs = [{ label: appName, to: "/" }];
|
|
2422
|
-
const decodedPathname = decodeURIComponent(pathname);
|
|
2423
|
-
if (decodedPathname === "/") {
|
|
2424
|
-
return breadcrumbs;
|
|
2425
|
-
}
|
|
2426
|
-
const segments = decodedPathname.split("/").filter(Boolean);
|
|
2427
|
-
let currentPath = "";
|
|
2428
|
-
segments.forEach((segment, index) => {
|
|
2429
|
-
currentPath += `/${segment}`;
|
|
2430
|
-
const isLast = index === segments.length - 1;
|
|
2431
|
-
let label = overrides.get(currentPath) ?? routeLabels[currentPath];
|
|
2432
|
-
if (!label) {
|
|
2433
|
-
label = segment.charAt(0).toUpperCase() + segment.slice(1).replace(/-/g, " ");
|
|
2434
|
-
}
|
|
2435
|
-
if (isLast) {
|
|
2436
|
-
breadcrumbs.push({ label });
|
|
2437
|
-
} else {
|
|
2438
|
-
breadcrumbs.push({ label, to: currentPath });
|
|
2439
|
-
}
|
|
2440
|
-
});
|
|
2441
|
-
return breadcrumbs;
|
|
2442
|
-
}
|
|
2443
|
-
function Header() {
|
|
2444
|
-
const routerState = useRouterState();
|
|
2445
|
-
const navigate = useNavigate();
|
|
2446
|
-
const { branding } = useAdminConfig();
|
|
2447
|
-
let overrides = /* @__PURE__ */ new Map();
|
|
2448
|
-
try {
|
|
2449
|
-
const breadcrumbContext = useBreadcrumbContext();
|
|
2450
|
-
overrides = breadcrumbContext.overrides;
|
|
2451
|
-
} catch {
|
|
2452
|
-
}
|
|
2453
|
-
const breadcrumbs = getBreadcrumbs(routerState.location.pathname, branding.appName, overrides);
|
|
2454
|
-
let user = null;
|
|
2455
|
-
let role = null;
|
|
2456
|
-
let logout = async () => {
|
|
2457
|
-
};
|
|
2458
|
-
let isAuthenticated = false;
|
|
2459
|
-
try {
|
|
2460
|
-
const auth = useAuth();
|
|
2461
|
-
user = auth.user;
|
|
2462
|
-
role = auth.role;
|
|
2463
|
-
logout = auth.logout;
|
|
2464
|
-
isAuthenticated = auth.isAuthenticated;
|
|
2465
|
-
} catch {
|
|
2466
|
-
}
|
|
2467
|
-
const roleDefinition = role ? getRole(role) : null;
|
|
2468
|
-
const roleDisplayName = roleDefinition?.displayName ?? role ?? "No Role";
|
|
2469
|
-
const userDisplayName = user?.name ?? user?.email ?? "User";
|
|
2470
|
-
const getInitials = (name) => {
|
|
2471
|
-
const parts = name.split(" ");
|
|
2472
|
-
if (parts.length >= 2) {
|
|
2473
|
-
return `${parts[0][0]}${parts[1][0]}`.toUpperCase();
|
|
2474
|
-
}
|
|
2475
|
-
return name.slice(0, 2).toUpperCase();
|
|
2476
|
-
};
|
|
2477
|
-
const userInitials = user?.name ? getInitials(user.name) : "U";
|
|
2478
|
-
const handleLogout = async () => {
|
|
2479
|
-
await logout();
|
|
2480
|
-
};
|
|
2481
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("header", { className: "sticky top-0 z-40 flex h-14 items-center justify-between border-b border-border bg-background/95 px-6 backdrop-blur supports-[backdrop-filter]:bg-background/60", children: [
|
|
2482
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Breadcrumb, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(BreadcrumbList, { children: breadcrumbs.map((crumb, index) => /* @__PURE__ */ jsxRuntimeExports.jsxs(reactExports.Fragment, { children: [
|
|
2483
|
-
index > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(BreadcrumbSeparator, {}),
|
|
2484
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(BreadcrumbItem, { children: crumb.to ? /* @__PURE__ */ jsxRuntimeExports.jsx(BreadcrumbLink, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Link, { to: crumb.to, className: "flex items-center gap-1.5", children: [
|
|
2485
|
-
index === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(House, { className: "size-3.5" }),
|
|
2486
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: crumb.label })
|
|
2487
|
-
] }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx(BreadcrumbPage, { children: crumb.label }) })
|
|
2488
|
-
] }, index)) }) }),
|
|
2489
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
2490
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(Popover, { children: [
|
|
2491
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { variant: "ghost", size: "icon", className: "size-9", children: [
|
|
2492
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Bell, { className: "size-4" }),
|
|
2493
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sr-only", children: "Notifications" })
|
|
2494
|
-
] }) }),
|
|
2495
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(PopoverContent, { align: "end", className: "w-80", children: [
|
|
2496
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-between pb-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "font-medium", children: "Notifications" }) }),
|
|
2497
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center py-8 text-center", children: [
|
|
2498
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Bell, { className: "size-8 text-muted-foreground/50" }),
|
|
2499
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-2 text-sm font-medium", children: "No notifications yet" }),
|
|
2500
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "You're all caught up!" })
|
|
2501
|
-
] })
|
|
2502
|
-
] })
|
|
2503
|
-
] }),
|
|
2504
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(Popover, { children: [
|
|
2505
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { variant: "ghost", size: "icon", className: "size-9", children: [
|
|
2506
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(CircleQuestionMark, { className: "size-4" }),
|
|
2507
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sr-only", children: "Help" })
|
|
2508
|
-
] }) }),
|
|
2509
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(PopoverContent, { align: "end", className: "w-72", children: [
|
|
2510
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pb-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "font-medium", children: "Help & Resources" }) }),
|
|
2511
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
|
|
2512
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
|
|
2513
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("h5", { className: "mb-2 text-xs font-medium text-muted-foreground", children: "Keyboard Shortcuts" }),
|
|
2514
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1", children: KEYBOARD_SHORTCUTS.map((shortcut, index) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between text-sm", children: [
|
|
2515
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground", children: shortcut.description }),
|
|
2516
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex gap-0.5", children: shortcut.keys.map((key, keyIndex) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2517
|
-
"kbd",
|
|
2518
|
-
{
|
|
2519
|
-
className: "rounded border border-border bg-muted px-1.5 py-0.5 font-mono text-xs",
|
|
2520
|
-
children: key
|
|
2521
|
-
},
|
|
2522
|
-
keyIndex
|
|
2523
|
-
)) })
|
|
2524
|
-
] }, index)) })
|
|
2525
|
-
] }),
|
|
2526
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
|
|
2527
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("h5", { className: "mb-2 text-xs font-medium text-muted-foreground", children: "Resources" }),
|
|
2528
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1", children: HELP_RESOURCES.map((resource) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
2529
|
-
"a",
|
|
2530
|
-
{
|
|
2531
|
-
href: resource.url,
|
|
2532
|
-
target: "_blank",
|
|
2533
|
-
rel: "noopener noreferrer",
|
|
2534
|
-
className: "flex items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent",
|
|
2535
|
-
children: [
|
|
2536
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(resource.icon, { className: "size-4 text-muted-foreground" }),
|
|
2537
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1", children: resource.label }),
|
|
2538
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(ExternalLink, { className: "size-3 text-muted-foreground" })
|
|
2539
|
-
]
|
|
2540
|
-
},
|
|
2541
|
-
resource.label
|
|
2542
|
-
)) })
|
|
2543
|
-
] })
|
|
2544
|
-
] })
|
|
2545
|
-
] })
|
|
2546
|
-
] }),
|
|
2547
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(DropdownMenu, { children: [
|
|
2548
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { variant: "ghost", className: "h-9 gap-2 pl-2 pr-3", children: [
|
|
2549
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(Avatar, { className: "size-6", children: [
|
|
2550
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(AvatarImage, { src: user?.avatarUrl, alt: userDisplayName }),
|
|
2551
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(AvatarFallback, { className: "text-xs", children: userInitials })
|
|
2552
|
-
] }),
|
|
2553
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-medium", children: userDisplayName }),
|
|
2554
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3.5 text-muted-foreground" })
|
|
2555
|
-
] }) }),
|
|
2556
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(DropdownMenuContent, { align: "end", className: "w-56", children: [
|
|
2557
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(DropdownMenuLabel, { className: "font-normal", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col space-y-1", children: [
|
|
2558
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-medium", children: userDisplayName }),
|
|
2559
|
-
user?.email && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: user.email }),
|
|
2560
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: roleDisplayName })
|
|
2561
|
-
] }) }),
|
|
2562
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(DropdownMenuSeparator, {}),
|
|
2563
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(DropdownMenuItem, { onClick: () => navigate({ to: "/settings" }), children: [
|
|
2564
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(User, { className: "mr-2 size-4" }),
|
|
2565
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Profile & Settings" })
|
|
2566
|
-
] }),
|
|
2567
|
-
isAuthenticated && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
2568
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(DropdownMenuSeparator, {}),
|
|
2569
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(DropdownMenuItem, { onClick: handleLogout, className: "text-destructive focus:text-destructive", children: [
|
|
2570
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(LogOut, { className: "mr-2 size-4" }),
|
|
2571
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Logout" })
|
|
2572
|
-
] })
|
|
2573
|
-
] })
|
|
2574
|
-
] })
|
|
2575
|
-
] })
|
|
2576
|
-
] })
|
|
2577
|
-
] });
|
|
2578
|
-
}
|
|
2579
|
-
function AdminLayout({ children }) {
|
|
2580
|
-
const { layout } = useAdminConfig();
|
|
2581
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-h-screen bg-background", children: [
|
|
2582
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Sidebar, {}),
|
|
2583
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-1 flex-col", style: { marginLeft: layout.sidebarWidth }, children: [
|
|
2584
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Header, {}),
|
|
2585
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("main", { className: "flex-1 overflow-auto p-6", children })
|
|
2586
|
-
] })
|
|
2587
|
-
] });
|
|
2588
|
-
}
|
|
2589
|
-
function DefaultLoading() {
|
|
2590
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "route-guard route-guard--loading bg-background flex items-center justify-center min-h-screen", children: /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "h-8 w-8 animate-spin text-muted-foreground" }) });
|
|
2591
|
-
}
|
|
2592
|
-
function DefaultUnauthenticated() {
|
|
2593
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "route-guard route-guard--unauthenticated", children: [
|
|
2594
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "route-guard-icon", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
2595
|
-
"svg",
|
|
2596
|
-
{
|
|
2597
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
2598
|
-
width: "48",
|
|
2599
|
-
height: "48",
|
|
2600
|
-
viewBox: "0 0 24 24",
|
|
2601
|
-
fill: "none",
|
|
2602
|
-
stroke: "currentColor",
|
|
2603
|
-
strokeWidth: "2",
|
|
2604
|
-
strokeLinecap: "round",
|
|
2605
|
-
strokeLinejoin: "round",
|
|
2606
|
-
children: [
|
|
2607
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "3", y: "11", width: "18", height: "11", rx: "2", ry: "2" }),
|
|
2608
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M7 11V7a5 5 0 0 1 10 0v4" })
|
|
2609
|
-
]
|
|
2610
|
-
}
|
|
2611
|
-
) }),
|
|
2612
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("h2", { children: "Authentication Required" }),
|
|
2613
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "Please log in to access the admin panel." })
|
|
2614
|
-
] });
|
|
2615
|
-
}
|
|
2616
|
-
function DefaultUnauthorized({
|
|
2617
|
-
requiredRole,
|
|
2618
|
-
requiredPermission
|
|
2619
|
-
}) {
|
|
2620
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "route-guard route-guard--unauthorized", children: [
|
|
2621
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "route-guard-icon", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
2622
|
-
"svg",
|
|
2623
|
-
{
|
|
2624
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
2625
|
-
width: "48",
|
|
2626
|
-
height: "48",
|
|
2627
|
-
viewBox: "0 0 24 24",
|
|
2628
|
-
fill: "none",
|
|
2629
|
-
stroke: "currentColor",
|
|
2630
|
-
strokeWidth: "2",
|
|
2631
|
-
strokeLinecap: "round",
|
|
2632
|
-
strokeLinejoin: "round",
|
|
2633
|
-
children: [
|
|
2634
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
2635
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "4.93", y1: "4.93", x2: "19.07", y2: "19.07" })
|
|
2636
|
-
]
|
|
2637
|
-
}
|
|
2638
|
-
) }),
|
|
2639
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("h2", { children: "Access Denied" }),
|
|
2640
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "You don't have permission to access this page." }),
|
|
2641
|
-
requiredRole && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "route-guard-detail", children: [
|
|
2642
|
-
"Required role: ",
|
|
2643
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: requiredRole })
|
|
2644
|
-
] }),
|
|
2645
|
-
requiredPermission && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "route-guard-detail", children: [
|
|
2646
|
-
"Required permission:",
|
|
2647
|
-
" ",
|
|
2648
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("strong", { children: [
|
|
2649
|
-
requiredPermission.action,
|
|
2650
|
-
" on ",
|
|
2651
|
-
requiredPermission.resource
|
|
2652
|
-
] })
|
|
2653
|
-
] })
|
|
2654
|
-
] });
|
|
2655
|
-
}
|
|
2656
|
-
function DefaultError({ error }) {
|
|
2657
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "route-guard route-guard--error", children: [
|
|
2658
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "route-guard-icon", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
2659
|
-
"svg",
|
|
2660
|
-
{
|
|
2661
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
2662
|
-
width: "48",
|
|
2663
|
-
height: "48",
|
|
2664
|
-
viewBox: "0 0 24 24",
|
|
2665
|
-
fill: "none",
|
|
2666
|
-
stroke: "currentColor",
|
|
2667
|
-
strokeWidth: "2",
|
|
2668
|
-
strokeLinecap: "round",
|
|
2669
|
-
strokeLinejoin: "round",
|
|
2670
|
-
children: [
|
|
2671
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
2672
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
|
|
2673
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
|
|
2674
|
-
]
|
|
2675
|
-
}
|
|
2676
|
-
) }),
|
|
2677
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("h2", { children: "Authentication Error" }),
|
|
2678
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: error })
|
|
2679
|
-
] });
|
|
2680
|
-
}
|
|
2681
|
-
function RouteGuard({
|
|
2682
|
-
children,
|
|
2683
|
-
requiredPermission,
|
|
2684
|
-
requiredRole,
|
|
2685
|
-
loadingComponent,
|
|
2686
|
-
unauthenticatedComponent,
|
|
2687
|
-
unauthorizedComponent,
|
|
2688
|
-
onUnauthenticated,
|
|
2689
|
-
onUnauthorized
|
|
2690
|
-
}) {
|
|
2691
|
-
const { authState, role, checkPermission, error } = useAuth();
|
|
2692
|
-
if (authState === "loading") {
|
|
2693
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: loadingComponent ?? /* @__PURE__ */ jsxRuntimeExports.jsx(DefaultLoading, {}) });
|
|
2694
|
-
}
|
|
2695
|
-
if (authState === "error") {
|
|
2696
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(DefaultError, { error: error ?? "An error occurred" });
|
|
2697
|
-
}
|
|
2698
|
-
if (authState === "unauthenticated") {
|
|
2699
|
-
onUnauthenticated?.();
|
|
2700
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: unauthenticatedComponent ?? /* @__PURE__ */ jsxRuntimeExports.jsx(DefaultUnauthenticated, {}) });
|
|
2701
|
-
}
|
|
2702
|
-
if (!role) {
|
|
2703
|
-
onUnauthorized?.();
|
|
2704
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: unauthorizedComponent ?? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2705
|
-
DefaultUnauthorized,
|
|
2706
|
-
{
|
|
2707
|
-
requiredRole,
|
|
2708
|
-
requiredPermission
|
|
2709
|
-
}
|
|
2710
|
-
) });
|
|
2711
|
-
}
|
|
2712
|
-
if (requiredRole && role !== requiredRole) {
|
|
2713
|
-
onUnauthorized?.();
|
|
2714
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: unauthorizedComponent ?? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2715
|
-
DefaultUnauthorized,
|
|
2716
|
-
{
|
|
2717
|
-
requiredRole,
|
|
2718
|
-
requiredPermission
|
|
2719
|
-
}
|
|
2720
|
-
) });
|
|
2721
|
-
}
|
|
2722
|
-
if (requiredPermission && !checkPermission(requiredPermission)) {
|
|
2723
|
-
onUnauthorized?.();
|
|
2724
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: unauthorizedComponent ?? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2725
|
-
DefaultUnauthorized,
|
|
2726
|
-
{
|
|
2727
|
-
requiredRole,
|
|
2728
|
-
requiredPermission
|
|
2729
|
-
}
|
|
2730
|
-
) });
|
|
2731
|
-
}
|
|
2732
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children });
|
|
2733
|
-
}
|
|
2734
|
-
const createSsrRpc = (functionId, importer) => {
|
|
2735
|
-
const url = "/_serverFn/" + functionId;
|
|
2736
|
-
const serverFnMeta = { id: functionId };
|
|
2737
|
-
const fn = async (...args) => {
|
|
2738
|
-
const serverFn = await getServerFnById(functionId);
|
|
2739
|
-
return serverFn(...args);
|
|
2740
|
-
};
|
|
2741
|
-
return Object.assign(fn, {
|
|
2742
|
-
url,
|
|
2743
|
-
serverFnMeta,
|
|
2744
|
-
[TSS_SERVER_FUNCTION]: true
|
|
2745
|
-
});
|
|
2746
|
-
};
|
|
2747
|
-
const getServerConfig = createServerFn({
|
|
2748
|
-
method: "GET"
|
|
2749
|
-
}).handler(createSsrRpc("dff4e5b7f7b29b6a323200a2df0a5335d739cf583e83c9e514598af6b5ade819"));
|
|
2750
|
-
const api = anyApi;
|
|
2751
|
-
componentsGeneric();
|
|
2752
|
-
const mockGetUser = () => {
|
|
2753
|
-
return {
|
|
2754
|
-
id: "mock_user_123",
|
|
2755
|
-
name: "Demo Admin",
|
|
2756
|
-
email: "admin@example.com"
|
|
2757
|
-
};
|
|
2758
|
-
};
|
|
2759
|
-
const mockGetUserRole = () => {
|
|
2760
|
-
return "admin";
|
|
2761
|
-
};
|
|
2762
|
-
const mockLogout = () => {
|
|
2763
|
-
console.log("Logout called (mock mode)");
|
|
2764
|
-
};
|
|
2765
|
-
const noAuthGetUser = () => null;
|
|
2766
|
-
const noAuthGetUserRole = () => null;
|
|
2767
|
-
const noAuthLogout = () => {
|
|
2768
|
-
};
|
|
2769
|
-
function getAuthConfig(authMode) {
|
|
2770
|
-
switch (authMode) {
|
|
2771
|
-
case "mock":
|
|
2772
|
-
case "demo":
|
|
2773
|
-
return {
|
|
2774
|
-
getUser: mockGetUser,
|
|
2775
|
-
getUserRole: mockGetUserRole,
|
|
2776
|
-
onLogout: mockLogout
|
|
2777
|
-
};
|
|
2778
|
-
case "none":
|
|
2779
|
-
case "disabled":
|
|
2780
|
-
return {
|
|
2781
|
-
getUser: noAuthGetUser,
|
|
2782
|
-
getUserRole: noAuthGetUserRole,
|
|
2783
|
-
onLogout: noAuthLogout
|
|
2784
|
-
};
|
|
2785
|
-
default:
|
|
2786
|
-
return {
|
|
2787
|
-
getUser: mockGetUser,
|
|
2788
|
-
getUserRole: mockGetUserRole,
|
|
2789
|
-
onLogout: mockLogout
|
|
2790
|
-
};
|
|
2791
|
-
}
|
|
2792
|
-
}
|
|
2793
|
-
const Route$a = createRootRoute({
|
|
2794
|
-
head: () => ({
|
|
2795
|
-
meta: [
|
|
2796
|
-
{
|
|
2797
|
-
charSet: "utf-8"
|
|
2798
|
-
},
|
|
2799
|
-
{
|
|
2800
|
-
name: "viewport",
|
|
2801
|
-
content: "width=device-width, initial-scale=1"
|
|
2802
|
-
},
|
|
2803
|
-
{
|
|
2804
|
-
title: "Convex CMS Admin"
|
|
2805
|
-
},
|
|
2806
|
-
{
|
|
2807
|
-
name: "description",
|
|
2808
|
-
content: "Admin interface for Convex CMS - manage content, media, and publishing workflows"
|
|
2809
|
-
}
|
|
2810
|
-
],
|
|
2811
|
-
links: [
|
|
2812
|
-
{ rel: "stylesheet", href: globalsCss },
|
|
2813
|
-
{ rel: "icon", href: "/favicon.ico" }
|
|
2814
|
-
]
|
|
2815
|
-
}),
|
|
2816
|
-
// Load server config at route initialization
|
|
2817
|
-
loader: async () => {
|
|
2818
|
-
const config = await getServerConfig();
|
|
2819
|
-
return { config };
|
|
2820
|
-
},
|
|
2821
|
-
component: RootComponent
|
|
2822
|
-
});
|
|
2823
|
-
function RootComponent() {
|
|
2824
|
-
const { config } = Route$a.useLoaderData();
|
|
2825
|
-
const authConfig = reactExports.useMemo(() => getAuthConfig(config.authMode), [config.authMode]);
|
|
2826
|
-
const adminConfig = reactExports.useMemo(
|
|
2827
|
-
() => resolveAdminConfig(config.adminConfig),
|
|
2828
|
-
[config.adminConfig]
|
|
2829
|
-
);
|
|
2830
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(RootDocument, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(ThemeProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(BreadcrumbProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(ConvexProviderWrapper, { config, adminConfig, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2831
|
-
AuthProvider,
|
|
2832
|
-
{
|
|
2833
|
-
getUser: authConfig.getUser,
|
|
2834
|
-
getUserRole: authConfig.getUserRole,
|
|
2835
|
-
onLogout: authConfig.onLogout,
|
|
2836
|
-
children: /* @__PURE__ */ jsxRuntimeExports.jsx(RouteGuard, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(AdminLayout, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(Outlet, {}) }) })
|
|
2837
|
-
}
|
|
2838
|
-
) }) }) }) });
|
|
2839
|
-
}
|
|
2840
|
-
function ConvexProviderWrapper({
|
|
2841
|
-
children,
|
|
2842
|
-
config,
|
|
2843
|
-
adminConfig
|
|
2844
|
-
}) {
|
|
2845
|
-
const convex = reactExports.useMemo(() => {
|
|
2846
|
-
if (!config.convexUrl) return null;
|
|
2847
|
-
return new ConvexReactClient(config.convexUrl);
|
|
2848
|
-
}, [config.convexUrl]);
|
|
2849
|
-
if (!convex) {
|
|
2850
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex min-h-screen items-center justify-center bg-background p-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "diff-modified max-w-lg space-y-4 rounded-lg border p-6 text-center", children: [
|
|
2851
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-xl font-semibold text-diff-modified", children: "Convex Configuration Required" }),
|
|
2852
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-diff-modified-foreground", children: "Please provide a Convex deployment URL to connect to your backend." }),
|
|
2853
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2 text-left text-sm text-diff-modified-foreground", children: [
|
|
2854
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "font-medium", children: "Options:" }),
|
|
2855
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("ul", { className: "list-inside list-disc space-y-1", children: [
|
|
2856
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("li", { children: [
|
|
2857
|
-
"Run with URL:",
|
|
2858
|
-
" ",
|
|
2859
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "rounded bg-diff-modified-bg/50 px-1", children: "npx convex-cms admin --url YOUR_URL" })
|
|
2860
|
-
] }),
|
|
2861
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("li", { children: [
|
|
2862
|
-
"Set environment variable:",
|
|
2863
|
-
" ",
|
|
2864
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "rounded bg-diff-modified-bg/50 px-1", children: "CONVEX_URL=YOUR_URL" })
|
|
2865
|
-
] }),
|
|
2866
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("li", { children: [
|
|
2867
|
-
"Add to",
|
|
2868
|
-
" ",
|
|
2869
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "rounded bg-diff-modified-bg/50 px-1", children: ".env.local" }),
|
|
2870
|
-
":",
|
|
2871
|
-
" ",
|
|
2872
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "rounded bg-diff-modified-bg/50 px-1", children: "CONVEX_URL=YOUR_URL" })
|
|
2873
|
-
] })
|
|
2874
|
-
] })
|
|
2875
|
-
] }),
|
|
2876
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-sm text-diff-modified-foreground", children: [
|
|
2877
|
-
"Run",
|
|
2878
|
-
" ",
|
|
2879
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "rounded bg-diff-modified-bg/50 px-1", children: "npx convex dev" }),
|
|
2880
|
-
" to start Convex and get your URL."
|
|
2881
|
-
] })
|
|
2882
|
-
] }) });
|
|
2883
|
-
}
|
|
2884
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(ConvexProvider, { client: convex, children: /* @__PURE__ */ jsxRuntimeExports.jsx(ApiProvider, { api: api.admin, children: /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsConfigProvider, { baseConfig: adminConfig, children }) }) });
|
|
2885
|
-
}
|
|
2886
|
-
function RootDocument({ children }) {
|
|
2887
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("html", { lang: "en", children: [
|
|
2888
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("head", { children: /* @__PURE__ */ jsxRuntimeExports.jsx(HeadContent, {}) }),
|
|
2889
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("body", { children: [
|
|
2890
|
-
children,
|
|
2891
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Scripts, {})
|
|
2892
|
-
] })
|
|
2893
|
-
] });
|
|
2894
|
-
}
|
|
2895
|
-
const $$splitComponentImporter$9 = () => import("./trash-JAzYGh7A.mjs");
|
|
2896
|
-
const Route$9 = createFileRoute("/trash")({
|
|
2897
|
-
component: lazyRouteComponent($$splitComponentImporter$9, "component")
|
|
2898
|
-
});
|
|
2899
|
-
const $$splitComponentImporter$8 = () => import("./taxonomies-BbBNx260.mjs");
|
|
2900
|
-
const Route$8 = createFileRoute("/taxonomies")({
|
|
2901
|
-
component: lazyRouteComponent($$splitComponentImporter$8, "component")
|
|
2902
|
-
});
|
|
2903
|
-
const $$splitComponentImporter$7 = () => import("./settings-KVJNe0GM.mjs");
|
|
2904
|
-
const Route$7 = createFileRoute("/settings")({
|
|
2905
|
-
component: lazyRouteComponent($$splitComponentImporter$7, "component")
|
|
2906
|
-
});
|
|
2907
|
-
const $$splitComponentImporter$6 = () => import("./media-MpjxOZL8.mjs");
|
|
2908
|
-
const Route$6 = createFileRoute("/media")({
|
|
2909
|
-
component: lazyRouteComponent($$splitComponentImporter$6, "component")
|
|
2910
|
-
});
|
|
2911
|
-
const $$splitComponentImporter$5 = () => import("./content-types-BzgRcS8K.mjs");
|
|
2912
|
-
const Route$5 = createFileRoute("/content-types")({
|
|
2913
|
-
component: lazyRouteComponent($$splitComponentImporter$5, "component")
|
|
2914
|
-
});
|
|
2915
|
-
const $$splitComponentImporter$4 = () => import("./content-DKRI-YqL.mjs");
|
|
2916
|
-
const Route$4 = createFileRoute("/content")({
|
|
2917
|
-
component: lazyRouteComponent($$splitComponentImporter$4, "component")
|
|
2918
|
-
});
|
|
2919
|
-
const $$splitComponentImporter$3 = () => import("./index-BPf6_agY.mjs");
|
|
2920
|
-
const Route$3 = createFileRoute("/")({
|
|
2921
|
-
component: lazyRouteComponent($$splitComponentImporter$3, "component")
|
|
2922
|
-
});
|
|
2923
|
-
const $$splitComponentImporter$2 = () => import("./_entryId-B5xoXoJf.mjs");
|
|
2924
|
-
const Route$2 = createFileRoute("/entries/$entryId")({
|
|
2925
|
-
component: lazyRouteComponent($$splitComponentImporter$2, "component")
|
|
2926
|
-
});
|
|
2927
|
-
const $$splitComponentImporter$1 = () => import("./_contentTypeId-BWEbjqxY.mjs");
|
|
2928
|
-
const Route$1 = createFileRoute("/entries/type/$contentTypeId")({
|
|
2929
|
-
component: lazyRouteComponent($$splitComponentImporter$1, "component")
|
|
2930
|
-
});
|
|
2931
|
-
const $$splitComponentImporter = () => import("./new._contentTypeId-DSb4qR9j.mjs");
|
|
2932
|
-
const Route = createFileRoute("/entries/new/$contentTypeId")({
|
|
2933
|
-
component: lazyRouteComponent($$splitComponentImporter, "component")
|
|
2934
|
-
});
|
|
2935
|
-
const TrashRoute = Route$9.update({
|
|
2936
|
-
id: "/trash",
|
|
2937
|
-
path: "/trash",
|
|
2938
|
-
getParentRoute: () => Route$a
|
|
2939
|
-
});
|
|
2940
|
-
const TaxonomiesRoute = Route$8.update({
|
|
2941
|
-
id: "/taxonomies",
|
|
2942
|
-
path: "/taxonomies",
|
|
2943
|
-
getParentRoute: () => Route$a
|
|
2944
|
-
});
|
|
2945
|
-
const SettingsRoute = Route$7.update({
|
|
2946
|
-
id: "/settings",
|
|
2947
|
-
path: "/settings",
|
|
2948
|
-
getParentRoute: () => Route$a
|
|
2949
|
-
});
|
|
2950
|
-
const MediaRoute = Route$6.update({
|
|
2951
|
-
id: "/media",
|
|
2952
|
-
path: "/media",
|
|
2953
|
-
getParentRoute: () => Route$a
|
|
2954
|
-
});
|
|
2955
|
-
const ContentTypesRoute = Route$5.update({
|
|
2956
|
-
id: "/content-types",
|
|
2957
|
-
path: "/content-types",
|
|
2958
|
-
getParentRoute: () => Route$a
|
|
2959
|
-
});
|
|
2960
|
-
const ContentRoute = Route$4.update({
|
|
2961
|
-
id: "/content",
|
|
2962
|
-
path: "/content",
|
|
2963
|
-
getParentRoute: () => Route$a
|
|
2964
|
-
});
|
|
2965
|
-
const IndexRoute = Route$3.update({
|
|
2966
|
-
id: "/",
|
|
2967
|
-
path: "/",
|
|
2968
|
-
getParentRoute: () => Route$a
|
|
2969
|
-
});
|
|
2970
|
-
const EntriesEntryIdRoute = Route$2.update({
|
|
2971
|
-
id: "/entries/$entryId",
|
|
2972
|
-
path: "/entries/$entryId",
|
|
2973
|
-
getParentRoute: () => Route$a
|
|
2974
|
-
});
|
|
2975
|
-
const EntriesTypeContentTypeIdRoute = Route$1.update({
|
|
2976
|
-
id: "/entries/type/$contentTypeId",
|
|
2977
|
-
path: "/entries/type/$contentTypeId",
|
|
2978
|
-
getParentRoute: () => Route$a
|
|
2979
|
-
});
|
|
2980
|
-
const EntriesNewContentTypeIdRoute = Route.update({
|
|
2981
|
-
id: "/entries/new/$contentTypeId",
|
|
2982
|
-
path: "/entries/new/$contentTypeId",
|
|
2983
|
-
getParentRoute: () => Route$a
|
|
2984
|
-
});
|
|
2985
|
-
const rootRouteChildren = {
|
|
2986
|
-
IndexRoute,
|
|
2987
|
-
ContentRoute,
|
|
2988
|
-
ContentTypesRoute,
|
|
2989
|
-
MediaRoute,
|
|
2990
|
-
SettingsRoute,
|
|
2991
|
-
TaxonomiesRoute,
|
|
2992
|
-
TrashRoute,
|
|
2993
|
-
EntriesEntryIdRoute,
|
|
2994
|
-
EntriesNewContentTypeIdRoute,
|
|
2995
|
-
EntriesTypeContentTypeIdRoute
|
|
2996
|
-
};
|
|
2997
|
-
const routeTree = Route$a._addFileChildren(rootRouteChildren)._addFileTypes();
|
|
2998
|
-
function getRouter() {
|
|
2999
|
-
const router2 = createRouter({
|
|
3000
|
-
routeTree,
|
|
3001
|
-
scrollRestoration: true,
|
|
3002
|
-
defaultPreload: "intent"
|
|
3003
|
-
});
|
|
3004
|
-
return router2;
|
|
3005
|
-
}
|
|
3006
|
-
const router = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
3007
|
-
__proto__: null,
|
|
3008
|
-
getRouter
|
|
3009
|
-
}, Symbol.toStringTag, { value: "Module" }));
|
|
3010
|
-
export {
|
|
3011
|
-
PopoverContent as A,
|
|
3012
|
-
Button as B,
|
|
3013
|
-
CmsButton as C,
|
|
3014
|
-
Dialog as D,
|
|
3015
|
-
useAuth as E,
|
|
3016
|
-
getResourcePermissions as F,
|
|
3017
|
-
canAccessResource as G,
|
|
3018
|
-
hasPermission as H,
|
|
3019
|
-
Input as I,
|
|
3020
|
-
Route as J,
|
|
3021
|
-
buttonVariants as K,
|
|
3022
|
-
Label as L,
|
|
3023
|
-
useBreadcrumbContext as M,
|
|
3024
|
-
router as N,
|
|
3025
|
-
Popover as P,
|
|
3026
|
-
RouteGuard as R,
|
|
3027
|
-
Select as S,
|
|
3028
|
-
Textarea as T,
|
|
3029
|
-
SelectTrigger as a,
|
|
3030
|
-
SelectValue as b,
|
|
3031
|
-
SelectContent as c,
|
|
3032
|
-
SelectItem as d,
|
|
3033
|
-
Checkbox as e,
|
|
3034
|
-
cn as f,
|
|
3035
|
-
CmsConfirmDialog as g,
|
|
3036
|
-
api as h,
|
|
3037
|
-
CmsDialog as i,
|
|
3038
|
-
useAdminConfig as j,
|
|
3039
|
-
useTheme as k,
|
|
3040
|
-
DialogContent as l,
|
|
3041
|
-
DialogTitle as m,
|
|
3042
|
-
DropdownMenu as n,
|
|
3043
|
-
DropdownMenuTrigger as o,
|
|
3044
|
-
DropdownMenuContent as p,
|
|
3045
|
-
DropdownMenuItem as q,
|
|
3046
|
-
DropdownMenuSeparator as r,
|
|
3047
|
-
DialogHeader as s,
|
|
3048
|
-
DialogFooter as t,
|
|
3049
|
-
useApi as u,
|
|
3050
|
-
useSettingsConfig as v,
|
|
3051
|
-
ContentTypeFormModal as w,
|
|
3052
|
-
Route$2 as x,
|
|
3053
|
-
Route$1 as y,
|
|
3054
|
-
PopoverTrigger as z
|
|
3055
|
-
};
|