@kyro-cms/admin 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/README.md +149 -51
  2. package/package.json +53 -6
  3. package/src/collections/auth/index.ts +2 -2
  4. package/src/collections/portfolio/index.ts +343 -0
  5. package/src/components/ActionBar.tsx +153 -16
  6. package/src/components/Admin.tsx +136 -27
  7. package/src/components/ApiExplorer.tsx +325 -0
  8. package/src/components/ApiKeysManager.tsx +563 -0
  9. package/src/components/AuditLogsPage.tsx +664 -0
  10. package/src/components/AutoForm.tsx +1417 -661
  11. package/src/components/BrandingHub.tsx +267 -0
  12. package/src/components/BulkActionsBar.tsx +3 -3
  13. package/src/components/CreateView.tsx +3 -3
  14. package/src/components/Dashboard.tsx +393 -0
  15. package/src/components/DetailView.tsx +199 -57
  16. package/src/components/DeveloperCenter.tsx +403 -0
  17. package/src/components/EnhancedListView.tsx +786 -0
  18. package/src/components/GraphQLExplorer.tsx +675 -0
  19. package/src/components/GraphQLPlayground.tsx +627 -0
  20. package/src/components/ListView.tsx +191 -53
  21. package/src/components/MediaGallery.tsx +1569 -0
  22. package/src/components/Modal.tsx +149 -0
  23. package/src/components/RestPlayground.tsx +951 -0
  24. package/src/components/Sidebar.astro +237 -0
  25. package/src/components/UserManagement.tsx +204 -0
  26. package/src/components/VersionHistoryPanel.tsx +3 -3
  27. package/src/components/WebhookManager.tsx +608 -0
  28. package/src/components/blocks/AccordionBlock.tsx +97 -0
  29. package/src/components/blocks/ArrayBlock.tsx +75 -0
  30. package/src/components/blocks/BlockEditModal.MARKER +12 -0
  31. package/src/components/blocks/BlockEditModal.tsx +774 -0
  32. package/src/components/blocks/ButtonBlock.tsx +165 -0
  33. package/src/components/blocks/ChildBlocksTree.tsx +551 -0
  34. package/src/components/blocks/CodeBlock.tsx +66 -0
  35. package/src/components/blocks/ColumnsBlock.tsx +151 -0
  36. package/src/components/blocks/DividerBlock.tsx +43 -0
  37. package/src/components/blocks/FileBlock.tsx +64 -0
  38. package/src/components/blocks/HeadingBlock.tsx +81 -0
  39. package/src/components/blocks/HeroBlock.tsx +157 -0
  40. package/src/components/blocks/ImageBlock.tsx +83 -0
  41. package/src/components/blocks/LinkBlock.tsx +71 -0
  42. package/src/components/blocks/ListBlock.tsx +39 -0
  43. package/src/components/blocks/ParagraphBlock.tsx +61 -0
  44. package/src/components/blocks/RelationshipBlock.tsx +279 -0
  45. package/src/components/blocks/VStackBlock.tsx +75 -0
  46. package/src/components/blocks/VideoBlock.tsx +45 -0
  47. package/src/components/blocks/index.ts +10 -0
  48. package/src/components/fields/BlocksField.tsx +323 -0
  49. package/src/components/fields/CheckboxField.tsx +15 -9
  50. package/src/components/fields/CodeField.tsx +234 -0
  51. package/src/components/fields/DateField.tsx +38 -11
  52. package/src/components/fields/EditorClient.tsx +271 -0
  53. package/src/components/fields/FileField.tsx +390 -0
  54. package/src/components/fields/HybridContentField.tsx +109 -0
  55. package/src/components/fields/ImageField.tsx +429 -0
  56. package/src/components/fields/JSONField.tsx +361 -0
  57. package/src/components/fields/MarkdownField.tsx +282 -0
  58. package/src/components/fields/NumberField.tsx +42 -12
  59. package/src/components/fields/PortableTextField.tsx +143 -0
  60. package/src/components/fields/PortableTextRenderer.tsx +68 -0
  61. package/src/components/fields/RelationshipField.tsx +231 -59
  62. package/src/components/fields/SelectField.tsx +25 -15
  63. package/src/components/fields/TextField.tsx +45 -14
  64. package/src/components/fields/extensions/blockComponents.tsx +237 -0
  65. package/src/components/fields/extensions/blocksStore.ts +273 -0
  66. package/src/components/fields/index.ts +13 -0
  67. package/src/components/index.ts +1 -2
  68. package/src/components/layout/Header.tsx +2 -2
  69. package/src/components/layout/Layout.tsx +2 -2
  70. package/src/components/ui/Badge.tsx +9 -4
  71. package/src/components/ui/BlockDrawer.tsx +79 -0
  72. package/src/components/ui/Button.tsx +1 -1
  73. package/src/components/ui/CommandPalette.tsx +362 -0
  74. package/src/components/ui/CommandPaletteWrapper.tsx +97 -0
  75. package/src/components/ui/Dropdown.tsx +1 -1
  76. package/src/components/ui/Modal.tsx +37 -12
  77. package/src/components/ui/PromptModal.tsx +94 -0
  78. package/src/components/ui/SlidePanel.tsx +43 -16
  79. package/src/components/ui/Toast.tsx +80 -14
  80. package/src/env.d.ts +16 -0
  81. package/src/env.ts +20 -0
  82. package/src/index.ts +0 -1
  83. package/src/layouts/AdminLayout.astro +164 -170
  84. package/src/layouts/AuthLayout.astro +23 -6
  85. package/src/lib/MediaService.ts +541 -0
  86. package/src/lib/auth/sqlite-adapter.ts +319 -0
  87. package/src/lib/config.ts +22 -6
  88. package/src/lib/dataStore.ts +132 -74
  89. package/src/lib/db/adapter.ts +54 -0
  90. package/src/lib/db/drizzle-mysql-adapter.ts +194 -0
  91. package/src/lib/db/drizzle-mysql-auth-adapter.ts +327 -0
  92. package/src/lib/db/drizzle-postgres-adapter.ts +202 -0
  93. package/src/lib/db/drizzle-postgres-auth-adapter.ts +304 -0
  94. package/src/lib/db/drizzle-sqlite-adapter.ts +227 -0
  95. package/src/lib/db/drizzle-sqlite-auth-adapter.ts +548 -0
  96. package/src/lib/db/index.ts +449 -0
  97. package/src/lib/db/mongodb-adapter.ts +207 -0
  98. package/src/lib/db/mongodb-auth-adapter.ts +305 -0
  99. package/src/lib/db/schema/mysql-auth.ts +113 -0
  100. package/src/lib/db/schema/mysql-content.ts +20 -0
  101. package/src/lib/db/schema/postgres-auth.ts +116 -0
  102. package/src/lib/db/schema/postgres-content.ts +35 -0
  103. package/src/lib/db/schema/postgres-media.ts +52 -0
  104. package/src/lib/db/schema/postgres-settings.ts +11 -0
  105. package/src/lib/db/schema/sqlite-auth.ts +112 -0
  106. package/src/lib/db/schema/sqlite-content.ts +20 -0
  107. package/src/lib/graphql/index.ts +1 -0
  108. package/src/lib/graphql/schema.ts +443 -0
  109. package/src/lib/rate-limit.ts +267 -0
  110. package/src/lib/storage.ts +374 -0
  111. package/src/lib/store.ts +85 -0
  112. package/src/middleware.ts +70 -11
  113. package/src/pages/[collection]/[id].astro +178 -122
  114. package/src/pages/[collection]/index.astro +24 -156
  115. package/src/pages/admin/api-explorer.astro +98 -0
  116. package/src/pages/admin/graphql-explorer.astro +40 -0
  117. package/src/pages/admin/graphql.astro +97 -0
  118. package/src/pages/admin/index.astro +200 -139
  119. package/src/pages/admin/keys.astro +8 -0
  120. package/src/pages/admin/rest-playground.astro +44 -0
  121. package/src/pages/admin/webhooks.astro +8 -0
  122. package/src/pages/api/[collection]/[id]/publish.ts +44 -0
  123. package/src/pages/api/[collection]/[id]/unpublish.ts +42 -0
  124. package/src/pages/api/[collection]/[id]/versions.ts +36 -0
  125. package/src/pages/api/[collection]/[id].ts +102 -159
  126. package/src/pages/api/[collection]/index.ts +151 -230
  127. package/src/pages/api/auth/[id].ts +48 -69
  128. package/src/pages/api/auth/audit-logs.ts +20 -43
  129. package/src/pages/api/auth/login.ts +159 -45
  130. package/src/pages/api/auth/logout.ts +42 -24
  131. package/src/pages/api/auth/refresh.ts +119 -0
  132. package/src/pages/api/auth/register.ts +110 -40
  133. package/src/pages/api/auth/users.ts +22 -97
  134. package/src/pages/api/collections.ts +59 -0
  135. package/src/pages/api/globals/[slug]/test.ts +172 -0
  136. package/src/pages/api/globals/[slug].ts +42 -0
  137. package/src/pages/api/graphql.ts +90 -0
  138. package/src/pages/api/health.ts +417 -40
  139. package/src/pages/api/keys/[id].ts +26 -0
  140. package/src/pages/api/keys/index.ts +75 -0
  141. package/src/pages/api/media/[id].ts +309 -0
  142. package/src/pages/api/media/folders.ts +609 -0
  143. package/src/pages/api/media/index.ts +146 -0
  144. package/src/pages/api/media/resize.ts +267 -0
  145. package/src/pages/api/search.ts +82 -0
  146. package/src/pages/api/slug-availability.ts +70 -0
  147. package/src/pages/api/storage-config.ts +20 -0
  148. package/src/pages/api/storage-status.ts +206 -0
  149. package/src/pages/api/upload.ts +334 -0
  150. package/src/pages/api/webhooks/index.ts +71 -0
  151. package/src/pages/audit/index.astro +2 -104
  152. package/src/pages/login.astro +11 -11
  153. package/src/pages/media.astro +10 -0
  154. package/src/pages/preview/[collection]/[id].astro +178 -0
  155. package/src/pages/register.astro +13 -13
  156. package/src/pages/roles/index.astro +21 -21
  157. package/src/pages/settings/[slug].astro +162 -0
  158. package/src/pages/settings/index.astro +9 -0
  159. package/src/pages/users/[id].astro +29 -21
  160. package/src/pages/users/index.astro +22 -17
  161. package/src/pages/users/new.astro +18 -17
  162. package/src/styles/main.css +553 -128
  163. package/src/components/layout/Sidebar.tsx +0 -497
