@kyro-cms/admin 0.3.1 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/dist/EditorClient-XEUOVAAC.js +466 -0
  2. package/dist/EditorClient-XEUOVAAC.js.map +1 -0
  3. package/dist/EditorClient-YLCGVDXY.cjs +468 -0
  4. package/dist/EditorClient-YLCGVDXY.cjs.map +1 -0
  5. package/dist/chunk-7KPIUCGT.js +384 -0
  6. package/dist/chunk-7KPIUCGT.js.map +1 -0
  7. package/dist/chunk-GOACG6R7.cjs +473 -0
  8. package/dist/chunk-GOACG6R7.cjs.map +1 -0
  9. package/dist/index.cjs +14861 -0
  10. package/dist/index.cjs.map +1 -0
  11. package/dist/index.css +1661 -0
  12. package/dist/index.css.map +1 -0
  13. package/dist/index.d.ts +563 -0
  14. package/dist/index.js +14784 -0
  15. package/dist/index.js.map +1 -0
  16. package/package.json +19 -19
  17. package/src/components/ActionBar.tsx +7 -43
  18. package/src/components/Admin.tsx +138 -277
  19. package/src/components/ApiKeysManager.tsx +428 -419
  20. package/src/components/AuditLogsPage.tsx +35 -39
  21. package/src/components/AuthBridge.tsx +51 -0
  22. package/src/components/AutoForm.tsx +495 -1230
  23. package/src/components/BrandingHub.tsx +18 -19
  24. package/src/components/BulkActionsBar.tsx +1 -1
  25. package/src/components/CreateView.tsx +22 -36
  26. package/src/components/Dashboard.tsx +60 -84
  27. package/src/components/DetailView.tsx +113 -91
  28. package/src/components/DeveloperCenter.tsx +200 -198
  29. package/src/components/FieldRenderer.tsx +206 -0
  30. package/src/components/GraphQLPlayground.tsx +340 -480
  31. package/src/components/ListView.tsx +828 -254
  32. package/src/components/LoginPage.tsx +3 -4
  33. package/src/components/MarketplaceManager.tsx +254 -0
  34. package/src/components/MediaGallery.tsx +856 -1192
  35. package/src/components/PluginsManager.tsx +277 -0
  36. package/src/components/RestPlayground.tsx +398 -560
  37. package/src/components/SessionsManager.tsx +211 -0
  38. package/src/components/Sidebar.astro +179 -151
  39. package/src/components/ThemeProvider.tsx +7 -161
  40. package/src/components/UserManagement.tsx +162 -146
  41. package/src/components/UserMenu.tsx +110 -0
  42. package/src/components/WebhookManager.tsx +305 -367
  43. package/src/components/blocks/AccordionBlock.tsx +4 -4
  44. package/src/components/blocks/ArrayBlock.tsx +3 -3
  45. package/src/components/blocks/BlockEditModal.tsx +8 -8
  46. package/src/components/blocks/BlockWrapper.tsx +61 -0
  47. package/src/components/blocks/ButtonBlock.tsx +4 -4
  48. package/src/components/blocks/ChildBlocksTree.tsx +23 -25
  49. package/src/components/blocks/CodeBlock.tsx +15 -15
  50. package/src/components/blocks/ColumnsBlock.tsx +6 -44
  51. package/src/components/blocks/DividerBlock.tsx +3 -3
  52. package/src/components/blocks/FileBlock.tsx +4 -4
  53. package/src/components/blocks/HeadingBlock.tsx +6 -38
  54. package/src/components/blocks/HeroBlock.tsx +4 -4
  55. package/src/components/blocks/ImageBlock.tsx +4 -4
  56. package/src/components/blocks/LinkBlock.tsx +4 -4
  57. package/src/components/blocks/ListBlock.tsx +3 -3
  58. package/src/components/blocks/ParagraphBlock.tsx +12 -42
  59. package/src/components/blocks/RelationshipBlock.tsx +4 -4
  60. package/src/components/blocks/RichTextBlock.tsx +4 -4
  61. package/src/components/blocks/VStackBlock.tsx +5 -37
  62. package/src/components/blocks/VideoBlock.tsx +4 -4
  63. package/src/components/blocks/types.ts +11 -0
  64. package/src/components/fields/AccordionField.tsx +1 -1
  65. package/src/components/fields/ArrayField.tsx +2 -2
  66. package/src/components/fields/ArrayLayout.tsx +93 -0
  67. package/src/components/fields/BlocksField.tsx +122 -111
  68. package/src/components/fields/ButtonField.tsx +1 -1
  69. package/src/components/fields/CheckboxField.tsx +14 -15
  70. package/src/components/fields/ChildrenField.tsx +2 -2
  71. package/src/components/fields/CodeField.tsx +3 -3
  72. package/src/components/fields/ColumnsField.tsx +2 -2
  73. package/src/components/fields/DateField.tsx +13 -26
  74. package/src/components/fields/EditorClient.tsx +26 -28
  75. package/src/components/fields/FieldLayout.tsx +52 -0
  76. package/src/components/fields/GroupLayout.tsx +35 -0
  77. package/src/components/fields/JSONField.tsx +7 -7
  78. package/src/components/fields/LinkField.tsx +1 -1
  79. package/src/components/fields/MarkdownField.tsx +1 -1
  80. package/src/components/fields/NumberField.tsx +13 -26
  81. package/src/components/fields/PortableTextField.tsx +4 -4
  82. package/src/components/fields/PortableTextRenderer.tsx +1 -1
  83. package/src/components/fields/RelationshipBlockField.tsx +31 -23
  84. package/src/components/fields/RelationshipField.tsx +14 -14
  85. package/src/components/fields/SelectField.tsx +17 -26
  86. package/src/components/fields/TabsLayout.tsx +69 -0
  87. package/src/components/fields/TextField.tsx +85 -38
  88. package/src/components/fields/UploadField.tsx +71 -41
  89. package/src/components/fields/VideoField.tsx +1 -1
  90. package/src/components/fields/extensions/blockComponents.tsx +2 -2
  91. package/src/components/fields/extensions/blocksStore.ts +207 -193
  92. package/src/components/fields/types.ts +22 -0
  93. package/src/components/layout/Layout.tsx +1 -1
  94. package/src/components/ui/ActionMenu.tsx +63 -0
  95. package/src/components/ui/Badge.tsx +59 -5
  96. package/src/components/ui/BlockDrawer.tsx +4 -5
  97. package/src/components/ui/CommandPalette.tsx +58 -36
  98. package/src/components/ui/CommandPaletteWrapper.tsx +18 -17
  99. package/src/components/ui/Dropdown.tsx +18 -16
  100. package/src/components/ui/EmptyState.tsx +25 -0
  101. package/src/components/ui/GlobalModal.tsx +49 -0
  102. package/src/components/ui/IconButton.tsx +44 -0
  103. package/src/components/ui/Modal.tsx +19 -20
  104. package/src/components/ui/PageHeader.tsx +158 -0
  105. package/src/components/ui/Pagination.tsx +61 -0
  106. package/src/components/ui/PromptModal.tsx +1 -1
  107. package/src/components/ui/SearchInput.tsx +57 -0
  108. package/src/components/ui/SeoPreview.tsx +31 -0
  109. package/src/components/ui/SessionModal.tsx +0 -0
  110. package/src/components/ui/SlidePanel.tsx +2 -0
  111. package/src/components/ui/Toast.tsx +65 -122
  112. package/src/components/ui/Toaster.tsx +18 -0
  113. package/src/components/ui/icons.tsx +112 -0
  114. package/src/components/users/UserDetail.tsx +290 -0
  115. package/src/components/users/UserForm.tsx +242 -0
  116. package/src/components/users/UsersList.tsx +338 -0
  117. package/src/env.d.ts +13 -13
  118. package/src/fields/index.ts +2 -1
  119. package/src/global.d.ts +7 -0
  120. package/src/hooks/data.ts +2 -9
  121. package/src/hooks/useAsyncData.ts +36 -0
  122. package/src/hooks/useAutoFormState.ts +527 -0
  123. package/src/hooks/useSelection.ts +49 -0
  124. package/src/hooks/useSession.ts +0 -0
  125. package/src/index.ts +11 -1
  126. package/src/integration.ts +86 -11
  127. package/src/kyro-cms.d.ts +209 -0
  128. package/src/layouts/AdminLayout.astro +128 -11
  129. package/src/layouts/AuthLayout.astro +21 -5
  130. package/src/lib/api.ts +175 -55
  131. package/src/lib/autoform-store.ts +435 -0
  132. package/src/lib/config.ts +82 -34
  133. package/src/lib/createRegistry.ts +29 -0
  134. package/src/lib/default-kyro-config.ts +4 -0
  135. package/src/lib/globals.ts +50 -0
  136. package/src/lib/media-utils.ts +18 -0
  137. package/src/lib/object-utils.ts +77 -0
  138. package/src/lib/paths.ts +61 -0
  139. package/src/lib/stores/index.ts +370 -0
  140. package/src/lib/types.ts +43 -0
  141. package/src/lib/useResourceManager.ts +105 -0
  142. package/src/pages/403.astro +67 -0
  143. package/src/pages/[collection]/[id].astro +14 -180
  144. package/src/pages/[collection]/index.astro +11 -6
  145. package/src/pages/api-explorer.astro +173 -0
  146. package/src/pages/audit/index.astro +2 -0
  147. package/src/pages/auth/login.astro +122 -0
  148. package/src/pages/auth/register.astro +167 -0
  149. package/src/pages/graphql-explorer.astro +59 -0
  150. package/src/pages/{admin/graphql.astro → graphql.astro} +51 -17
  151. package/src/pages/index.astro +577 -0
  152. package/src/pages/index_ALT.astro +3 -0
  153. package/src/pages/keys.astro +11 -0
  154. package/src/pages/marketplace.astro +11 -0
  155. package/src/pages/media.astro +3 -0
  156. package/src/pages/plugins.astro +8 -0
  157. package/src/pages/preview/[collection]/[id].astro +188 -123
  158. package/src/pages/rest-playground.astro +62 -0
  159. package/src/pages/roles/index.astro +183 -76
  160. package/src/pages/sessions.astro +8 -0
  161. package/src/pages/settings/[slug].astro +92 -114
  162. package/src/pages/settings/index.astro +5 -3
  163. package/src/pages/users/[id].astro +25 -154
  164. package/src/pages/users/index.astro +19 -130
  165. package/src/pages/users/new.astro +9 -86
  166. package/src/pages/webhooks.astro +11 -0
  167. package/src/routes.ts +80 -0
  168. package/src/styles/main.css +119 -79
  169. package/src/theme/tokens.ts +1 -0
  170. package/src/vite-env.d.ts +14 -0
  171. package/src/collections/auth/index.ts +0 -155
  172. package/src/collections/portfolio/index.ts +0 -343
  173. package/src/components/ApiExplorer.tsx +0 -325
  174. package/src/components/EnhancedListView.tsx +0 -889
  175. package/src/components/GraphQLExplorer.tsx +0 -675
  176. package/src/components/Icons.tsx +0 -23
  177. package/src/components/StatusBadge.tsx +0 -76
  178. package/src/lib/MediaService.ts +0 -541
  179. package/src/lib/auth/sqlite-adapter.ts +0 -319
  180. package/src/lib/dataStore.ts +0 -226
  181. package/src/lib/db/adapter.ts +0 -54
  182. package/src/lib/db/drizzle-mysql-adapter.ts +0 -194
  183. package/src/lib/db/drizzle-mysql-auth-adapter.ts +0 -327
  184. package/src/lib/db/drizzle-postgres-adapter.ts +0 -202
  185. package/src/lib/db/drizzle-postgres-auth-adapter.ts +0 -304
  186. package/src/lib/db/drizzle-sqlite-adapter.ts +0 -227
  187. package/src/lib/db/drizzle-sqlite-auth-adapter.ts +0 -548
  188. package/src/lib/db/index.ts +0 -449
  189. package/src/lib/db/mongodb-adapter.ts +0 -207
  190. package/src/lib/db/mongodb-auth-adapter.ts +0 -305
  191. package/src/lib/db/schema/mysql-auth.ts +0 -113
  192. package/src/lib/db/schema/mysql-content.ts +0 -20
  193. package/src/lib/db/schema/postgres-auth.ts +0 -116
  194. package/src/lib/db/schema/postgres-content.ts +0 -35
  195. package/src/lib/db/schema/postgres-media.ts +0 -52
  196. package/src/lib/db/schema/postgres-settings.ts +0 -11
  197. package/src/lib/db/schema/sqlite-auth.ts +0 -112
  198. package/src/lib/db/schema/sqlite-content.ts +0 -20
  199. package/src/lib/db/version-adapter.ts +0 -248
  200. package/src/lib/graphql/index.ts +0 -1
  201. package/src/lib/graphql/schema.ts +0 -443
  202. package/src/lib/rate-limit.ts +0 -267
  203. package/src/lib/storage.ts +0 -374
  204. package/src/lib/store.ts +0 -85
  205. package/src/middleware.ts +0 -177
  206. package/src/pages/admin/api-explorer.astro +0 -98
  207. package/src/pages/admin/graphql-explorer.astro +0 -40
  208. package/src/pages/admin/index.astro +0 -286
  209. package/src/pages/admin/keys.astro +0 -8
  210. package/src/pages/admin/rest-playground.astro +0 -44
  211. package/src/pages/admin/webhooks.astro +0 -8
  212. package/src/pages/api/[collection]/[id]/publish.ts +0 -52
  213. package/src/pages/api/[collection]/[id]/unpublish.ts +0 -42
  214. package/src/pages/api/[collection]/[id]/versions.ts +0 -66
  215. package/src/pages/api/[collection]/[id].ts +0 -213
  216. package/src/pages/api/[collection]/index.ts +0 -209
  217. package/src/pages/api/auth/[id].ts +0 -121
  218. package/src/pages/api/auth/audit-logs.ts +0 -57
  219. package/src/pages/api/auth/login.ts +0 -211
  220. package/src/pages/api/auth/logout.ts +0 -66
  221. package/src/pages/api/auth/me.ts +0 -36
  222. package/src/pages/api/auth/refresh.ts +0 -119
  223. package/src/pages/api/auth/register.ts +0 -188
  224. package/src/pages/api/auth/users.ts +0 -97
  225. package/src/pages/api/collections.ts +0 -59
  226. package/src/pages/api/globals/[slug].ts +0 -42
  227. package/src/pages/api/graphql.ts +0 -90
  228. package/src/pages/api/health.ts +0 -426
  229. package/src/pages/api/keys/[id].ts +0 -26
  230. package/src/pages/api/keys/index.ts +0 -75
  231. package/src/pages/api/media/[id].ts +0 -309
  232. package/src/pages/api/media/folders.ts +0 -609
  233. package/src/pages/api/media/index.ts +0 -146
  234. package/src/pages/api/media/resize.ts +0 -267
  235. package/src/pages/api/search.ts +0 -82
  236. package/src/pages/api/slug-availability.ts +0 -70
  237. package/src/pages/api/storage-config.ts +0 -20
  238. package/src/pages/api/storage-status.ts +0 -206
  239. package/src/pages/api/upload.ts +0 -334
  240. package/src/pages/api/webhooks/index.ts +0 -71
  241. package/src/pages/login.astro +0 -82
  242. package/src/pages/register.astro +0 -102
