@kyro-cms/admin 0.3.2 → 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
@@ -10,7 +10,7 @@ import {
10
10
  Check,
11
11
  RefreshCcw,
12
12
  Sparkles,
13
- } from "lucide-react";
13
+ } from "./ui/icons";
14
14
 
15
15
  export function BrandingHub() {
16
16
  const [siteName, setSiteName] = useState("Kyro CMS");
@@ -73,11 +73,11 @@ export function BrandingHub() {
73
73
  ];
74
74
 
75
75
  return (
76
- <div className="w-full space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-700 px-8 pb-32">
76
+ <div className="w-full space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-700 pb-32">
77
77
  {/* Header */}
78
78
  <div className="flex flex-col lg:flex-row lg:items-center justify-between gap-6 pt-4">
79
79
  <div>
80
- <h1 className="text-4xl font-black tracking-tighter text-[var(--kyro-text-primary)]">
80
+ <h1 className="text-xl font-bold tracking-tighter text-[var(--kyro-text-primary)]">
81
81
  Branding <span className="text-[var(--kyro-primary)]">Hub</span>
82
82
  </h1>
83
83
  <p className="text-[var(--kyro-text-secondary)] mt-1 font-medium opacity-60">
@@ -90,11 +90,10 @@ export function BrandingHub() {
90
90
  type="button"
91
91
  onClick={handleSave}
92
92
  disabled={saving}
93
- className={`flex items-center gap-2 px-8 py-3 rounded-2xl font-black text-sm shadow-xl transition-all active:scale-95 ${
94
- saved
95
- ? "bg-green-500 text-white"
96
- : "bg-[var(--kyro-primary)] text-white hover:shadow-[var(--kyro-primary)]"
97
- }`}
93
+ className={`flex items-center gap-2 px-8 py-3 rounded-2xl font-bold text-sm shadow-xl transition-all active:scale-95 ${saved
94
+ ? "bg-green-500 text-white"
95
+ : "bg-[var(--kyro-primary)] text-white hover:shadow-[var(--kyro-primary)]"
96
+ }`}
98
97
  >
99
98
  {saving ? (
100
99
  <RefreshCcw className="w-4 h-4 animate-spin" />
@@ -117,12 +116,12 @@ export function BrandingHub() {
117
116
  <section className="surface-tile p-8 space-y-8">
118
117
  <div className="flex items-center gap-3 mb-2">
119
118
  <Tag className="w-5 h-5 text-[var(--kyro-primary)]" />
120
- <h2 className="text-xl font-black tracking-tight">Core Identity</h2>
119
+ <h2 className="text-xl font-bold tracking-tight">Core Identity</h2>
121
120
  </div>
122
121
 
123
122
  <div className="space-y-6">
124
123
  <div className="space-y-2">
125
- <label className="text-[10px] font-black uppercase tracking-[0.2em] opacity-40">
124
+ <label className="text-[10px] font-bold tracking-[0.2em] opacity-40">
126
125
  Site Public Name
127
126
  </label>
128
127
  <input
@@ -135,7 +134,7 @@ export function BrandingHub() {
135
134
  </div>
136
135
 
137
136
  <div className="space-y-2">
138
- <label className="text-[10px] font-black uppercase tracking-[0.2em] opacity-40">
137
+ <label className="text-[10px] font-bold tracking-[0.2em] opacity-40">
139
138
  Admin Dashboard Title
140
139
  </label>
141
140
  <input
@@ -148,7 +147,7 @@ export function BrandingHub() {
148
147
  </div>
149
148
 
150
149
  <div className="space-y-2">
151
- <label className="text-[10px] font-black uppercase tracking-[0.2em] opacity-40">
150
+ <label className="text-[10px] font-bold tracking-[0.2em] opacity-40">
152
151
  System Greeting
153
152
  </label>
154
153
  <textarea
@@ -166,14 +165,14 @@ export function BrandingHub() {
166
165
  <section className="surface-tile p-8 space-y-8">
167
166
  <div className="flex items-center gap-3 mb-2">
168
167
  <Palette className="w-5 h-5 text-[var(--kyro-primary)]" />
169
- <h2 className="text-xl font-black tracking-tight">
168
+ <h2 className="text-xl font-bold tracking-tight">
170
169
  Visual Aesthetic
171
170
  </h2>
172
171
  </div>
173
172
 
174
173
  <div className="space-y-8">
175
174
  <div className="space-y-4">
176
- <label className="text-[10px] font-black uppercase tracking-[0.2em] opacity-40">
175
+ <label className="text-[10px] font-bold tracking-[0.2em] opacity-40">
177
176
  Primary Brand Color
178
177
  </label>
179
178
  <div className="grid grid-cols-6 gap-3">
@@ -191,13 +190,13 @@ export function BrandingHub() {
191
190
  </div>
192
191
 
193
192
  <div className="space-y-4 pt-4 border-t border-[var(--kyro-border)]">
194
- <label className="text-[10px] font-black uppercase tracking-[0.2em] opacity-40">
193
+ <label className="text-[10px] font-bold tracking-[0.2em] opacity-40">
195
194
  Project Logo (SVG/PNG)
196
195
  </label>
197
196
  <div className="flex items-center gap-6">
198
197
  <div className="w-20 h-20 rounded-2xl bg-[var(--kyro-bg-secondary)] border-2 border-dashed border-[var(--kyro-border)] flex flex-col items-center justify-center text-[var(--kyro-text-secondary)] hover:border-[var(--kyro-primary)] hover:text-[var(--kyro-primary)] cursor-pointer transition-all">
199
198
  <ImageIcon className="w-6 h-6 mb-1 opacity-40" />
200
- <span className="text-[8px] font-black uppercase">
199
+ <span className="text-[8px] font-bold ">
201
200
  Upload
202
201
  </span>
203
202
  </div>
@@ -219,7 +218,7 @@ export function BrandingHub() {
219
218
  <section className="lg:col-span-2 surface-tile p-8 overflow-hidden relative group">
220
219
  <div className="absolute top-0 right-0 p-8 flex items-center gap-2 text-[var(--kyro-primary)]">
221
220
  <Sparkles className="w-4 h-4" />
222
- <span className="text-[10px] font-black uppercase tracking-widest">
221
+ <span className="text-[10px] font-bold tracking-widest">
223
222
  Live Preview
224
223
  </span>
225
224
  </div>
@@ -227,11 +226,11 @@ export function BrandingHub() {
227
226
  <div className="max-w-2xl mx-auto py-12 text-center space-y-6">
228
227
  <div className="inline-flex items-center gap-3 px-4 py-2 bg-[var(--kyro-bg-secondary)] rounded-full border border-[var(--kyro-border)]">
229
228
  <span className="w-2 h-2 rounded-full bg-green-500 animate-pulse" />
230
- <span className="text-[10px] font-black uppercase tracking-widest opacity-60">
229
+ <span className="text-[10px] font-bold tracking-widest opacity-60">
231
230
  {adminTitle} Online
232
231
  </span>
233
232
  </div>
234
- <h2 className="text-5xl font-black tracking-tighter leading-none italic">
233
+ <h2 className="text-5xl font-bold tracking-tighter leading-none italic">
235
234
  Welcome to {siteName}.
236
235
  </h2>
237
236
  <p className="text-xl font-medium text-[var(--kyro-text-secondary)] opacity-60">
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
  import { Dropdown, DropdownItem, DropdownSeparator } from "./ui/Dropdown";
3
- import { CountBadge } from "./StatusBadge";
3
+ import { CountBadge } from "./ui/Badge";
4
4
 
5
5
  interface BulkAction {
6
6
  label: string;
@@ -3,6 +3,9 @@ import { apiPost } from "../lib/api";
3
3
  import type { KyroConfig, CollectionConfig } from "@kyro-cms/core/client";
4
4
  import { AutoForm } from "./AutoForm";
5
5
  import { Spinner } from "./ui/Spinner";
6
+ import { PageHeader } from "./ui/PageHeader";
7
+ import { adminPath } from "../lib/paths";
8
+
6
9
 
7
10
  interface CreateViewProps {
8
11
  config: KyroConfig;
@@ -25,8 +28,9 @@ export function CreateView({
25
28
  const fields = collection.fields || [];
26
29
  const label = collection.label || collection.slug;
27
30
 
28
- const handleSubmit = async (e: React.FormEvent) => {
29
- e.preventDefault();
31
+ const handleSubmit = async (e?: React.FormEvent) => {
32
+ if (e) e.preventDefault();
33
+
30
34
  try {
31
35
  setSaving(true);
32
36
  await apiPost(`/api/${collection.slug}`, data);
@@ -40,44 +44,26 @@ export function CreateView({
40
44
 
41
45
  return (
42
46
  <div className="kyro-detail">
43
- <div className="kyro-detail-header">
44
- <button type="button" className="kyro-detail-back" onClick={onCancel}>
45
- <svg
46
- width="18"
47
- height="18"
48
- viewBox="0 0 24 24"
49
- fill="none"
50
- stroke="currentColor"
51
- strokeWidth="2"
52
- >
53
- <path d="M19 12H5M12 19l-7-7 7-7" />
54
- </svg>
55
- </button>
56
- <h2 className="kyro-detail-title">Create {label}</h2>
57
- <div className="kyro-detail-actions">
58
- <button
59
- type="button"
60
- className="kyro-btn kyro-btn-secondary kyro-btn-md"
61
- onClick={onCancel}
62
- disabled={saving}
63
- >
64
- Cancel
65
- </button>
66
- <button
67
- type="button"
68
- className="kyro-btn kyro-btn-primary kyro-btn-md"
69
- onClick={handleSubmit}
70
- disabled={saving}
71
- >
72
- {saving ? <Spinner size="sm" /> : `Create ${label}`}
73
- </button>
74
- </div>
75
- </div>
47
+ <PageHeader
48
+ back={{ onClick: onCancel }}
49
+ breadcrumbs={[
50
+ { label: "Collections" },
51
+ { label: label, href: `${adminPath}/${collection.slug}` },
52
+ { label: "New" }
53
+ ]}
54
+ title={`Create ${collection.singularLabel || label}`}
55
+ action={{
56
+ label: saving ? "Creating..." : `Create ${collection.singularLabel || label}`,
57
+ onClick: () => handleSubmit(),
58
+ }}
59
+
60
+ />
61
+
76
62
 
77
63
  <div className="kyro-detail-body">
78
64
  <div className="kyro-card">
79
65
  <div className="kyro-card-content">
80
- <form onSubmit={handleSubmit}>
66
+ <form>
81
67
  <AutoForm
82
68
  config={{ ...collection, fields }}
83
69
  data={data}
@@ -1,23 +1,18 @@
1
1
  import React, { useState, useEffect } from "react";
2
- import {
3
- LayoutDashboard,
4
- FileText,
5
- Image as ImageIcon,
6
- Users,
7
- Plus,
8
- ArrowUpRight,
9
- Activity,
10
- Clock,
11
- ArrowRight,
12
- } from "lucide-react";
2
+ import { LayoutDashboard, FileText, Image as ImageIcon, Users, Plus, ArrowUpRight, Activity, Clock as ClockIcon, ArrowRight } from "./ui/icons";
3
+ import { useAuthStore } from "../lib/stores";
4
+ import { authCollectionSlugs } from "../lib/config";
5
+ import { PageHeader } from "./ui/PageHeader";
6
+
13
7
 
14
8
  interface DashboardProps {
15
- collections: any;
9
+ collections: Record<string, unknown>;
16
10
  onNavigate: (view: string, collection?: string) => void;
17
- user: any;
11
+ user: Record<string, unknown> | null;
18
12
  }
19
13
 
20
14
  export function Dashboard({ collections, onNavigate, user }: DashboardProps) {
15
+ const { permissions } = useAuthStore();
21
16
  const [stats, setStats] = useState({
22
17
  totalDocs: 0,
23
18
  totalMedia: 0,
@@ -67,39 +62,30 @@ export function Dashboard({ collections, onNavigate, user }: DashboardProps) {
67
62
  time: "3h ago",
68
63
  },
69
64
  ],
70
- } as any);
65
+ } as { totalDocs: number; totalMedia: number; totalUsers: number; recentActivity: Array<{ id: number; type: string; user: string; doc: string; collection: string; time: string }> });
71
66
  setLoading(false);
72
67
  }, 800);
73
68
  return () => clearTimeout(timer);
74
69
  }, []);
75
70
 
76
- const collectionList = Object.entries(collections);
71
+ const collectionList = Object.entries(collections).filter(
72
+ ([slug]) =>
73
+ !authCollectionSlugs.includes(slug) &&
74
+ permissions?.collections?.[slug]?.read !== false,
75
+ );
77
76
 
78
77
  return (
79
78
  <div className="w-full space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-700 px-8 pb-12">
80
- {/* Header Section */}
81
- <div className="flex flex-col lg:flex-row lg:items-center justify-between gap-6 pt-4">
82
- <div>
83
- <h1 className="text-4xl font-black tracking-tighter text-[var(--kyro-text-primary)]">
84
- Welcome back,{" "}
85
- <span className="text-[var(--kyro-primary)]">
86
- {user?.email?.split("@")[0] || "Admin"}
87
- </span>
88
- </h1>
89
- <p className="text-[var(--kyro-text-secondary)] mt-1 font-medium italic opacity-70">
90
- Everything looks great in your command center today.
91
- </p>
92
- </div>
93
- <div className="flex items-center gap-3">
94
- <button type="button"
95
- onClick={() => onNavigate("create", collectionList[0]?.[0])}
96
- className="flex items-center gap-2 px-6 py-3 bg-[var(--kyro-sidebar-active)] text-[var(--kyro-sidebar-text-active)] rounded-2xl font-black text-sm shadow-xl active:scale-95 transition-all"
97
- >
98
- <Plus className="w-4 h-4" />
99
- New Document
100
- </button>
101
- </div>
102
- </div>
79
+ <PageHeader
80
+ title={`Welcome back, ${user?.email?.split("@")[0] || "Admin"}`}
81
+ description="Everything looks great in your command center today."
82
+ action={collectionList.length > 0 && permissions?.collections?.[collectionList[0]?.[0]]?.create !== false ? {
83
+ label: "New Document",
84
+ onClick: () => onNavigate("create", collectionList[0]?.[0]),
85
+ icon: Plus,
86
+ } : undefined}
87
+ />
88
+
103
89
 
104
90
  {/* Stats Grid */}
105
91
  <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
@@ -134,10 +120,10 @@ export function Dashboard({ collections, onNavigate, user }: DashboardProps) {
134
120
  className="surface-tile p-6 flex items-center justify-between group hover:border-[var(--kyro-primary)] transition-all duration-500 cursor-default"
135
121
  >
136
122
  <div>
137
- <p className="text-[10px] font-black uppercase tracking-[0.2em] opacity-40 mb-1">
123
+ <p className="text-[10px] font-bold tracking-[0.2em] opacity-40 mb-1">
138
124
  {stat.label}
139
125
  </p>
140
- <h3 className="text-3xl font-black tracking-tighter">
126
+ <h3 className="text-3xl font-bold tracking-tighter">
141
127
  {loading ? "..." : stat.value}
142
128
  </h3>
143
129
  </div>
@@ -154,11 +140,11 @@ export function Dashboard({ collections, onNavigate, user }: DashboardProps) {
154
140
  {/* Main Content Area: Collections & Insights */}
155
141
  <div className="space-y-8 min-w-0">
156
142
  <section className="surface-tile p-8">
157
- <h2 className="text-xl font-black mb-1 tracking-tight flex items-center gap-2">
143
+ <h2 className="text-xl font-bold mb-1 tracking-tight flex items-center gap-2">
158
144
  <LayoutDashboard className="w-5 h-5 opacity-40" />
159
145
  Content Growth
160
146
  </h2>
161
- <p className="text-[10px] font-black uppercase tracking-widest opacity-40 mb-8">
147
+ <p className="text-[10px] font-bold tracking-widest opacity-40 mb-8">
162
148
  Snapshot of document velocity over the last 7 days
163
149
  </p>
164
150
 
@@ -204,7 +190,7 @@ export function Dashboard({ collections, onNavigate, user }: DashboardProps) {
204
190
  className="filter drop-shadow-[0_0_8px_var(--kyro-primary)]"
205
191
  />
206
192
  </svg>
207
- <div className="flex justify-between mt-4 text-[10px] font-black opacity-30 tracking-widest uppercase">
193
+ <div className="flex justify-between mt-4 text-[10px] font-bold opacity-30 tracking-widest ">
208
194
  <span>Mon</span>
209
195
  <span>Tue</span>
210
196
  <span>Wed</span>
@@ -216,14 +202,14 @@ export function Dashboard({ collections, onNavigate, user }: DashboardProps) {
216
202
  </div>
217
203
 
218
204
  <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
219
- {collectionList.map(([slug, config]: [string, any]) => (
205
+ {collectionList.map(([slug, config]: [string, Record<string, unknown>]) => (
220
206
  <div
221
207
  key={slug}
222
208
  onClick={() => onNavigate("list", slug)}
223
209
  className="p-6 rounded-2xl border border-[var(--kyro-border)] hover:border-[var(--kyro-primary)] bg-[var(--kyro-bg-secondary)] hover:bg-[var(--kyro-surface)] transition-all cursor-pointer group"
224
210
  >
225
211
  <div className="flex items-center justify-between mb-2">
226
- <h3 className="font-black text-lg tracking-tight group-hover:text-[var(--kyro-primary)] transition-colors">
212
+ <h3 className="font-bold text-lg tracking-tight group-hover:text-[var(--kyro-primary)] transition-colors">
227
213
  {config.label || slug}
228
214
  </h3>
229
215
  <ArrowUpRight className="w-4 h-4 opacity-0 group-hover:opacity-100 transition-all" />
@@ -245,51 +231,41 @@ export function Dashboard({ collections, onNavigate, user }: DashboardProps) {
245
231
  <section className="grid grid-cols-1 md:grid-cols-2 gap-6">
246
232
  <section className="surface-tile p-6 border border-[var(--kyro-border)] hover:border-[var(--kyro-primary)] transition-all cursor-pointer group">
247
233
  <div className="flex items-center justify-between mb-4">
248
- <h3 className="text-xl font-black tracking-tight group-hover:text-[var(--kyro-primary)] transition-colors flex items-center gap-2">
234
+ <h3 className="text-xl font-bold tracking-tight group-hover:text-[var(--kyro-primary)] transition-colors flex items-center gap-2">
249
235
  <Plus className="w-5 h-5" />
250
236
  Quick Links
251
237
  </h3>
252
238
  </div>
253
239
  <div className="space-y-3">
254
- <button type="button"
255
- onClick={() => onNavigate("new", "posts")}
256
- className="w-full flex items-center justify-between p-4 bg-[var(--kyro-bg-secondary)] border border-[var(--kyro-border)] rounded-xl hover:bg-[var(--kyro-surface-accent)] hover:border-[var(--kyro-primary)] transition-all group/btn"
257
- >
258
- <span className="text-sm font-bold text-[var(--kyro-text-primary)]">
259
- New Post
260
- </span>
261
- <Plus className="w-4 h-4 text-[var(--kyro-text-secondary)] group-hover/btn:text-[var(--kyro-primary)]" />
262
- </button>
263
- <button type="button"
264
- onClick={() => onNavigate("new", "products")}
265
- className="w-full flex items-center justify-between p-4 bg-[var(--kyro-bg-secondary)] border border-[var(--kyro-border)] rounded-xl hover:bg-[var(--kyro-surface-accent)] hover:border-[var(--kyro-primary)] transition-all group/btn"
266
- >
267
- <span className="text-sm font-bold text-[var(--kyro-text-primary)]">
268
- New Product
269
- </span>
270
- <Plus className="w-4 h-4 text-[var(--kyro-text-secondary)] group-hover/btn:text-[var(--kyro-primary)]" />
271
- </button>
272
- <button type="button"
273
- onClick={() => onNavigate("new", "pages")}
274
- className="w-full flex items-center justify-between p-4 bg-[var(--kyro-bg-secondary)] border border-[var(--kyro-border)] rounded-xl hover:bg-[var(--kyro-surface-accent)] hover:border-[var(--kyro-primary)] transition-all group/btn"
275
- >
276
- <span className="text-sm font-bold text-[var(--kyro-text-primary)]">
277
- New Page
278
- </span>
279
- <Plus className="w-4 h-4 text-[var(--kyro-text-secondary)] group-hover/btn:text-[var(--kyro-primary)]" />
280
- </button>
240
+ {collectionList
241
+ .filter(([slug]) => permissions?.collections?.[slug]?.create !== false)
242
+ .map(([slug]) => (
243
+ <button type="button"
244
+ key={slug}
245
+ onClick={() => onNavigate("new", slug)}
246
+ className="w-full flex items-center justify-between p-4 bg-[var(--kyro-bg-secondary)] border border-[var(--kyro-border)] rounded-xl hover:bg-[var(--kyro-surface-accent)] hover:border-[var(--kyro-primary)] transition-all group/btn"
247
+ >
248
+ <span className="text-sm font-medium text-[var(--kyro-text-primary)]">
249
+ New {(collections[slug] as Record<string, unknown>)?.singularLabel || (collections[slug] as Record<string, unknown>)?.label || slug}
250
+ </span>
251
+ <Plus className="w-4 h-4 text-[var(--kyro-text-secondary)] group-hover/btn:text-[var(--kyro-primary)]" />
252
+ </button>
253
+ ))}
254
+ {collectionList.every(([slug]) => permissions?.collections?.[slug]?.create === false) && (
255
+ <p className="text-sm text-[var(--kyro-text-secondary)] py-2">No collections available for creation.</p>
256
+ )}
281
257
  </div>
282
258
  </section>
283
259
 
284
260
  <div className="surface-tile p-8 bg-[#0f172a] text-white border-none shadow-2xl shadow-blue-500/10 overflow-hidden relative group cursor-pointer">
285
261
  <div className="relative z-10">
286
- <h3 className="text-2xl font-black tracking-tighter mb-2">
262
+ <h3 className="text-2xl font-bold tracking-tighter mb-2">
287
263
  Media Library
288
264
  </h3>
289
265
  <p className="opacity-80 text-sm font-medium mb-6">
290
266
  Manage high-fidelity assets with our liquid masonry gallery.
291
267
  </p>
292
- <div className="flex items-center gap-2 font-black text-xs uppercase tracking-widest text-blue-400">
268
+ <div className="flex items-center gap-2 font-bold text-xs tracking-widest text-blue-400">
293
269
  Open Assets{" "}
294
270
  <ArrowRight className="w-4 h-4 group-hover:translate-x-1 transition-transform" />
295
271
  </div>
@@ -302,23 +278,23 @@ export function Dashboard({ collections, onNavigate, user }: DashboardProps) {
302
278
  {/* Sidebar Area: Recent Activity */}
303
279
  <div className="space-y-6">
304
280
  <section className="surface-tile p-8">
305
- <h2 className="text-xl font-black mb-6 tracking-tight flex items-center gap-2">
306
- <Clock className="w-5 h-5 opacity-40" />
281
+ <h2 className="text-xl font-bold mb-6 tracking-tight flex items-center gap-2">
282
+ <ClockIcon className="w-5 h-5 opacity-40" />
307
283
  Recent Activity
308
284
  </h2>
309
285
  <div className="space-y-6">
310
- {stats.recentActivity.map((act: any) => (
286
+ {stats.recentActivity.map((act: Record<string, unknown>) => (
311
287
  <div key={act.id} className="flex gap-4 group">
312
288
  <div className="mt-1">
313
289
  <div className="w-8 h-8 rounded-full bg-[var(--kyro-bg-secondary)] flex items-center justify-center border border-[var(--kyro-border)] group-hover:bg-[var(--kyro-primary)] transition-colors">
314
- <span className="text-[10px] font-black">
290
+ <span className="text-[10px] font-bold">
315
291
  {act.user[0]}
316
292
  </span>
317
293
  </div>
318
294
  </div>
319
295
  <div className="flex-1 border-b border-[var(--kyro-border)] pb-4 group-last:border-none">
320
296
  <p className="text-sm font-medium text-[var(--kyro-text-primary)] leading-snug">
321
- <span className="font-black">{act.user}</span>{" "}
297
+ <span className="font-bold">{act.user}</span>{" "}
322
298
  {act.type === "create"
323
299
  ? "created"
324
300
  : act.type === "edit"
@@ -331,20 +307,20 @@ export function Dashboard({ collections, onNavigate, user }: DashboardProps) {
331
307
  </span>{" "}
332
308
  in <span className="opacity-60">{act.collection}</span>
333
309
  </p>
334
- <span className="text-[10px] font-bold uppercase opacity-40 mt-1 block">
310
+ <span className="text-[10px] font-bold opacity-40 mt-1 block">
335
311
  {act.time}
336
312
  </span>
337
313
  </div>
338
314
  </div>
339
315
  ))}
340
316
  </div>
341
- <button type="button" className="w-full mt-6 py-3 text-xs font-black uppercase tracking-widest text-[var(--kyro-text-secondary)] hover:text-[var(--kyro-text-primary)] transition-colors border-t border-[var(--kyro-border)] pt-6">
317
+ <button type="button" className="w-full mt-6 py-3 text-xs font-bold tracking-widest text-[var(--kyro-text-secondary)] hover:text-[var(--kyro-text-primary)] transition-colors border-t border-[var(--kyro-border)] pt-6">
342
318
  View Audit Logs
343
319
  </button>
344
320
  </section>
345
321
 
346
322
  <section className="surface-tile p-8">
347
- <h2 className="text-xl font-black mb-6 tracking-tight flex items-center gap-2">
323
+ <h2 className="text-xl font-bold mb-6 tracking-tight flex items-center gap-2">
348
324
  <Activity className="w-5 h-5 opacity-40" />
349
325
  System Status
350
326
  </h2>
@@ -375,7 +351,7 @@ export function Dashboard({ collections, onNavigate, user }: DashboardProps) {
375
351
  {sys.label}
376
352
  </span>
377
353
  <div className="flex items-center gap-2">
378
- <span className="text-[10px] font-black uppercase tracking-widest opacity-60">
354
+ <span className="text-[10px] font-bold tracking-widest opacity-60">
379
355
  {sys.status}
380
356
  </span>
381
357
  <div