@@ -34,6 +34,28 @@ export function ActionBar({
34
34
  publishedAt,
35
35
  updatedAt,
36
36
  }: ActionBarProps) {
37
+ const styles = {
38
+ btn: {
39
+ padding: "0.5rem 1rem",
40
+ borderRadius: "0.75rem",
41
+ fontWeight: 600,
42
+ fontSize: "0.875rem",
43
+ transition: "all 0.2s",
44
+ border: "none",
45
+ cursor: "pointer",
46
+ },
47
+ idle: { backgroundColor: "#6b7280", color: "white" },
48
+ saving: {
49
+ backgroundColor: "#9ca3af",
50
+ color: "white",
51
+ opacity: 0.7,
52
+ cursor: "not-allowed",
53
+ },
54
+ saved: { backgroundColor: "#22c55e", color: "white" },
55
+ error: { backgroundColor: "#ef4444", color: "white" },
56
+ changes: { backgroundColor: "#eab308", color: "black" },
57
+ };
58
+
37
59
  const getSaveStatusText = () => {
38
60
  if (saveStatus === "saving") return "Saving...";
39
61
  if (saveStatus === "saved") return "Saved";
@@ -42,18 +64,92 @@ export function ActionBar({
42
64
  return null;
43
65
  };
44
66
 
67
+ const getSaveButtonClass = () => {
68
+ const base = "kyro-btn kyro-btn-md";
69
+ if (saveStatus === "saving") return `${base} opacity-50 cursor-wait`;
70
+ if (saveStatus === "saved")
71
+ return `${base} bg-green-500 hover:bg-green-600 text-white`;
72
+ if (saveStatus === "error")
73
+ return `${base} bg-red-500 hover:bg-red-600 text-white`;
74
+ if (hasChanges)
75
+ return `${base} bg-yellow-500 hover:bg-yellow-600 text-black`;
76
+ return `${base} bg-gray-500 hover:bg-gray-600 text-white`;
77
+ };
78
+
45
79
  const getStatusBadge = () => {
46
80
  const statusConfig = {
47
- draft: { label: "Draft", class: "bg-gray-100 text-gray-600" },
48
- published: { label: "Published", class: "bg-green-100 text-green-700" },
49
- scheduled: { label: "Scheduled", class: "bg-blue-100 text-blue-700" },
50
- archived: { label: "Archived", class: "bg-yellow-100 text-yellow-700" },
81
+ draft: {
82
+ label: "Draft",
83
+ class: "bg-gray-100 text-gray-600 border-gray-200",
84
+ icon: (
85
+ <svg
86
+ width="12"
87
+ height="12"
88
+ viewBox="0 0 24 24"
89
+ fill="none"
90
+ stroke="currentColor"
91
+ strokeWidth="2.5"
92
+ >
93
+ <path d="M12 2v20M2 12h20" />
94
+ </svg>
95
+ ),
96
+ },
97
+ published: {
98
+ label: "Published",
99
+ class: "bg-green-100 text-green-700 border-green-200",
100
+ icon: (
101
+ <svg
102
+ width="12"
103
+ height="12"
104
+ viewBox="0 0 24 24"
105
+ fill="none"
106
+ stroke="currentColor"
107
+ strokeWidth="2.5"
108
+ >
109
+ <path d="M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z" />
110
+ </svg>
111
+ ),
112
+ },
113
+ scheduled: {
114
+ label: "Scheduled",
115
+ class: "bg-blue-100 text-blue-700 border-blue-200",
116
+ icon: (
117
+ <svg
118
+ width="12"
119
+ height="12"
120
+ viewBox="0 0 24 24"
121
+ fill="none"
122
+ stroke="currentColor"
123
+ strokeWidth="2.5"
124
+ >
125
+ <circle cx="12" cy="12" r="10" />
126
+ <path d="M12 6v6l4 2" />
127
+ </svg>
128
+ ),
129
+ },
130
+ archived: {
131
+ label: "Archived",
132
+ class: "bg-yellow-100 text-yellow-700 border-yellow-200",
133
+ icon: (
134
+ <svg
135
+ width="12"
136
+ height="12"
137
+ viewBox="0 0 24 24"
138
+ fill="none"
139
+ stroke="currentColor"
140
+ strokeWidth="2.5"
141
+ >
142
+ <path d="M21 8v13H3V8M1 3h22v5H1zM10 12h4" />
143
+ </svg>
144
+ ),
145
+ },
51
146
  };
52
147
  const config = statusConfig[status];
53
148
  return (
54
149
  <span
55
- className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${config.class}`}
150
+ className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-semibold border ${config.class}`}
56
151
  >
152
+ {config.icon}
57
153
  {config.label}
58
154
  </span>
59
155
  );
@@ -80,46 +176,87 @@ export function ActionBar({
80
176
  </span>
81
177
  )}
82
178
  </div>
83
- <div className="text-xs text-gray-400 space-y-0.5">
84
- {updatedAt && <div>Updated: {formatDate(updatedAt)}</div>}
179
+ <div className="text-xs space-y-0.5">
180
+ {updatedAt && (
181
+ <div className="text-gray-400">
182
+ Updated: {formatDate(updatedAt)}
183
+ </div>
184
+ )}
85
185
  {publishedAt && status === "published" && (
86
- <div>Published: {formatDate(publishedAt)}</div>
186
+ <div className="text-[var(--kyro-primary)] font-medium">
187
+ Published: {formatDate(publishedAt)}
188
+ </div>
87
189
  )}
88
190
  </div>
89
191
  </div>
90
192
 
91
193
  <div className="flex items-center gap-2">
92
194
  {status === "draft" && onPublish && (
93
- <button
195
+ <button type="button"
94
196
  onClick={onPublish}
95
197
  disabled={saveStatus === "saving"}
96
- className="kyro-btn kyro-btn-primary kyro-btn-md"
198
+ className="bg-[var(--kyro-primary)] hover:opacity-90 text-white kyro-btn-md flex items-center gap-2"
97
199
  >
200
+ <svg
201
+ width="16"
202
+ height="16"
203
+ viewBox="0 0 24 24"
204
+ fill="none"
205
+ stroke="currentColor"
206
+ strokeWidth="2.5"
207
+ >
208
+ <path d="M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z" />
209
+ </svg>
98
210
  Publish
99
211
  </button>
100
212
  )}
101
213
  {status === "published" && onUnpublish && (
102
- <button
214
+ <button type="button"
103
215
  onClick={onUnpublish}
104
216
  disabled={saveStatus === "saving"}
105
- className="kyro-btn kyro-btn-secondary kyro-btn-md"
217
+ className="kyro-btn kyro-btn-warning kyro-btn-md flex items-center gap-2"
106
218
  >
219
+ <svg
220
+ width="16"
221
+ height="16"
222
+ viewBox="0 0 24 24"
223
+ fill="none"
224
+ stroke="currentColor"
225
+ strokeWidth="2.5"
226
+ >
227
+ <path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8M3 3v5h5" />
228
+ </svg>
107
229
  Unpublish
108
230
  </button>
109
231
  )}
110
- <button
232
+ <button type="button"
111
233
  onClick={onSave}
112
234
  disabled={
113
235
  saveStatus === "saving" || (!hasChanges && saveStatus !== "error")
114
236
  }
115
- className="kyro-btn kyro-btn-secondary kyro-btn-md"
237
+ style={{
238
+ ...styles.btn,
239
+ ...(saveStatus === "saving"
240
+ ? styles.saving
241
+ : saveStatus === "saved"
242
+ ? styles.saved
243
+ : saveStatus === "error"
244
+ ? styles.error
245
+ : hasChanges
246
+ ? styles.changes
247
+ : styles.idle),
248
+ }}
116
249
  >
117
- {saveStatus === "saving" ? "Saving..." : "Save"}
250
+ {saveStatus === "saving"
251
+ ? "Saving..."
252
+ : saveStatus === "saved"
253
+ ? "Saved"
254
+ : "Save"}
118
255
  </button>
119
256
 
120
257
  <Dropdown
121
258
  trigger={
122
- <button className="kyro-btn kyro-btn-ghost kyro-btn-md p-2">
259
+ <button type="button" className="kyro-btn kyro-btn-ghost kyro-btn-md p-2">
123
260
  <svg
124
261
  width="16"
125
262
  height="16"
@@ -1,15 +1,32 @@
1
1
  import { useState, useEffect } from "react";
2
2
  import type { CollectionConfig, GlobalConfig } from "@kyro-cms/core";
3
- import { Sidebar } from "./layout/Sidebar";
4
3
  import { ListView } from "./ListView";
5
4
  import { DetailView } from "./DetailView";
6
5
  import { CreateView } from "./CreateView";
7
6
  import { LoginPage } from "./LoginPage";
7
+ import { Dashboard } from "./Dashboard";
8
+ import { UserManagement } from "./UserManagement";
9
+ import { BrandingHub } from "./BrandingHub";
10
+ import { DeveloperCenter } from "./DeveloperCenter";
11
+ import { WebhookManager } from "./WebhookManager";
12
+ import { MediaGallery } from "./MediaGallery";
13
+ import { CommandPalette } from "./ui/CommandPalette";
8
14
  import { Toast, ToastProvider } from "./ui/Toast";
9
15
  import { ThemeProvider, type ThemeMode } from "./ThemeProvider";
10
16
  import "../styles/main.css";
11
17
 
12
- type View = "list" | "detail" | "create" | "settings";
18
+ type View =
19
+ | "list"
20
+ | "detail"
21
+ | "create"
22
+ | "settings"
23
+ | "users"
24
+ | "roles"
25
+ | "audit"
26
+ | "media"
27
+ | "branding"
28
+ | "developer"
29
+ | "webhooks";
13
30
 
14
31
  export interface KyroAdminConfig {
15
32
  collections?: CollectionConfig[] | Record<string, CollectionConfig>;
@@ -82,10 +99,22 @@ export function Admin({ config, theme = "light", onThemeChange }: AdminProps) {
82
99
  const [currentView, setCurrentView] = useState<View>("list");
83
100
  const [selectedId, setSelectedId] = useState<string | null>(null);
84
101
  const [toasts, setToasts] = useState<ToastMessage[]>([]);
102
+ const [showCommandPalette, setShowCommandPalette] = useState(false);
85
103
 
86
104
  const collections = normalizeCollections(config.collections);
87
105
  const globals = normalizeGlobals(config.globals);
88
106
 
107
+ useEffect(() => {
108
+ const handleKeyDown = (e: KeyboardEvent) => {
109
+ if ((e.metaKey || e.ctrlKey) && e.key === "k") {
110
+ e.preventDefault();
111
+ setShowCommandPalette((prev) => !prev);
112
+ }
113
+ };
114
+ window.addEventListener("keydown", handleKeyDown);
115
+ return () => window.removeEventListener("keydown", handleKeyDown);
116
+ }, []);
117
+
89
118
  useEffect(() => {
90
119
  const token = localStorage.getItem("kyro_token");
91
120
  const userStr = localStorage.getItem("kyro_user");
@@ -102,10 +131,7 @@ export function Admin({ config, theme = "light", onThemeChange }: AdminProps) {
102
131
  }, []);
103
132
 
104
133
  useEffect(() => {
105
- const collectionKeys = Object.keys(collections);
106
- if (collectionKeys.length > 0 && !activeCollection) {
107
- setActiveCollection(collectionKeys[0]);
108
- }
134
+ // No longer auto-selecting the first collection to allow Dashboard as home
109
135
  }, [authenticated]);
110
136
 
111
137
  const handleAuth = (token: string, user: AuthUser) => {
@@ -133,7 +159,37 @@ export function Admin({ config, theme = "light", onThemeChange }: AdminProps) {
133
159
  }, 5000);
134
160
  };
135
161
 
162
+ const removeToast = (id: string) => {
163
+ setToasts((prev) => prev.filter((t) => t.id !== id));
164
+ };
165
+
136
166
  const handleCollectionChange = (collectionName: string) => {
167
+ if (!collectionName) {
168
+ setActiveCollection(null);
169
+ setActiveGlobal(null);
170
+ setCurrentView("list");
171
+ setSelectedId(null);
172
+ return;
173
+ }
174
+
175
+ // Check if it's a special governance or developer view
176
+ if (
177
+ [
178
+ "users",
179
+ "roles",
180
+ "audit",
181
+ "media",
182
+ "branding",
183
+ "developer",
184
+ "webhooks",
185
+ ].includes(collectionName)
186
+ ) {
187
+ setActiveCollection(null);
188
+ setActiveGlobal(null);
189
+ setCurrentView(collectionName as any);
190
+ return;
191
+ }
192
+
137
193
  setActiveCollection(collectionName);
138
194
  setActiveGlobal(null);
139
195
  setCurrentView("list");
@@ -178,13 +234,54 @@ export function Admin({ config, theme = "light", onThemeChange }: AdminProps) {
178
234
  config={{} as any}
179
235
  global={global}
180
236
  onBack={() => setCurrentView("list")}
181
- onSave={() => handleActionComplete("update")}
237
+ onSave={() => addToast("success", "Configuration saved successfully")}
182
238
  onError={handleError}
183
239
  mode="global"
184
240
  />
185
241
  );
186
242
  }
187
243
 
244
+ if (!activeCollection && !activeGlobal) {
245
+ if (currentView === "users") return <UserManagement />;
246
+ if (currentView === "roles")
247
+ return (
248
+ <div className="p-6">
249
+ <h2 className="text-xl font-bold">Roles Management</h2>
250
+ <p className="text-gray-500 mt-2">
251
+ Configure user roles and permissions.
252
+ </p>
253
+ </div>
254
+ );
255
+ if (currentView === "audit")
256
+ return (
257
+ <div className="p-6">
258
+ <h2 className="text-xl font-bold">Audit Logs</h2>
259
+ <p className="text-gray-500 mt-2">
260
+ View system activity and changes.
261
+ </p>
262
+ </div>
263
+ );
264
+ if (currentView === "media") return <MediaGallery />;
265
+ if (currentView === "branding") return <BrandingHub />;
266
+ if (currentView === "developer")
267
+ return <DeveloperCenter collections={collections} />;
268
+ if (currentView === "webhooks") return <WebhookManager />;
269
+
270
+ return (
271
+ <Dashboard
272
+ collections={collections}
273
+ onNavigate={(view, collection) => {
274
+ if (collection) {
275
+ setActiveCollection(collection);
276
+ setActiveGlobal(null);
277
+ }
278
+ setCurrentView(view as any);
279
+ }}
280
+ user={currentUser}
281
+ />
282
+ );
283
+ }
284
+
188
285
  if (!activeCollection) return null;
189
286
 
190
287
  const collection = collections[activeCollection];
@@ -231,33 +328,45 @@ export function Admin({ config, theme = "light", onThemeChange }: AdminProps) {
231
328
 
232
329
  return (
233
330
  <ThemeProvider defaultMode={theme}>
234
- <ToastProvider>
331
+ <ToastProvider
332
+ toasts={toasts}
333
+ addToast={addToast}
334
+ removeToast={removeToast}
335
+ >
235
336
  <div className="kyro-admin">
236
- <Sidebar
237
- collections={collections}
238
- globals={globals}
239
- activeCollection={activeCollection}
240
- activeGlobal={activeGlobal}
241
- onCollectionClick={handleCollectionChange}
242
- onGlobalClick={handleGlobalChange}
243
- user={currentUser}
244
- onLogout={handleLogout}
245
- />
246
337
  <div className="kyro-main">
247
338
  <div className="kyro-content">{renderContent()}</div>
248
339
  </div>
249
- {toasts.map((toast) => (
250
- <Toast
251
- key={toast.id}
252
- type={toast.type}
253
- message={toast.message}
254
- onClose={() =>
255
- setToasts((prev) => prev.filter((t) => t.id !== toast.id))
340
+ <CommandPalette
341
+ isOpen={showCommandPalette}
342
+ onClose={() => setShowCommandPalette(false)}
343
+ collections={collections}
344
+ globals={globals}
345
+ onNavigate={(view, collection, id) => {
346
+ if (view === "list" && collection) {
347
+ handleCollectionChange(collection);
348
+ } else if (view === "settings" && collection) {
349
+ handleGlobalChange(collection);
350
+ } else if (view === "media") {
351
+ setActiveCollection(null);
352
+ setActiveGlobal(null);
353
+ setCurrentView("list"); // This might need a separate 'media' view state if implemented
354
+ } else if (view === "create" && collection) {
355
+ setActiveCollection(collection);
356
+ setCurrentView("create");
256
357
  }
257
- />
258
- ))}
358
+ }}
359
+ />
259
360
  </div>
260
361
  </ToastProvider>
362
+ {toasts.map((toast) => (
363
+ <Toast
364
+ key={toast.id}
365
+ type={toast.type}
366
+ message={toast.message}
367
+ onClose={() => removeToast(toast.id)}
368
+ />
369
+ ))}
261
370
  </ThemeProvider>
262
371
  );
263
372
  }