@@ -1,112 +1,219 @@
1
1
  ---
2
- import AdminLayout from '../../layouts/AdminLayout.astro';
2
+ import AdminLayout from "../../layouts/AdminLayout.astro";
3
+
4
+ import { adminPath, apiPath } from "../../lib/paths";
3
5
 
4
6
  const defaultRoles = [
5
- { name: 'super_admin', level: 100, inherits: ['admin'], description: 'Full system access across all tenants', color: 'bg-red-500/10 text-red-500 border-red-500/20' },
6
- { name: 'admin', level: 90, inherits: ['editor'], description: 'Full tenant access with all content permissions', color: 'bg-purple-500/10 text-purple-500 border-purple-500/20' },
7
- { name: 'editor', level: 70, inherits: ['author'], description: 'Edit and publish all content', color: 'bg-blue-500/10 text-blue-500 border-blue-500/20' },
8
- { name: 'author', level: 50, inherits: ['customer'], description: 'Create and edit own content', color: 'bg-green-500/10 text-green-500 border-green-500/20' },
9
- { name: 'customer', level: 30, inherits: [], description: 'Access own data and make purchases', color: 'bg-[var(--kyro-surface-accent)] text-[var(--kyro-text-secondary)] border-[var(--kyro-border)]' },
10
- { name: 'guest', level: 10, inherits: [], description: 'Public read-only access', color: 'bg-yellow-500/10 text-yellow-500 border-yellow-500/20' },
7
+ {
8
+ name: "super_admin",
9
+ level: 100,
10
+ inherits: ["admin"],
11
+ description: "Full system access across all tenants",
12
+ color: "bg-red-500/10 text-red-500 border-red-500/20",
13
+ },
14
+ {
15
+ name: "admin",
16
+ level: 90,
17
+ inherits: ["editor"],
18
+ description: "Full tenant access with all content permissions",
19
+ color: "bg-purple-500/10 text-purple-500 border-purple-500/20",
20
+ },
21
+ {
22
+ name: "editor",
23
+ level: 70,
24
+ inherits: ["author"],
25
+ description: "Edit and publish all content",
26
+ color: "bg-blue-500/10 text-blue-500 border-blue-500/20",
27
+ },
28
+ {
29
+ name: "author",
30
+ level: 50,
31
+ inherits: ["customer"],
32
+ description: "Create and edit own content",
33
+ color: "bg-green-500/10 text-green-500 border-green-500/20",
34
+ },
35
+ {
36
+ name: "customer",
37
+ level: 30,
38
+ inherits: [],
39
+ description: "Access own data and make purchases",
40
+ color:
41
+ "bg-[var(--kyro-surface-accent)] text-[var(--kyro-text-secondary)] border-[var(--kyro-border)]",
42
+ },
43
+ {
44
+ name: "guest",
45
+ level: 10,
46
+ inherits: [],
47
+ description: "Public read-only access",
48
+ color: "bg-yellow-500/10 text-yellow-500 border-yellow-500/20",
49
+ },
11
50
  ];
