@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
@@ -1,5 +1,5 @@
1
1
  import React, { useState, useEffect } from "react";
2
- import { apiGet, apiPost, apiDelete } from "../lib/api";
2
+ import { apiGet, apiPost, apiDelete, resolveApi } from "../lib/api";
3
3
  import {
4
4
  Terminal,
5
5
  Key,
@@ -13,11 +13,17 @@ import {
13
13
  Lock,
14
14
  Eye,
15
15
  EyeOff,
16
- } from "lucide-react";
16
+ } from "./ui/icons";
17
17
  import CodeMirror from "@uiw/react-codemirror";
18
18
  import { json } from "@codemirror/lang-json";
19
19
  import { aura } from "@uiw/codemirror-theme-aura";
20
+ import { useUIStore } from "../lib/stores";
20
21
  import { Modal, ModalContent, ModalActions } from "./ui/Modal";
22
+ import { PageHeader } from "./ui/PageHeader";
23
+ import { Badge } from "./ui/Badge";
24
+
25
+ // @ts-ignore
26
+ const API_BASE = typeof __KYRO_API_PATH__ !== 'undefined' ? __KYRO_API_PATH__ : '/api';
21
27
 
22
28
  interface ApiKey {
23
29
  id: string;
@@ -27,17 +33,16 @@ interface ApiKey {
27
33
  createdAt: string;
28
34
  }
29
35
 
30
- export function DeveloperCenter({ collections }: { collections: any }) {
36
+ export function DeveloperCenter({ collections }: { collections: Record<string, unknown> }) {
31
37
  const [keys, setKeys] = useState<ApiKey[]>([]);
32
38
  const [loading, setLoading] = useState(false);
33
39
  const [showKey, setShowKey] = useState<string | null>(null);
34
40
  const [testEndpoint, setTestEndpoint] = useState("");
35
41
  const [playgroundResult, setPlaygroundResult] = useState<unknown>(null);
36
42
  const [exploring, setExploring] = useState(false);
43
+ const { confirm, alert } = useUIStore();
37
44
 
38
45
  const [showCreateModal, setShowCreateModal] = useState(false);
39
- const [showDeleteModal, setShowDeleteModal] = useState(false);
40
- const [deleteKeyId, setDeleteKeyId] = useState<string | null>(null);
41
46
  const [newKeyName, setNewKeyName] = useState("");
42
47
 
43
48
  const loadKeys = async () => {
@@ -63,35 +68,36 @@ export function DeveloperCenter({ collections }: { collections: any }) {
63
68
  try {
64
69
  await apiPost("/api/keys", { name: newKeyName });
65
70
  loadKeys();
71
+ setShowCreateModal(false);
72
+ setNewKeyName("");
66
73
  } catch (e) {
67
74
  console.error(e);
75
+ alert({ title: "Error", message: "Failed to generate API key" });
68
76
  }
69
- setShowCreateModal(false);
70
- setNewKeyName("");
71
- };
72
-
73
- const handleRevokeKey = async (id: string) => {
74
- setDeleteKeyId(id);
75
- setShowDeleteModal(true);
76
77
  };
77
78
 
78
- const confirmRevokeKey = async () => {
79
- if (!deleteKeyId) return;
80
- try {
81
- await apiDelete(`/api/keys/${deleteKeyId}`);
82
- loadKeys();
83
- } catch (e) {
84
- console.error(e);
85
- }
86
- setShowDeleteModal(false);
87
- setDeleteKeyId(null);
79
+ const handleRevokeKey = (id: string) => {
80
+ confirm({
81
+ title: "Revoke API Key",
82
+ message: "Are you sure you want to revoke this key? Any integrations using it will stop working.",
83
+ variant: "danger",
84
+ onConfirm: async () => {
85
+ try {
86
+ await apiDelete(`/api/keys/${id}`);
87
+ loadKeys();
88
+ } catch (e) {
89
+ console.error(e);
90
+ alert({ title: "Error", message: "Failed to revoke API key" });
91
+ }
92
+ }
93
+ });
88
94
  };
89
95
 
90
96
  const handleRunTest = async () => {
91
97
  if (!testEndpoint) return;
92
98
  setExploring(true);
93
99
  try {
94
- const response = await fetch(`/api/${testEndpoint}`);
100
+ const response = await fetch(resolveApi(`/api/${testEndpoint}`));
95
101
  const data = await response.json();
96
102
  setPlaygroundResult(data);
97
103
  } catch (e) {
@@ -104,219 +110,237 @@ export function DeveloperCenter({ collections }: { collections: any }) {
104
110
  };
105
111
 
106
112
  return (
107
- <div className="w-full space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-700 px-8 pb-32">
108
- {/* Header */}
109
- <div className="flex flex-col lg:flex-row lg:items-center justify-between gap-6 pt-4">
110
- <div>
111
- <h1 className="text-4xl font-black tracking-tighter text-[var(--kyro-text-primary)]">
112
- Developer <span className="text-[var(--kyro-primary)]">Center</span>
113
- </h1>
114
- <p className="text-[var(--kyro-text-secondary)] mt-1 font-medium opacity-60">
115
- Provision access keys and explore the headless API ecosystem.
116
- </p>
117
- </div>
118
- <div className="flex items-center gap-3">
119
- <button
120
- type="button"
121
- onClick={handleGenerateKey}
122
- className="flex items-center gap-2 px-6 py-3 bg-[var(--kyro-sidebar-active)] text-[var(--kyro-sidebar-text-active)] rounded-full font-black text-sm shadow-xl hover:shadow-[var(--kyro-primary)] active:scale-95 transition-all"
123
- >
124
- <Key className="w-4 h-4" />
125
- Generate New Key
126
- </button>
127
- </div>
128
- </div>
113
+ <div className="w-full space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-700 pb-32">
114
+ <PageHeader
115
+ title="Developer Center"
116
+ description="Provision access keys and explore the headless API ecosystem."
117
+ icon={Code2}
118
+ actions={[
119
+ {
120
+ label: "Generate Key",
121
+ onClick: handleGenerateKey,
122
+ icon: Key,
123
+ },
124
+ ]}
125
+ />
129
126
 
130
127
  <div className="grid grid-cols-1 xl:grid-cols-3 gap-8">
131
128
  {/* API Key List */}
132
129
  <section className="xl:col-span-2 space-y-6">
133
- <div className="flex items-center justify-between px-2">
134
- <div className="flex items-center gap-2">
135
- <Lock className="w-4 h-4 text-[var(--kyro-primary)]" />
136
- <span className="text-[10px] font-black uppercase tracking-widest opacity-40">
137
- Access Credentials
138
- </span>
139
- </div>
130
+ <div className="flex items-center gap-2 px-2">
131
+ <div className="w-1 h-4 bg-[var(--kyro-primary)] rounded-full" />
132
+ <h2 className="text-sm font-medium tracking-[0.2em] opacity-40 uppercase">Access Credentials</h2>
140
133
  </div>
141
134
 
142
135
  <div className="space-y-4">
143
- {keys.map((key) => (
144
- <div
145
- key={key.id}
146
- className="surface-tile p-6 group transition-all duration-300 hover:border-[var(--kyro-primary)] hover:shadow-xl"
147
- >
148
- <div className="flex flex-col md:flex-row md:items-center justify-between gap-6">
149
- <div className="flex-1 min-w-0">
150
- <h3 className="text-lg font-black tracking-tight mb-1">
151
- {key.name}
152
- </h3>
153
- <div className="flex items-center gap-3">
154
- <div className="flex-1 bg-[var(--kyro-bg-secondary)] border border-[var(--kyro-border)] rounded-xl px-4 py-2 flex items-center justify-between group/key overflow-hidden">
136
+ {keys.length === 0 ? (
137
+ <div className="p-12 text-center rounded-[2rem] border-2 border-dashed border-[var(--kyro-border)] bg-[var(--kyro-surface-accent)]/30">
138
+ <Lock className="w-10 h-10 mx-auto mb-4 opacity-20" />
139
+ <p className="text-sm text-[var(--kyro-text-secondary)] opacity-50">No API keys found. Generate one to get started.</p>
140
+ </div>
141
+ ) : (
142
+ keys.map((key) => (
143
+ <div
144
+ key={key.id}
145
+ className="group relative overflow-hidden bg-[var(--kyro-surface)] border border-[var(--kyro-border)] rounded-3xl p-6 hover:border-[var(--kyro-primary)]/50 transition-all duration-300"
146
+ >
147
+ <div className="flex flex-col md:flex-row md:items-center justify-between gap-6 relative z-10">
148
+ <div className="flex-1 min-w-0">
149
+ <div className="flex items-center gap-3 mb-4">
150
+ <div className="p-2.5 bg-[var(--kyro-surface-accent)] rounded-xl group-hover:bg-[var(--kyro-primary)]/10 transition-colors">
151
+ <Key className="w-5 h-5 text-[var(--kyro-text-secondary)] group-hover:text-[var(--kyro-primary)] transition-colors" />
152
+ </div>
153
+ <h3 className="text-lg font-bold group-hover:text-[var(--kyro-primary)] transition-colors truncate">
154
+ {key.name}
155
+ </h3>
156
+ </div>
157
+
158
+ <div className="flex-1 bg-[var(--kyro-bg)] border border-[var(--kyro-border)] rounded-2xl px-4 py-3 flex items-center justify-between group/key overflow-hidden">
155
159
  <code className="text-xs font-mono opacity-80 truncate mr-4">
156
160
  {showKey === key.id
157
161
  ? key.key
158
162
  : "••••••••••••••••••••••••••••••••"}
159
163
  </code>
160
- <div className="flex items-center gap-1 shrink-0">
164
+ <div className="flex items-center gap-2 shrink-0">
161
165
  <button
162
166
  type="button"
163
167
  onClick={() =>
164
168
  setShowKey(showKey === key.id ? null : key.id)
165
169
  }
166
- className="p-1.5 hover:bg-[var(--kyro-surface)] rounded-md transition-all text-[var(--kyro-text-secondary)]"
170
+ className="p-1.5 hover:bg-[var(--kyro-surface-accent)] rounded-lg transition-all text-[var(--kyro-text-secondary)]"
167
171
  >
168
172
  {showKey === key.id ? (
169
- <EyeOff className="w-3.5 h-3.5" />
173
+ <EyeOff className="w-4 h-4" />
170
174
  ) : (
171
- <Eye className="w-3.5 h-3.5" />
175
+ <Eye className="w-4 h-4" />
172
176
  )}
173
177
  </button>
174
178
  <button
175
179
  type="button"
176
- className="p-1.5 hover:bg-[var(--kyro-surface)] rounded-md transition-all text-[var(--kyro-text-secondary)]"
180
+ className="p-1.5 hover:bg-[var(--kyro-surface-accent)] rounded-lg transition-all text-[var(--kyro-text-secondary)]"
181
+ onClick={() => {
182
+ navigator.clipboard.writeText(key.key);
183
+ alert({ title: "Success", message: "API key copied to clipboard" });
184
+ }}
177
185
  >
178
- <Copy className="w-3.5 h-3.5" />
186
+ <Copy className="w-4 h-4" />
179
187
  </button>
180
188
  </div>
181
189
  </div>
190
+
191
+ <div className="flex items-center gap-6 mt-4 text-[10px] font-bold opacity-30 uppercase tracking-widest">
192
+ <div className="flex items-center gap-2">
193
+ <span>Issued:</span>
194
+ <span>{new Date(key.createdAt).toLocaleDateString()}</span>
195
+ </div>
196
+ <div className="flex items-center gap-2">
197
+ <span>Status:</span>
198
+ <span className="text-green-500">Active</span>
199
+ </div>
200
+ </div>
182
201
  </div>
183
- <div className="flex items-center gap-4 mt-3 text-[10px] font-black uppercase tracking-widest opacity-30">
184
- <span>
185
- Created {new Date(key.createdAt).toLocaleDateString()}
186
- </span>
187
- <span>
188
- Last used{" "}
189
- {key.lastUsed
190
- ? new Date(key.lastUsed).toLocaleDateString()
191
- : "Never"}
192
- </span>
193
- </div>
194
- </div>
195
- <div className="flex items-center gap-3">
202
+
196
203
  <button
197
204
  type="button"
198
205
  onClick={() => handleRevokeKey(key.id)}
199
- className="p-3 bg-red-500/5 text-red-500 rounded-xl hover:bg-red-500/10 transition-all border border-transparent hover:border-red-500/20"
206
+ className="p-3.5 bg-red-500/5 text-red-500 rounded-2xl hover:bg-red-500/10 transition-all border border-transparent hover:border-red-500/30 self-start md:self-center"
200
207
  title="Revoke Key"
201
208
  >
202
- <Trash2 className="w-4 h-4" />
209
+ <Trash2 className="w-5 h-5" />
203
210
  </button>
204
211
  </div>
205
212
  </div>
206
- </div>
207
- ))}
213
+ ))
214
+ )}
208
215
  </div>
209
216
  </section>
210
217
 
211
- {/* Sidebar Stats/Info */}
218
+ {/* Sidebar Info - Modernized */}
212
219
  <section className="space-y-6">
213
- <div className="surface-tile p-8 bg-[var(--kyro-primary)] group overflow-hidden relative">
220
+ <div className="relative overflow-hidden rounded-[2.5rem] border border-[var(--kyro-border)] bg-gradient-to-br from-[var(--kyro-primary)] to-[var(--kyro-primary)]/80 p-8 group shadow-2xl shadow-[var(--kyro-primary)]/20">
221
+ <div className="absolute top-0 right-0 p-8 opacity-[0.1] pointer-events-none group-hover:scale-110 transition-transform duration-700">
222
+ <Terminal className="w-48 h-48 rotate-12" />
223
+ </div>
214
224
  <div className="relative z-10">
215
- <Terminal className="w-8 h-8 text-white mb-4 opacity-40" />
216
- <h3 className="text-white text-xl font-black tracking-tight mb-2">
225
+ <div className="w-12 h-12 bg-white/20 backdrop-blur-md rounded-2xl flex items-center justify-center mb-6">
226
+ <Terminal className="w-6 h-6 text-white" />
227
+ </div>
228
+ <h3 className="text-white text-2xl font-bold tracking-tight mb-3">
217
229
  Endpoint Hub
218
230
  </h3>
219
- <p className="text-white/70 text-sm font-medium leading-relaxed mb-6">
220
- All content is delivered via our optimized REST API. Use your
221
- keys to authorize requests.
231
+ <p className="text-white/80 text-sm font-medium leading-relaxed mb-8">
232
+ Kyro CMS is built for high-performance content delivery. Use these endpoints to power your headless frontends.
222
233
  </p>
223
234
  <button
224
235
  type="button"
225
- className="w-full py-3 bg-white text-[var(--kyro-primary)] rounded-xl font-black text-xs uppercase tracking-widest hover:shadow-2xl transition-all flex items-center justify-center gap-2"
236
+ className="w-full py-4 bg-white text-[var(--kyro-primary)] rounded-[1.2rem] font-bold text-xs uppercase tracking-widest hover:shadow-xl transition-all flex items-center justify-center gap-2 active:scale-[0.98]"
226
237
  >
227
- Review API Docs
238
+ Full API Documentation
228
239
  <ExternalLink className="w-3.5 h-3.5" />
229
240
  </button>
230
241
  </div>
231
- {/* Decorative glow */}
232
- <div className="absolute -bottom-10 -right-10 w-40 h-40 bg-white opacity-10 blur-3xl group-hover:scale-150 transition-transform duration-700" />
233
242
  </div>
234
243
 
235
- <div className="surface-tile p-8">
236
- <h4 className="text-[10px] font-black uppercase tracking-widest opacity-40 mb-6">
237
- Base URLs
238
- </h4>
239
- <div className="space-y-4">
240
- <div>
241
- <p className="text-xs font-bold mb-2">
242
- Content Delivery (Production)
243
- </p>
244
- <div className="bg-[var(--kyro-bg-secondary)] p-3 rounded-xl border border-[var(--kyro-border)] flex items-center justify-between group">
245
- <code className="text-[10px] font-mono opacity-60">
246
- https://api.kyro.io/v1
247
- </code>
248
- <Copy className="w-3.5 h-3.5 opacity-0 group-hover:opacity-40 cursor-pointer transition-opacity" />
244
+ <div className="rounded-[2.5rem] border border-[var(--kyro-border)] bg-[var(--kyro-surface)]/50 p-8 space-y-6">
245
+ <h4 className="text-[10px] font-bold uppercase tracking-[0.2em] opacity-40">Environment Matrix</h4>
246
+ <div className="space-y-5">
247
+ <div className="space-y-2">
248
+ <div className="flex items-center justify-between">
249
+ <span className="text-xs font-bold">Content API</span>
250
+ <Badge variant="outline" className="text-[8px] font-bold uppercase py-0.5">Production</Badge>
251
+ </div>
252
+ <div className="group relative">
253
+ <div className="absolute -inset-0.5 bg-gradient-to-r from-[var(--kyro-primary)]/30 to-transparent rounded-xl blur opacity-0 group-hover:opacity-100 transition-all"></div>
254
+ <div className="relative bg-[var(--kyro-bg)] p-3.5 rounded-xl border border-[var(--kyro-border)] flex items-center justify-between">
255
+ <code className="text-[10px] font-mono opacity-60">https://api.kyro.io/v1</code>
256
+ <Copy className="w-3.5 h-3.5 opacity-0 group-hover:opacity-40 cursor-pointer transition-opacity" onClick={() => navigator.clipboard.writeText('https://api.kyro.io/v1')} />
257
+ </div>
249
258
  </div>
250
259
  </div>
251
- <div>
252
- <p className="text-xs font-bold mb-2">
253
- Editor Snapshots (Preview)
254
- </p>
255
- <div className="bg-[var(--kyro-bg-secondary)] p-3 rounded-xl border border-[var(--kyro-border)] flex items-center justify-between group">
256
- <code className="text-[10px] font-mono opacity-60">
257
- https://preview.kyro.io/v1
258
- </code>
259
- <Copy className="w-3.5 h-3.5 opacity-0 group-hover:opacity-40 cursor-pointer transition-opacity" />
260
+ <div className="space-y-2">
261
+ <div className="flex items-center justify-between">
262
+ <span className="text-xs font-bold">Draft Explorer</span>
263
+ <Badge variant="outline" className="text-[8px] font-bold uppercase py-0.5">Staging</Badge>
264
+ </div>
265
+ <div className="group relative">
266
+ <div className="absolute -inset-0.5 bg-gradient-to-r from-amber-500/30 to-transparent rounded-xl blur opacity-0 group-hover:opacity-100 transition-all"></div>
267
+ <div className="relative bg-[var(--kyro-bg)] p-3.5 rounded-xl border border-[var(--kyro-border)] flex items-center justify-between">
268
+ <code className="text-[10px] font-mono opacity-60">https://preview.kyro.io/v1</code>
269
+ <Copy className="w-3.5 h-3.5 opacity-0 group-hover:opacity-40 cursor-pointer transition-opacity" onClick={() => navigator.clipboard.writeText('https://preview.kyro.io/v1')} />
270
+ </div>
260
271
  </div>
261
272
  </div>
262
273
  </div>
263
274
  </div>
264
275
  </section>
265
276
 
266
- {/* Playground Explorer */}
267
- <section className="xl:col-span-3 surface-tile p-8 space-y-8">
268
- <div className="flex flex-col lg:flex-row lg:items-center justify-between gap-6">
269
- <div>
270
- <h2 className="text-2xl font-black tracking-tighter flex items-center gap-3">
271
- <PlayCircle className="w-6 h-6 text-[var(--kyro-primary)]" />
272
- API{" "}
273
- <span className="text-[var(--kyro-primary)]">Playground</span>
277
+ {/* Playground Explorer - High Fidelity */}
278
+ <section className="xl:col-span-3 rounded-[3rem] border border-[var(--kyro-border)] bg-[var(--kyro-surface)]/50 p-10 space-y-10 overflow-hidden relative">
279
+ <div className="absolute top-0 right-0 p-20 opacity-[0.02] pointer-events-none">
280
+ <PlayCircle className="w-96 h-96" />
281
+ </div>
282
+
283
+ <div className="flex flex-col lg:flex-row lg:items-center justify-between gap-10 relative z-10">
284
+ <div className="max-w-xl">
285
+ <h2 className="text-3xl font-bold tracking-tighter flex items-center gap-4 mb-3">
286
+ <div className="w-12 h-12 bg-[var(--kyro-primary)]/10 rounded-2xl flex items-center justify-center">
287
+ <PlayCircle className="w-7 h-7 text-[var(--kyro-primary)]" />
288
+ </div>
289
+ API <span className="text-[var(--kyro-primary)]">Explorer</span>
274
290
  </h2>
275
- <p className="text-[var(--kyro-text-secondary)] text-sm font-medium opacity-60">
276
- Test your endpoints and analyze response payloads in real-time.
291
+ <p className="text-[var(--kyro-text-secondary)] text-sm font-medium opacity-60 leading-relaxed">
292
+ Test your collection endpoints and analyze live response payloads.
293
+ Enter a collection slug to fetch its latest documents.
277
294
  </p>
278
295
  </div>
279
- <div className="flex items-center gap-3 flex-1 lg:max-w-xl">
280
- <div className="relative flex-1 group">
281
- <span className="absolute left-4 top-1/2 -translate-y-1/2 text-[10px] font-black opacity-40 mt-0.5">
282
- GET /api/
296
+
297
+ <div className="flex items-center gap-4 flex-1 lg:max-w-2xl bg-[var(--kyro-bg)] p-2 rounded-[2rem] border border-[var(--kyro-border)] shadow-2xl">
298
+ <div className="flex-1 relative flex items-center pl-6">
299
+ <span className="text-[11px] font-bold opacity-30 uppercase tracking-widest mr-2 whitespace-nowrap">
300
+ GET {API_BASE}/
283
301
  </span>
284
302
  <input
285
303
  type="text"
286
304
  value={testEndpoint}
287
305
  onChange={(e) => setTestEndpoint(e.target.value)}
288
306
  placeholder="collection-slug"
289
- className="w-full pl-20 pr-4 py-3 bg-[var(--kyro-bg-secondary)] border border-[var(--kyro-border)] rounded-2xl focus:outline-none focus:ring-2 focus:ring-[var(--kyro-primary)] transition-all font-mono text-xs font-bold"
307
+ className="w-full py-4 bg-transparent focus:outline-none font-mono text-sm font-bold text-[var(--kyro-primary)]"
290
308
  />
291
309
  </div>
292
310
  <button
293
311
  type="button"
294
312
  onClick={handleRunTest}
295
313
  disabled={exploring || !testEndpoint}
296
- className="px-6 py-3 bg-[var(--kyro-sidebar-active)] text-[var(--kyro-sidebar-text-active)] rounded-2xl font-black text-xs uppercase tracking-widest shadow-xl disabled:opacity-50 disabled:cursor-not-allowed hover:shadow-[var(--kyro-primary)] transition-all flex items-center gap-2 shrink-0"
314
+ className="px-8 py-4 bg-[var(--kyro-primary)] text-white rounded-[1.5rem] font-bold text-sm shadow-xl disabled:opacity-50 disabled:cursor-not-allowed hover:scale-[1.02] transition-all flex items-center gap-3 shrink-0"
297
315
  >
298
316
  {exploring ? (
299
- <RefreshCcw className="w-3.5 h-3.5 animate-spin" />
317
+ <RefreshCcw className="w-4 h-4 animate-spin" />
300
318
  ) : (
301
- <ChevronRight className="w-4 h-4" />
319
+ <ChevronRight className="w-5 h-5" />
302
320
  )}
303
- Fire
321
+ Run Query
304
322
  </button>
305
323
  </div>
306
324
  </div>
307
325
 
308
326
  {playgroundResult ? (
309
- <div className="animate-in fade-in slide-in-from-top-4 duration-500">
310
- <div className="flex items-center gap-2 mb-4">
311
- <Code2 className="w-4 h-4 opacity-40" />
312
- <span className="text-[10px] font-black uppercase tracking-widest opacity-40">
313
- JSON Response Payload
314
- </span>
327
+ <div className="animate-in fade-in zoom-in-95 duration-500 relative z-10">
328
+ <div className="flex items-center justify-between mb-4 px-2">
329
+ <div className="flex items-center gap-3">
330
+ <div className="w-1 h-3 bg-[var(--kyro-primary)] rounded-full" />
331
+ <span className="text-[10px] font-bold uppercase tracking-[0.2em] opacity-40">Response Payload</span>
332
+ </div>
333
+ <Badge variant="outline" className="text-[9px] font-mono px-3">200 OK</Badge>
315
334
  </div>
316
- <div className="rounded-3xl overflow-hidden border border-[var(--kyro-border)] shadow-2xl">
335
+ <div className="rounded-[2rem] overflow-hidden border border-[var(--kyro-border)] shadow-2xl bg-[#090b10]">
336
+ <div className="flex items-center gap-2 px-6 py-4 bg-[#11141d] border-b border-[var(--kyro-border)]/50">
337
+ <div className="w-2.5 h-2.5 rounded-full bg-red-500/20" />
338
+ <div className="w-2.5 h-2.5 rounded-full bg-amber-500/20" />
339
+ <div className="w-2.5 h-2.5 rounded-full bg-green-500/20" />
340
+ </div>
317
341
  <CodeMirror
318
342
  value={JSON.stringify(playgroundResult, null, 2)}
319
- height="350px"
343
+ height="450px"
320
344
  theme={aura}
321
345
  extensions={[json()]}
322
346
  editable={false}
@@ -325,10 +349,12 @@ export function DeveloperCenter({ collections }: { collections: any }) {
325
349
  </div>
326
350
  </div>
327
351
  ) : (
328
- <div className="h-64 rounded-3xl border-2 border-dashed border-[var(--kyro-border)] flex flex-col items-center justify-center opacity-30 select-none">
329
- <Code2 className="w-12 h-12 mb-4" />
330
- <p className="font-bold text-sm">
331
- Enter an endpoint above to begin testing.
352
+ <div className="h-80 rounded-[3rem] border-2 border-dashed border-[var(--kyro-border)] flex flex-col items-center justify-center bg-[var(--kyro-surface-accent)]/20 transition-all duration-700">
353
+ <div className="w-20 h-20 bg-[var(--kyro-surface)] rounded-3xl flex items-center justify-center shadow-lg border border-[var(--kyro-border)] mb-6 opacity-20">
354
+ <Code2 className="w-10 h-10" />
355
+ </div>
356
+ <p className="font-bold text-sm opacity-30 uppercase tracking-[0.2em]">
357
+ Awaiting request dispatch...
332
358
  </p>
333
359
  </div>
334
360
  )}
@@ -339,66 +365,42 @@ export function DeveloperCenter({ collections }: { collections: any }) {
339
365
  <Modal
340
366
  open={showCreateModal}
341
367
  onClose={() => setShowCreateModal(false)}
342
- title="Create API Key"
368
+ title="Generate Access Token"
369
+ size="lg"
343
370
  >
344
371
  <ModalContent>
345
- <p className="text-sm text-[var(--kyro-text-secondary)] mb-4">
346
- Give your API key a name to identify its purpose.
347
- </p>
348
- <input
349
- type="text"
350
- value={newKeyName}
351
- onChange={(e) => setNewKeyName(e.target.value)}
352
- placeholder="e.g., Production API"
353
- className="w-full px-4 py-3 bg-[var(--kyro-bg)] border border-[var(--kyro-border)] rounded-xl text-[var(--kyro-text-primary)] focus:outline-none focus:border-[var(--kyro-primary)]"
354
- onKeyDown={(e) => e.key === "Enter" && confirmGenerateKey()}
355
- />
372
+ <div className="space-y-6">
373
+ <p className="text-sm text-[var(--kyro-text-secondary)] opacity-70 leading-relaxed">
374
+ Define a name for this API key to identify its integration context.
375
+ Keys are encrypted at rest and should be treated with extreme caution.
376
+ </p>
377
+ <div className="space-y-2">
378
+ <label className="text-xs font-bold uppercase tracking-widest opacity-40 px-1">Token Name</label>
379
+ <input
380
+ type="text"
381
+ value={newKeyName}
382
+ onChange={(e) => setNewKeyName(e.target.value)}
383
+ placeholder="e.g., Mobile App SDK, Production Server"
384
+ className="w-full px-6 py-4 bg-[var(--kyro-bg)] border border-[var(--kyro-border)] rounded-2xl text-[var(--kyro-text-primary)] focus:outline-none focus:border-[var(--kyro-primary)] shadow-sm transition-all"
385
+ onKeyDown={(e) => e.key === "Enter" && confirmGenerateKey()}
386
+ />
387
+ </div>
388
+ </div>
356
389
  </ModalContent>
357
390
  <ModalActions>
358
391
  <button
359
392
  type="button"
360
393
  onClick={() => setShowCreateModal(false)}
361
- className="px-4 py-2 rounded-lg font-medium text-sm border border-[var(--kyro-border)] text-[var(--kyro-text-secondary)] hover:bg-[var(--kyro-surface-accent)] hover:text-[var(--kyro-text-primary)] transition-colors"
394
+ className="px-6 py-3 rounded-xl font-bold text-sm border border-[var(--kyro-border)] text-[var(--kyro-text-secondary)] hover:bg-[var(--kyro-surface-accent)] transition-all"
362
395
  >
363
396
  Cancel
364
397
  </button>
365
398
  <button
366
399
  type="button"
367
400
  onClick={confirmGenerateKey}
368
- className="px-4 py-2 rounded-lg font-medium text-sm bg-[var(--kyro-sidebar-active)] text-[var(--kyro-sidebar-text-active)] hover:opacity-90 transition-colors"
369
- >
370
- Create
371
- </button>
372
- </ModalActions>
373
- </Modal>
374
-
375
- {/* Delete Confirmation Modal */}
376
- <Modal
377
- open={showDeleteModal}
378
- onClose={() => setShowDeleteModal(false)}
379
- title="Revoke API Key"
380
- variant="danger"
381
- >
382
- <ModalContent>
383
- <p className="text-sm text-[var(--kyro-text-secondary)]">
384
- Are you sure you want to revoke this key? Any integrations using it
385
- will stop working.
386
- </p>
387
- </ModalContent>
388
- <ModalActions>
389
- <button
390
- type="button"
391
- onClick={() => setShowDeleteModal(false)}
392
- className="px-4 py-2 rounded-lg font-medium text-sm border border-[var(--kyro-border)] text-[var(--kyro-text-secondary)] hover:bg-[var(--kyro-surface-accent)] hover:text-[var(--kyro-text-primary)] transition-colors"
393
- >
394
- Cancel
395
- </button>
396
- <button
397
- type="button"
398
- onClick={confirmRevokeKey}
399
- className="px-4 py-2 rounded-lg font-medium text-sm bg-red-500 text-white hover:bg-red-600 transition-colors"
401
+ className="px-8 py-3 rounded-xl font-bold text-sm bg-[var(--kyro-primary)] text-white hover:opacity-90 shadow-lg shadow-[var(--kyro-primary)]/20 transition-all"
400
402
  >
401
- Revoke
403
+ Generate Token
402
404
  </button>
403
405
  </ModalActions>
404
406
  </Modal>