12
51
 
13
52
  const permissions: Record<string, string[]> = {
14
- super_admin: ['*'],
15
- admin: ['users:*', 'roles:*', 'content:*', 'settings:*', 'audit:read'],
16
- editor: ['content:*', 'media:*', 'comments:*'],
17
- author: ['content:create', 'content:read', 'content:update:own', 'media:*'],
18
- customer: ['content:read', 'orders:*', 'profile:*'],
19
- guest: ['content:read'],
53
+ super_admin: ["*"],
54
+ admin: ["users:*", "roles:*", "content:*", "settings:*", "audit:read"],
55
+ editor: ["content:*", "media:*", "comments:*"],
56
+ author: ["content:create", "content:read", "content:update:own", "media:*"],
57
+ customer: ["content:read", "orders:*", "profile:*"],
58
+ guest: ["content:read"],
20
59
  };
21
60
  ---
22
61
 
23
62
  <AdminLayout title="Roles">
24
- <div class="flex-1 overflow-y-auto p-8 pr-12 space-y-8">
63
+ <div class="flex-1 overflow-y-auto pr-12 space-y-8">
25
64
  <!-- Header -->
26
65
  <div class="surface-tile p-6">
27
- <h1 class="text-3xl font-black tracking-tighter text-[var(--kyro-text-primary)]">Roles & Permissions</h1>
28
- <p class="text-sm text-[var(--kyro-text-secondary)] mt-1 font-medium italic">
66
+ <h1
67
+ class="text-xl font-bold tracking-tighter text-[var(--kyro-text-primary)]"
68
+ >
69
+ Roles & Permissions
70
+ </h1>
71
+ <p class="text-sm text-[var(--kyro-text-secondary)] mt-1 font-regular">
29
72
  Role-based access control with hierarchical permissions
30
73
  </p>
31
74
  </div>
32
75
 
33
76
  <!-- Role Hierarchy -->
34
77
  <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
35
- {defaultRoles.map((role) => (
36
- <div class={`surface-tile p-6 border-2 ${role.color.split(' ').slice(2).join(' ')} rounded-2xl`}>
37
- <div class="flex items-center justify-between mb-4">
38
- <h3 class="text-xl font-black text-[var(--kyro-text-primary)] tracking-tighter">{role.name}</h3>
39
- <span class={`px-3 py-1 rounded-lg text-xs font-bold ${role.color.split(' ').slice(0, 2).join(' ')}`}>
40
- Level {role.level}
41
- </span>
42
- </div>
43
- <p class="text-sm text-[var(--kyro-text-secondary)] mb-4">{role.description}</p>
44
- {role.inherits.length > 0 && (
45
- <div class="mb-4">
46
- <span class="text-xs font-bold text-[var(--kyro-text-secondary)] uppercase tracking-[0.1em] opacity-50">Inherits:</span>
47
- <div class="flex gap-2 mt-2">
48
- {role.inherits.map((parent) => (
49
- <span class="px-2 py-1 bg-[var(--kyro-surface-accent)] rounded text-xs font-bold text-[var(--kyro-text-secondary)]">{parent}</span>
78
+ {
79
+ defaultRoles.map((role) => (
80
+ <div
81
+ class={`surface-tile p-6 border-2 ${role.color.split(" ").slice(2).join(" ")} rounded-2xl`}
82
+ >
83
+ <div class="flex items-center justify-between mb-4">
84
+ <h3 class="text-xl font-bold text-[var(--kyro-text-primary)] tracking-tighter">
85
+ {role.name}
86
+ </h3>
87
+ <span
88
+ class={`px-3 py-1 rounded-lg text-xs font-bold ${role.color.split(" ").slice(0, 2).join(" ")}`}
89
+ >
90
+ Level {role.level}
91
+ </span>
92
+ </div>
93
+ <p class="text-sm text-[var(--kyro-text-secondary)] mb-4">
94
+ {role.description}
95
+ </p>
96
+ {role.inherits.length > 0 && (
97
+ <div class="mb-4">
98
+ <span class="text-xs font-bold text-[var(--kyro-text-secondary)] tracking-[0.1em] opacity-50">
99
+ Inherits:
100
+ </span>
101
+ <div class="flex gap-2 mt-2">
102
+ {role.inherits.map((parent) => (
103
+ <span class="px-2 py-1 bg-[var(--kyro-surface-accent)] rounded text-xs font-bold text-[var(--kyro-text-secondary)]">
104
+ {parent}
105
+ </span>
106
+ ))}
107
+ </div>
108
+ </div>
109
+ )}
110
+ <div>
111
+ <span class="text-xs font-bold text-[var(--kyro-text-secondary)] tracking-[0.1em] opacity-50">
112
+ Permissions:
113
+ </span>
114
+ <div class="flex flex-wrap gap-1.5 mt-2">
115
+ {(permissions[role.name] || []).map((perm) => (
116
+ <span class="px-2 py-0.5 bg-[var(--kyro-surface-accent)] rounded text-[10px] font-medium text-[var(--kyro-text-primary)]">
117
+ {perm}
118
+ </span>
50
119
  ))}
51
120
  </div>
52
121
  </div>
53
- )}
54
- <div>
55
- <span class="text-xs font-bold text-[var(--kyro-text-secondary)] uppercase tracking-[0.1em] opacity-50">Permissions:</span>
56
- <div class="flex flex-wrap gap-1.5 mt-2">
57
- {(permissions[role.name] || []).map((perm) => (
58
- <span class="px-2 py-0.5 bg-[var(--kyro-surface-accent)] rounded text-[10px] font-bold text-[var(--kyro-text-primary)]">{perm}</span>
59
- ))}
60
- </div>
61
122
  </div>
62
- </div>
63
- ))}
123
+ ))
124
+ }
64
125
  </div>
65
126
 
66
127
  <!-- Permission Matrix -->
67
128
  <div class="surface-tile overflow-hidden">
68
- <div class="p-6 border-b border-[var(--kyro-border)] bg-[var(--kyro-surface-accent)]">
69
- <h2 class="text-xl font-black text-[var(--kyro-text-primary)] tracking-tighter">Permission Matrix</h2>
129
+ <div
130
+ class="p-6 border-b border-[var(--kyro-border)] bg-[var(--kyro-surface-accent)]"
131
+ >
132
+ <h2
133
+ class="text-xl font-bold text-[var(--kyro-text-primary)] tracking-tighter"
134
+ >
135
+ Permission Matrix
136
+ </h2>
70
137
  </div>
71
138
  <table class="w-full text-left">
72
139
  <thead>
73
- <tr class="text-[var(--kyro-text-secondary)] font-bold text-[10px] uppercase tracking-[0.3em] border-b border-[var(--kyro-border)]">
140
+ <tr
141
+ class="text-[var(--kyro-text-secondary)] font-bold text-[10px] tracking-[0.3em] border-b border-[var(--kyro-border)]"
142
+ >
74
143
  <th class="px-6 py-4">Resource</th>
75
- {defaultRoles.map((role) => (
76
- <th class="px-4 py-4 text-center">{role.name}</th>
77
- ))}
144
+ {
145
+ defaultRoles.map((role) => (
146
+ <th class="px-4 py-4 text-center">{role.name}</th>
147
+ ))
148
+ }
78
149
  </tr>
79
150
  </thead>
80
151
  <tbody class="divide-y divide-[var(--kyro-border)]">
81
- {[
82
- { resource: 'Users', actions: ['create', 'read', 'update', 'delete'] },
83
- { resource: 'Roles', actions: ['create', 'read', 'update', 'delete'] },
84
- { resource: 'Content', actions: ['create', 'read', 'update', 'delete', 'publish'] },
85
- { resource: 'Media', actions: ['create', 'read', 'update', 'delete'] },
86
- { resource: 'Settings', actions: ['read', 'update'] },
87
- { resource: 'Audit Logs', actions: ['read'] },
88
- ].map((row) => (
89
- <tr class="hover:bg-[var(--kyro-surface-accent)] transition-colors">
90
- <td class="px-6 py-4 font-bold text-[var(--kyro-text-primary)]">{row.resource}</td>
91
- {defaultRoles.map((role) => {
92
- const hasAll = permissions[role.name]?.includes('*');
93
- const hasResource = permissions[role.name]?.some(p => p.startsWith(row.resource.toLowerCase() + ':') || p === '*');
94
- return (
95
- <td class="px-4 py-4 text-center">
96
- {hasAll || hasResource ? (
97
- <svg class="w-5 h-5 text-green-500 mx-auto" fill="currentColor" viewBox="0 0 20 20">
98
- <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
99
- </svg>
100
- ) : (
101
- <svg class="w-5 h-5 text-[var(--kyro-border)] mx-auto" fill="currentColor" viewBox="0 0 20 20">
102
- <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
103
- </svg>
104
- )}
105
- </td>
106
- );
107
- })}
108
- </tr>
109
- ))}
152
+ {
153
+ [
154
+ {
155
+ resource: "Users",
156
+ actions: ["create", "read", "update", "delete"],
157
+ },
158
+ {
159
+ resource: "Roles",
160
+ actions: ["create", "read", "update", "delete"],
161
+ },
162
+ {
163
+ resource: "Content",
164
+ actions: ["create", "read", "update", "delete", "publish"],
165
+ },
166
+ {
167
+ resource: "Media",
168
+ actions: ["create", "read", "update", "delete"],
169
+ },
170
+ { resource: "Settings", actions: ["read", "update"] },
171
+ { resource: "Audit Logs", actions: ["read"] },
172
+ ].map((row) => (
173
+ <tr class="hover:bg-[var(--kyro-surface-accent)] transition-colors">
174
+ <td class="px-6 py-4 font-medium text-[var(--kyro-text-primary)]">
175
+ {row.resource}
176
+ </td>
177
+ {defaultRoles.map((role) => {
178
+ const hasAll = permissions[role.name]?.includes("*");
179
+ const hasResource = permissions[role.name]?.some(
180
+ (p) =>
181
+ p.startsWith(row.resource.toLowerCase() + ":") ||
182
+ p === "*",
183
+ );
184
+ return (
185
+ <td class="px-4 py-4 text-center">
186
+ {hasAll || hasResource ? (
187
+ <svg
188
+ class="w-5 h-5 text-green-500 mx-auto"
189
+ fill="currentColor"
190
+ viewBox="0 0 20 20"
191
+ >
192
+ <path
193
+ fill-rule="evenodd"
194
+ d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
195
+ clip-rule="evenodd"
196
+ />
197
+ </svg>
198
+ ) : (
199
+ <svg
200
+ class="w-5 h-5 text-[var(--kyro-border)] mx-auto"
201
+ fill="currentColor"
202
+ viewBox="0 0 20 20"
203
+ >
204
+ <path
205
+ fill-rule="evenodd"
206
+ d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
207
+ clip-rule="evenodd"
208
+ />
209
+ </svg>
210
+ )}
211
+ </td>
212
+ );
213
+ })}
214
+ </tr>
215
+ ))
216
+ }
110
217
  </tbody>
111
218
  </table>
112
219
  </div>
@@ -0,0 +1,8 @@
1
+ ---
2
+ import AdminLayout from "../layouts/AdminLayout.astro";
3
+ import { SessionsManager } from "../components/SessionsManager";
4
+ ---
5
+
6
+ <AdminLayout title="Sessions">
7
+ <SessionsManager client:only="react" />
8
+ </AdminLayout>
@@ -1,161 +1,139 @@
1
1
  ---
2
- import AdminLayout from '../../layouts/AdminLayout.astro';
3
- import { globals } from '@/lib/config';
4
- import { AutoForm } from '@/components/AutoForm';
2
+ import AdminLayout from "../../layouts/AdminLayout.astro";
3
+ import { globals } from "../../lib/config";
4
+ import { AutoForm } from "../../components/AutoForm";
5
+
6
+ import { adminPath, apiPath } from "../../lib/paths";
5
7
 
6
8
  const { slug } = Astro.params;
7
9
 
8
10
  // Validate global exists
9
11
  if (!slug || !globals[slug]) {
10
- return Astro.redirect('/');
12
+ return Astro.redirect(adminPath);
11
13
  }
12
14
 
13
15
  const config = globals[slug];
16
+ const hasVersioning = !!config.versions;
14
17
 
15
- // Fetch current settings data from API
18
+ // Fetch current settings data from API (authenticated)
16
19
  let data: any = {};
17
20
  try {
18
- const response = await fetch(`${Astro.url.origin}/api/globals/${slug}`, {
19
- headers: Astro.request.headers,
20
- credentials: 'include'
21
- });
21
+ const fetchUrl = new URL(`${apiPath}/globals/${slug}`, Astro.url.origin);
22
+ if (hasVersioning) {
23
+ fetchUrl.searchParams.set("draft", "true");
24
+ }
25
+
26
+ const response = await fetch(
27
+ fetchUrl.toString(),
28
+ {
29
+ headers: Astro.request.headers,
30
+ credentials: "include",
31
+ },
32
+ );
22
33
  if (response.ok) {
23
34
  const result = await response.json();
24
35
  data = result.data || {};
25
36
  }
26
37
  } catch (error) {
27
- console.error('Failed to fetch settings:', error);
38
+ console.error("Failed to fetch settings:", error);
28
39
  }
29
40
 
30
41
  const activeSlug = slug;
31
42
  const allGlobals = Object.values(globals);
32
43
 
33
44
  const title = config.label || slug;
34
- const description = config.admin?.description || `Manage your ${title.toLowerCase()} configurations.`;
45
+ const description =
46
+ config.admin?.description ||
47
+ `Manage your ${title.toLowerCase()} configurations.`;
35
48
  ---
36
49
 
37
- <AdminLayout title="Settings">
38
- <div class="flex-1 overflow-y-auto p-8 space-y-6">
39
- <!-- Header -->
40
- <div class="surface-tile p-8 pb-0">
41
- <div class="flex items-center justify-between mb-8">
42
- <div>
43
- <h1 class="text-3xl font-black tracking-tighter text-[var(--kyro-text-primary)]">
44
- Settings
45
- </h1>
46
- <p class="text-sm text-[var(--kyro-text-secondary)] mt-1 font-medium italic">
47
- Global system configurations
48
- </p>
50
+ <AdminLayout title={`Settings - ${title}`}>
51
+ <div class="flex-1 overflow-y-auto">
52
+ { !hasVersioning && (
53
+ <!-- Standard Header (Only if no versioning/AutoForm header) -->
54
+ <div class="surface-tile p-8 pb-0 mb-6">
55
+ <div class="flex items-center justify-between mb-8">
56
+ <div>
57
+ <h1
58
+ class="text-xl font-bold tracking-tighter text-[var(--kyro-text-primary)]"
59
+ >
60
+ Settings
61
+ </h1>
62
+ <p
63
+ class="text-sm text-[var(--kyro-text-secondary)] mt-1 font-regular"
64
+ >
65
+ Global system configurations
66
+ </p>
67
+ </div>
68
+ <button
69
+ type="button"
70
+ id="settings-save-btn"
71
+ class="px-6 py-2.5 text-sm bg-[var(--kyro-sidebar-active)] text-[var(--kyro-sidebar-text-active)] rounded-full transition-all hover:opacity-90 active:scale-95 shadow-lg shadow-black/10"
72
+ >
73
+ Save Changes
74
+ </button>
49
75
  </div>
50
- <button
51
- type="submit"
52
- form="settings-form"
53
- id="btn-save"
54
- class="px-8 py-3 bg-[var(--kyro-sidebar-active)] text-[var(--kyro-sidebar-text-active)] rounded-xl font-bold transition-all hover:opacity-90 active:scale-95 shadow-lg shadow-black/10"
55
- >
56
- Save Changes
57
- </button>
58
- </div>
59
76
 
60
- <!-- Tabs Bar -->
61
- <div class="flex items-center gap-8 border-b border-[var(--kyro-border)] overflow-x-auto no-scrollbar">
62
- {
63
- allGlobals.map((g) => (
64
- <a
65
- href={`/settings/${g.slug}`}
66
- class={`pb-4 text-sm font-bold transition-all border-b-2 whitespace-nowrap ${
67
- activeSlug === g.slug
68
- ? "text-[var(--kyro-text-primary)] border-[var(--kyro-text-primary)]"
69
- : "text-[var(--kyro-text-secondary)] border-transparent hover:text-[var(--kyro-text-primary)]"
70
- }`}
71
- >
72
- {g.label || g.slug}
73
- </a>
74
- ))
75
- }
77
+ <!-- Tabs Bar -->
78
+ <div
79
+ class="flex items-center gap-8 border-b border-[var(--kyro-border)] overflow-x-auto no-scrollbar"
80
+ >
81
+ {
82
+ allGlobals.map((g) => (
83
+ <a
84
+ href={`${adminPath}/settings/${g.slug}`}
85
+ class={`pb-4 text-sm font-bold transition-all border-b-2 whitespace-nowrap ${
86
+ activeSlug === g.slug
87
+ ? "text-[var(--kyro-text-primary)] border-[var(--kyro-text-primary)]"
88
+ : "text-[var(--kyro-text-secondary)] border-transparent hover:text-[var(--kyro-text-primary)]"
89
+ }`}
90
+ >
91
+ {g.label || g.slug}
92
+ </a>
93
+ ))
94
+ }
95
+ </div>
76
96
  </div>
77
- </div>
97
+ )}
78
98
 
79
99
  <!-- Form Section -->
80
- <div class="surface-tile p-10 pt-4">
81
- <div class="mb-8">
82
- <h2 class="text-xl font-black text-[var(--kyro-text-primary)] tracking-tight">{config.label || slug}</h2>
83
- <p class="text-sm text-[var(--kyro-text-secondary)] mt-1">{description}</p>
84
- </div>
85
- <!-- Toast container -->
86
- <div id="toast-container" class="hidden fixed bottom-6 right-6 z-50">
87
- <div class="flex items-center gap-3 px-5 py-4 bg-[var(--kyro-sidebar-active)] text-[var(--kyro-sidebar-text-active)] rounded-xl shadow-2xl">
88
- <span id="toast-message"></span>
89
- <button onclick="document.getElementById('toast-container').classList.add('hidden')" class="opacity-50 hover:opacity-100 ml-2">
90
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
91
- </button>
100
+ <div class={hasVersioning ? "" : "surface-tile p-10 pt-4 mb-6 rounded-4xl"}>
101
+ { !hasVersioning && (
102
+ <div class="mb-8">
103
+ <h2
104
+ class="text-xl font-bold text-[var(--kyro-text-primary)] tracking-tight"
105
+ >
106
+ {config.label || slug}
107
+ </h2>
108
+ <p class="text-sm text-[var(--kyro-text-secondary)] mt-1">
109
+ {description}
110
+ </p>
92
111
  </div>
93
- </div>
94
-
112
+ )}
95
113
  <form id="settings-form">
114
+ <input type="hidden" id="form-slug" value={slug} />
96
115
  <AutoForm
97
116
  client:only="react"
98
117
  config={config}
99
118
  globalSlug={slug}
100
119
  data={data}
101
- layout="single"
120
+ layout={hasVersioning ? "split" : "single"}
102
121
  />
103
122
  <input type="hidden" id="form-data" name="form-data" value="{}" />
104
123
  </form>
105
124
  </div>
106
125
  </div>
107
126
 
108
- <script define:vars={{ slug }}>
109
- function showToast(message, isError = false) {
110
- const container = document.getElementById('toast-container');
111
- const msg = document.getElementById('toast-message');
112
- if (container && msg) {
113
- msg.textContent = message;
114
- container.classList.remove('hidden');
115
- if (!isError) {
116
- setTimeout(() => container.classList.add('hidden'), 3000);
117
- }
118
- }
119
- }
120
-
121
- document.getElementById('settings-form')?.addEventListener('submit', async (e) => {
122
- e.preventDefault();
123
-
124
- const btn = document.getElementById('btn-save');
125
- const originalText = btn?.textContent || '';
127
+ <script>
128
+ console.log("[Astro] Settings page script running");
129
+ document.getElementById("settings-save-btn")?.addEventListener("click", () => {
130
+ console.log("[Astro] Header save clicked");
131
+ const btn = document.getElementById("btn-save");
126
132
  if (btn) {
127
- btn.textContent = 'Saving...';
128
- btn.setAttribute('disabled', 'true');
129
- }
130
-
131
- // Read data from the hidden input that AutoForm writes to internally
132
- const hiddenInput = document.getElementById('form-data');
133
- let payload = {};
134
- if (hiddenInput && hiddenInput.value) {
135
- try { payload = JSON.parse(hiddenInput.value); } catch(e) {}
136
- }
137
-
138
- try {
139
- const response = await fetch(`/api/globals/${slug}`, {
140
- method: 'PATCH',
141
- credentials: 'include',
142
- headers: { 'Content-Type': 'application/json' },
143
- body: JSON.stringify(payload),
144
- });
145
-
146
- if (response.ok) {
147
- showToast('Settings saved successfully');
148
- } else {
149
- const error = await response.json();
150
- showToast(error.error || 'Failed to save settings', true);
151
- }
152
- } catch (error) {
153
- showToast('An error occurred while saving', true);
154
- } finally {
155
- if (btn) {
156
- btn.textContent = originalText;
157
- btn.removeAttribute('disabled');
158
- }
133
+ console.log("[Astro] Triggering hidden button click");
134
+ btn.click();
135
+ } else {
136
+ console.error("[Astro] Hidden save button not found!");
159
137
  }
160
138
  });
161
139
  </script>
@@ -1,9 +1,11 @@
1
1
  ---
2
- import { globals } from '@/lib/config';
2
+ import { globals } from '../../lib/config';
3
+
4
+ import { adminPath } from '../../lib/paths';
3
5
 
4
6
  const firstGlobal = Object.values(globals)[0];
5
7
  if (firstGlobal) {
6
- return Astro.redirect(`/settings/${firstGlobal.slug}`);
8
+ return Astro.redirect(`${adminPath}/settings/${firstGlobal.slug}`);
7
9
  }
8
- return Astro.redirect('/');
10
+ return Astro.redirect(adminPath);
9
11
  ---