@kyro-cms/admin 0.3.1 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/dist/EditorClient-XEUOVAAC.js +466 -0
  2. package/dist/EditorClient-XEUOVAAC.js.map +1 -0
  3. package/dist/EditorClient-YLCGVDXY.cjs +468 -0
  4. package/dist/EditorClient-YLCGVDXY.cjs.map +1 -0
  5. package/dist/chunk-7KPIUCGT.js +384 -0
  6. package/dist/chunk-7KPIUCGT.js.map +1 -0
  7. package/dist/chunk-GOACG6R7.cjs +473 -0
  8. package/dist/chunk-GOACG6R7.cjs.map +1 -0
  9. package/dist/index.cjs +14861 -0
  10. package/dist/index.cjs.map +1 -0
  11. package/dist/index.css +1661 -0
  12. package/dist/index.css.map +1 -0
  13. package/dist/index.d.ts +563 -0
  14. package/dist/index.js +14784 -0
  15. package/dist/index.js.map +1 -0
  16. package/package.json +19 -19
  17. package/src/components/ActionBar.tsx +7 -43
  18. package/src/components/Admin.tsx +138 -277
  19. package/src/components/ApiKeysManager.tsx +428 -419
  20. package/src/components/AuditLogsPage.tsx +35 -39
  21. package/src/components/AuthBridge.tsx +51 -0
  22. package/src/components/AutoForm.tsx +495 -1230
  23. package/src/components/BrandingHub.tsx +18 -19
  24. package/src/components/BulkActionsBar.tsx +1 -1
  25. package/src/components/CreateView.tsx +22 -36
  26. package/src/components/Dashboard.tsx +60 -84
  27. package/src/components/DetailView.tsx +113 -91
  28. package/src/components/DeveloperCenter.tsx +200 -198
  29. package/src/components/FieldRenderer.tsx +206 -0
  30. package/src/components/GraphQLPlayground.tsx +340 -480
  31. package/src/components/ListView.tsx +828 -254
  32. package/src/components/LoginPage.tsx +3 -4
  33. package/src/components/MarketplaceManager.tsx +254 -0
  34. package/src/components/MediaGallery.tsx +856 -1192
  35. package/src/components/PluginsManager.tsx +277 -0
  36. package/src/components/RestPlayground.tsx +398 -560
  37. package/src/components/SessionsManager.tsx +211 -0
  38. package/src/components/Sidebar.astro +179 -151
  39. package/src/components/ThemeProvider.tsx +7 -161
  40. package/src/components/UserManagement.tsx +162 -146
  41. package/src/components/UserMenu.tsx +110 -0
  42. package/src/components/WebhookManager.tsx +305 -367
  43. package/src/components/blocks/AccordionBlock.tsx +4 -4
  44. package/src/components/blocks/ArrayBlock.tsx +3 -3
  45. package/src/components/blocks/BlockEditModal.tsx +8 -8
  46. package/src/components/blocks/BlockWrapper.tsx +61 -0
  47. package/src/components/blocks/ButtonBlock.tsx +4 -4
  48. package/src/components/blocks/ChildBlocksTree.tsx +23 -25
  49. package/src/components/blocks/CodeBlock.tsx +15 -15
  50. package/src/components/blocks/ColumnsBlock.tsx +6 -44
  51. package/src/components/blocks/DividerBlock.tsx +3 -3
  52. package/src/components/blocks/FileBlock.tsx +4 -4
  53. package/src/components/blocks/HeadingBlock.tsx +6 -38
  54. package/src/components/blocks/HeroBlock.tsx +4 -4
  55. package/src/components/blocks/ImageBlock.tsx +4 -4
  56. package/src/components/blocks/LinkBlock.tsx +4 -4
  57. package/src/components/blocks/ListBlock.tsx +3 -3
  58. package/src/components/blocks/ParagraphBlock.tsx +12 -42
  59. package/src/components/blocks/RelationshipBlock.tsx +4 -4
  60. package/src/components/blocks/RichTextBlock.tsx +4 -4
  61. package/src/components/blocks/VStackBlock.tsx +5 -37
  62. package/src/components/blocks/VideoBlock.tsx +4 -4
  63. package/src/components/blocks/types.ts +11 -0
  64. package/src/components/fields/AccordionField.tsx +1 -1
  65. package/src/components/fields/ArrayField.tsx +2 -2
  66. package/src/components/fields/ArrayLayout.tsx +93 -0
  67. package/src/components/fields/BlocksField.tsx +122 -111
  68. package/src/components/fields/ButtonField.tsx +1 -1
  69. package/src/components/fields/CheckboxField.tsx +14 -15
  70. package/src/components/fields/ChildrenField.tsx +2 -2
  71. package/src/components/fields/CodeField.tsx +3 -3
  72. package/src/components/fields/ColumnsField.tsx +2 -2
  73. package/src/components/fields/DateField.tsx +13 -26
  74. package/src/components/fields/EditorClient.tsx +26 -28
  75. package/src/components/fields/FieldLayout.tsx +52 -0
  76. package/src/components/fields/GroupLayout.tsx +35 -0
  77. package/src/components/fields/JSONField.tsx +7 -7
  78. package/src/components/fields/LinkField.tsx +1 -1
  79. package/src/components/fields/MarkdownField.tsx +1 -1
  80. package/src/components/fields/NumberField.tsx +13 -26
  81. package/src/components/fields/PortableTextField.tsx +4 -4
  82. package/src/components/fields/PortableTextRenderer.tsx +1 -1
  83. package/src/components/fields/RelationshipBlockField.tsx +31 -23
  84. package/src/components/fields/RelationshipField.tsx +14 -14
  85. package/src/components/fields/SelectField.tsx +17 -26
  86. package/src/components/fields/TabsLayout.tsx +69 -0
  87. package/src/components/fields/TextField.tsx +85 -38
  88. package/src/components/fields/UploadField.tsx +71 -41
  89. package/src/components/fields/VideoField.tsx +1 -1
  90. package/src/components/fields/extensions/blockComponents.tsx +2 -2
  91. package/src/components/fields/extensions/blocksStore.ts +207 -193
  92. package/src/components/fields/types.ts +22 -0
  93. package/src/components/layout/Layout.tsx +1 -1
  94. package/src/components/ui/ActionMenu.tsx +63 -0
  95. package/src/components/ui/Badge.tsx +59 -5
  96. package/src/components/ui/BlockDrawer.tsx +4 -5
  97. package/src/components/ui/CommandPalette.tsx +58 -36
  98. package/src/components/ui/CommandPaletteWrapper.tsx +18 -17
  99. package/src/components/ui/Dropdown.tsx +18 -16
  100. package/src/components/ui/EmptyState.tsx +25 -0
  101. package/src/components/ui/GlobalModal.tsx +49 -0
  102. package/src/components/ui/IconButton.tsx +44 -0
  103. package/src/components/ui/Modal.tsx +19 -20
  104. package/src/components/ui/PageHeader.tsx +158 -0
  105. package/src/components/ui/Pagination.tsx +61 -0
  106. package/src/components/ui/PromptModal.tsx +1 -1
  107. package/src/components/ui/SearchInput.tsx +57 -0
  108. package/src/components/ui/SeoPreview.tsx +31 -0
  109. package/src/components/ui/SessionModal.tsx +0 -0
  110. package/src/components/ui/SlidePanel.tsx +2 -0
  111. package/src/components/ui/Toast.tsx +65 -122
  112. package/src/components/ui/Toaster.tsx +18 -0
  113. package/src/components/ui/icons.tsx +112 -0
  114. package/src/components/users/UserDetail.tsx +290 -0
  115. package/src/components/users/UserForm.tsx +242 -0
  116. package/src/components/users/UsersList.tsx +338 -0
  117. package/src/env.d.ts +13 -13
  118. package/src/fields/index.ts +2 -1
  119. package/src/global.d.ts +7 -0
  120. package/src/hooks/data.ts +2 -9
  121. package/src/hooks/useAsyncData.ts +36 -0
  122. package/src/hooks/useAutoFormState.ts +527 -0
  123. package/src/hooks/useSelection.ts +49 -0
  124. package/src/hooks/useSession.ts +0 -0
  125. package/src/index.ts +11 -1
  126. package/src/integration.ts +86 -11
  127. package/src/kyro-cms.d.ts +209 -0
  128. package/src/layouts/AdminLayout.astro +128 -11
  129. package/src/layouts/AuthLayout.astro +21 -5
  130. package/src/lib/api.ts +175 -55
  131. package/src/lib/autoform-store.ts +435 -0
  132. package/src/lib/config.ts +82 -34
  133. package/src/lib/createRegistry.ts +29 -0
  134. package/src/lib/default-kyro-config.ts +4 -0
  135. package/src/lib/globals.ts +50 -0
  136. package/src/lib/media-utils.ts +18 -0
  137. package/src/lib/object-utils.ts +77 -0
  138. package/src/lib/paths.ts +61 -0
  139. package/src/lib/stores/index.ts +370 -0
  140. package/src/lib/types.ts +43 -0
  141. package/src/lib/useResourceManager.ts +105 -0
  142. package/src/pages/403.astro +67 -0
  143. package/src/pages/[collection]/[id].astro +14 -180
  144. package/src/pages/[collection]/index.astro +11 -6
  145. package/src/pages/api-explorer.astro +173 -0
  146. package/src/pages/audit/index.astro +2 -0
  147. package/src/pages/auth/login.astro +122 -0
  148. package/src/pages/auth/register.astro +167 -0
  149. package/src/pages/graphql-explorer.astro +59 -0
  150. package/src/pages/{admin/graphql.astro → graphql.astro} +51 -17
  151. package/src/pages/index.astro +577 -0
  152. package/src/pages/index_ALT.astro +3 -0
  153. package/src/pages/keys.astro +11 -0
  154. package/src/pages/marketplace.astro +11 -0
  155. package/src/pages/media.astro +3 -0
  156. package/src/pages/plugins.astro +8 -0
  157. package/src/pages/preview/[collection]/[id].astro +188 -123
  158. package/src/pages/rest-playground.astro +62 -0
  159. package/src/pages/roles/index.astro +183 -76
  160. package/src/pages/sessions.astro +8 -0
  161. package/src/pages/settings/[slug].astro +92 -114
  162. package/src/pages/settings/index.astro +5 -3
  163. package/src/pages/users/[id].astro +25 -154
  164. package/src/pages/users/index.astro +19 -130
  165. package/src/pages/users/new.astro +9 -86
  166. package/src/pages/webhooks.astro +11 -0
  167. package/src/routes.ts +80 -0
  168. package/src/styles/main.css +119 -79
  169. package/src/theme/tokens.ts +1 -0
  170. package/src/vite-env.d.ts +14 -0
  171. package/src/collections/auth/index.ts +0 -155
  172. package/src/collections/portfolio/index.ts +0 -343
  173. package/src/components/ApiExplorer.tsx +0 -325
  174. package/src/components/EnhancedListView.tsx +0 -889
  175. package/src/components/GraphQLExplorer.tsx +0 -675
  176. package/src/components/Icons.tsx +0 -23
  177. package/src/components/StatusBadge.tsx +0 -76
  178. package/src/lib/MediaService.ts +0 -541
  179. package/src/lib/auth/sqlite-adapter.ts +0 -319
  180. package/src/lib/dataStore.ts +0 -226
  181. package/src/lib/db/adapter.ts +0 -54
  182. package/src/lib/db/drizzle-mysql-adapter.ts +0 -194
  183. package/src/lib/db/drizzle-mysql-auth-adapter.ts +0 -327
  184. package/src/lib/db/drizzle-postgres-adapter.ts +0 -202
  185. package/src/lib/db/drizzle-postgres-auth-adapter.ts +0 -304
  186. package/src/lib/db/drizzle-sqlite-adapter.ts +0 -227
  187. package/src/lib/db/drizzle-sqlite-auth-adapter.ts +0 -548
  188. package/src/lib/db/index.ts +0 -449
  189. package/src/lib/db/mongodb-adapter.ts +0 -207
  190. package/src/lib/db/mongodb-auth-adapter.ts +0 -305
  191. package/src/lib/db/schema/mysql-auth.ts +0 -113
  192. package/src/lib/db/schema/mysql-content.ts +0 -20
  193. package/src/lib/db/schema/postgres-auth.ts +0 -116
  194. package/src/lib/db/schema/postgres-content.ts +0 -35
  195. package/src/lib/db/schema/postgres-media.ts +0 -52
  196. package/src/lib/db/schema/postgres-settings.ts +0 -11
  197. package/src/lib/db/schema/sqlite-auth.ts +0 -112
  198. package/src/lib/db/schema/sqlite-content.ts +0 -20
  199. package/src/lib/db/version-adapter.ts +0 -248
  200. package/src/lib/graphql/index.ts +0 -1
  201. package/src/lib/graphql/schema.ts +0 -443
  202. package/src/lib/rate-limit.ts +0 -267
  203. package/src/lib/storage.ts +0 -374
  204. package/src/lib/store.ts +0 -85
  205. package/src/middleware.ts +0 -177
  206. package/src/pages/admin/api-explorer.astro +0 -98
  207. package/src/pages/admin/graphql-explorer.astro +0 -40
  208. package/src/pages/admin/index.astro +0 -286
  209. package/src/pages/admin/keys.astro +0 -8
  210. package/src/pages/admin/rest-playground.astro +0 -44
  211. package/src/pages/admin/webhooks.astro +0 -8
  212. package/src/pages/api/[collection]/[id]/publish.ts +0 -52
  213. package/src/pages/api/[collection]/[id]/unpublish.ts +0 -42
  214. package/src/pages/api/[collection]/[id]/versions.ts +0 -66
  215. package/src/pages/api/[collection]/[id].ts +0 -213
  216. package/src/pages/api/[collection]/index.ts +0 -209
  217. package/src/pages/api/auth/[id].ts +0 -121
  218. package/src/pages/api/auth/audit-logs.ts +0 -57
  219. package/src/pages/api/auth/login.ts +0 -211
  220. package/src/pages/api/auth/logout.ts +0 -66
  221. package/src/pages/api/auth/me.ts +0 -36
  222. package/src/pages/api/auth/refresh.ts +0 -119
  223. package/src/pages/api/auth/register.ts +0 -188
  224. package/src/pages/api/auth/users.ts +0 -97
  225. package/src/pages/api/collections.ts +0 -59
  226. package/src/pages/api/globals/[slug].ts +0 -42
  227. package/src/pages/api/graphql.ts +0 -90
  228. package/src/pages/api/health.ts +0 -426
  229. package/src/pages/api/keys/[id].ts +0 -26
  230. package/src/pages/api/keys/index.ts +0 -75
  231. package/src/pages/api/media/[id].ts +0 -309
  232. package/src/pages/api/media/folders.ts +0 -609
  233. package/src/pages/api/media/index.ts +0 -146
  234. package/src/pages/api/media/resize.ts +0 -267
  235. package/src/pages/api/search.ts +0 -82
  236. package/src/pages/api/slug-availability.ts +0 -70
  237. package/src/pages/api/storage-config.ts +0 -20
  238. package/src/pages/api/storage-status.ts +0 -206
  239. package/src/pages/api/upload.ts +0 -334
  240. package/src/pages/api/webhooks/index.ts +0 -71
  241. package/src/pages/login.astro +0 -82
  242. package/src/pages/register.astro +0 -102
@@ -1,343 +0,0 @@
1
- import type { CollectionConfig } from "@kyro-cms/core";
2
-
3
- export const portfolioCollections: Record<string, CollectionConfig> = {
4
- projects: {
5
- slug: "projects",
6
- label: "Projects",
7
- labelPlural: "Projects",
8
- singularLabel: "Project",
9
- admin: {
10
- useAsTitle: "title",
11
- defaultColumns: ["title", "featured", "status", "createdAt"],
12
- description: "Portfolio projects and case studies",
13
- },
14
- fields: [
15
- {
16
- name: "title",
17
- type: "text",
18
- required: true,
19
- label: "Title",
20
- admin: { description: "Project title" },
21
- },
22
- {
23
- name: "slug",
24
- type: "text",
25
- required: true,
26
- label: "Slug",
27
- admin: { description: "URL-friendly identifier" },
28
- },
29
- {
30
- name: "client",
31
- type: "text",
32
- label: "Client",
33
- admin: { description: "Client name" },
34
- },
35
- {
36
- name: "description",
37
- type: "richtext",
38
- label: "Description",
39
- admin: { description: "Project description" },
40
- },
41
- {
42
- name: "featuredImage",
43
- type: "upload",
44
- label: "Featured Image",
45
- relationTo: "media",
46
- admin: { description: "Main project image" },
47
- },
48
- {
49
- name: "gallery",
50
- type: "array",
51
- label: "Gallery",
52
- fields: [
53
- {
54
- name: "image",
55
- type: "upload",
56
- label: "Image",
57
- relationTo: "media",
58
- },
59
- {
60
- name: "caption",
61
- type: "text",
62
- label: "Caption",
63
- },
64
- ],
65
- admin: { description: "Additional project images" },
66
- },
67
- {
68
- name: "technologies",
69
- type: "array",
70
- label: "Technologies",
71
- fields: [
72
- {
73
- name: "name",
74
- type: "text",
75
- label: "Technology",
76
- },
77
- ],
78
- admin: { description: "Tools and technologies used" },
79
- },
80
- {
81
- name: "link",
82
- type: "text",
83
- label: "Project URL",
84
- admin: { description: "Live project link" },
85
- },
86
- {
87
- name: "github",
88
- type: "text",
89
- label: "GitHub URL",
90
- admin: { description: "Repository link" },
91
- },
92
- {
93
- name: "featured",
94
- type: "checkbox",
95
- label: "Featured",
96
- defaultValue: false,
97
- admin: { description: "Show on homepage" },
98
- },
99
- {
100
- name: "status",
101
- type: "select",
102
- label: "Status",
103
- options: [
104
- { label: "Draft", value: "draft" },
105
- { label: "Published", value: "published" },
106
- { label: "Archived", value: "archived" },
107
- ],
108
- defaultValue: "draft",
109
- },
110
- {
111
- name: "order",
112
- type: "number",
113
- label: "Display Order",
114
- defaultValue: 0,
115
- admin: { description: "Sort order" },
116
- },
117
- {
118
- name: "completedAt",
119
- type: "date",
120
- label: "Completed At",
121
- admin: { description: "Project completion date" },
122
- },
123
- ],
124
- timestamps: true,
125
- },
126
-
127
- team: {
128
- slug: "team",
129
- label: "Team Members",
130
- labelPlural: "Team Members",
131
- singularLabel: "Team Member",
132
- admin: {
133
- useAsTitle: "name",
134
- defaultColumns: ["name", "role", "order", "createdAt"],
135
- description: "Team members and staff",
136
- },
137
- fields: [
138
- {
139
- name: "name",
140
- type: "text",
141
- required: true,
142
- label: "Name",
143
- admin: { description: "Full name" },
144
- },
145
- {
146
- name: "role",
147
- type: "text",
148
- required: true,
149
- label: "Role",
150
- admin: { description: "Job title or role" },
151
- },
152
- {
153
- name: "bio",
154
- type: "markdown",
155
- label: "Bio",
156
- admin: { description: "Short biography" },
157
- },
158
- {
159
- name: "photo",
160
- type: "upload",
161
- label: "Photo",
162
- relationTo: "media",
163
- admin: { description: "Profile photo" },
164
- },
165
- {
166
- name: "gallery",
167
- type: "array",
168
- label: "Gallery",
169
- fields: [
170
- {
171
- name: "image",
172
- type: "upload",
173
- label: "Image",
174
- relationTo: "media",
175
- },
176
- {
177
- name: "caption",
178
- type: "text",
179
- label: "Caption",
180
- },
181
- ],
182
- admin: { description: "Additional photos" },
183
- },
184
- {
185
- name: "email",
186
- type: "email",
187
- label: "Email",
188
- admin: { description: "Contact email" },
189
- },
190
- {
191
- name: "phone",
192
- type: "text",
193
- label: "Phone",
194
- admin: { description: "Contact phone" },
195
- },
196
- {
197
- name: "linkedin",
198
- type: "text",
199
- label: "LinkedIn",
200
- admin: { description: "LinkedIn profile URL" },
201
- },
202
- {
203
- name: "twitter",
204
- type: "text",
205
- label: "Twitter",
206
- admin: { description: "Twitter handle" },
207
- },
208
- {
209
- name: "github",
210
- type: "text",
211
- label: "GitHub",
212
- admin: { description: "GitHub profile URL" },
213
- },
214
- {
215
- name: "website",
216
- type: "text",
217
- label: "Website",
218
- admin: { description: "Personal website" },
219
- },
220
- {
221
- name: "order",
222
- type: "number",
223
- label: "Display Order",
224
- defaultValue: 0,
225
- admin: { description: "Sort order" },
226
- },
227
- {
228
- name: "status",
229
- type: "select",
230
- label: "Status",
231
- options: [
232
- { label: "Active", value: "active" },
233
- { label: "Inactive", value: "inactive" },
234
- ],
235
- defaultValue: "active",
236
- },
237
- ],
238
- timestamps: true,
239
- },
240
-
241
- menus: {
242
- slug: "menus",
243
- label: "Menus",
244
- labelPlural: "Menus",
245
- singularLabel: "Menu",
246
- admin: {
247
- useAsTitle: "name",
248
- defaultColumns: ["name", "location", "createdAt"],
249
- description: "Navigation menus",
250
- },
251
- fields: [
252
- {
253
- name: "name",
254
- type: "text",
255
- required: true,
256
- label: "Name",
257
- admin: { description: "Menu name" },
258
- },
259
- {
260
- name: "location",
261
- type: "select",
262
- label: "Location",
263
- required: true,
264
- options: [
265
- { label: "Header", value: "header" },
266
- { label: "Footer", value: "footer" },
267
- { label: "Mobile", value: "mobile" },
268
- { label: "Sidebar", value: "sidebar" },
269
- ],
270
- admin: { description: "Where to display this menu" },
271
- },
272
- {
273
- name: "items",
274
- type: "array",
275
- label: "Menu Items",
276
- admin: { description: "Navigation items" },
277
- fields: [
278
- {
279
- name: "label",
280
- type: "text",
281
- label: "Label",
282
- },
283
- {
284
- name: "url",
285
- type: "text",
286
- label: "URL",
287
- admin: { description: "Link address" },
288
- },
289
- {
290
- name: "target",
291
- type: "select",
292
- label: "Target",
293
- options: [
294
- { label: "Same tab", value: "_self" },
295
- { label: "New tab", value: "_blank" },
296
- ],
297
- defaultValue: "_self",
298
- },
299
- {
300
- name: "icon",
301
- type: "text",
302
- label: "Icon",
303
- admin: { description: "Icon name" },
304
- },
305
- {
306
- name: "order",
307
- type: "number",
308
- label: "Order",
309
- defaultValue: 0,
310
- },
311
- {
312
- name: "children",
313
- type: "array",
314
- label: "Sub-items",
315
- fields: [
316
- {
317
- name: "label",
318
- type: "text",
319
- label: "Label",
320
- },
321
- {
322
- name: "url",
323
- type: "text",
324
- label: "URL",
325
- },
326
- {
327
- name: "target",
328
- type: "select",
329
- label: "Target",
330
- options: [
331
- { label: "Same tab", value: "_self" },
332
- { label: "New tab", value: "_blank" },
333
- ],
334
- defaultValue: "_self",
335
- },
336
- ],
337
- },
338
- ],
339
- },
340
- ],
341
- timestamps: true,
342
- },
343
- };
@@ -1,325 +0,0 @@
1
- import React, { useState, useEffect, useCallback } from "react";
2
-
3
- interface Collection {
4
- name: string;
5
- slug: string;
6
- fields: number;
7
- endpoints: {
8
- list: string;
9
- create: string;
10
- read: string;
11
- update: string;
12
- delete: string;
13
- };
14
- }
15
-
16
- interface ApiExplorerProps {
17
- collections: Collection[];
18
- }
19
-
20
- export function ApiExplorer({ collections }: ApiExplorerProps) {
21
- const [selectedEndpoint, setSelectedEndpoint] = useState<{
22
- collection: string;
23
- method: string;
24
- url: string;
25
- description: string;
26
- } | null>(null);
27
- const [response, setResponse] = useState<string>("");
28
- const [loading, setLoading] = useState(false);
29
- const [method, setMethod] = useState<"GET" | "POST" | "PATCH" | "DELETE">(
30
- "GET",
31
- );
32
- const [url, setUrl] = useState("");
33
- const [requestBody, setRequestBody] = useState("");
34
- const [status, setStatus] = useState<number | null>(null);
35
-
36
- useEffect(() => {
37
- if (collections.length > 0) {
38
- const first = collections[0];
39
- setSelectedEndpoint({
40
- collection: first.slug,
41
- method: "GET",
42
- url: `/api/${first.slug}`,
43
- description: `List all ${first.name}`,
44
- });
45
- setUrl(`/api/${first.slug}`);
46
- }
47
- }, [collections]);
48
-
49
- const handleEndpointClick = useCallback(
50
- (endpoint: typeof selectedEndpoint) => {
51
- if (!endpoint) return;
52
- setSelectedEndpoint(endpoint);
53
- setUrl(endpoint.url);
54
- setMethod(endpoint.method as "GET" | "POST" | "PATCH" | "DELETE");
55
- setRequestBody("");
56
- setResponse("");
57
- setStatus(null);
58
- },
59
- [],
60
- );
61
-
62
- const executeRequest = async () => {
63
- if (!url) return;
64
-
65
- setLoading(true);
66
- setResponse("");
67
- setStatus(null);
68
-
69
- try {
70
- const options: RequestInit = {
71
- method,
72
- headers: {
73
- "Content-Type": "application/json",
74
- },
75
- };
76
-
77
- if (["POST", "PATCH"].includes(method) && requestBody) {
78
- options.body = requestBody;
79
- }
80
-
81
- const res = await fetch(url, options);
82
- setStatus(res.status);
83
-
84
- const contentType = res.headers.get("content-type");
85
- if (contentType?.includes("application/json")) {
86
- const data = await res.json();
87
- setResponse(JSON.stringify(data, null, 2));
88
- } else {
89
- const text = await res.text();
90
- setResponse(text);
91
- }
92
- } catch (error) {
93
- setResponse(
94
- `Error: ${error instanceof Error ? error.message : "Unknown error"}`,
95
- );
96
- setStatus(500);
97
- } finally {
98
- setLoading(false);
99
- }
100
- };
101
-
102
- const getMethodColor = (m: string) => {
103
- switch (m) {
104
- case "GET":
105
- return "bg-green-500/10 text-green-600 border-green-500/20";
106
- case "POST":
107
- return "bg-blue-500/10 text-blue-600 border-blue-500/20";
108
- case "PATCH":
109
- return "bg-yellow-500/10 text-yellow-600 border-yellow-500/20";
110
- case "DELETE":
111
- return "bg-red-500/10 text-red-600 border-red-500/20";
112
- default:
113
- return "bg-gray-500/10 text-gray-600 border-gray-500/20";
114
- }
115
- };
116
-
117
- const getStatusColor = (s: number) => {
118
- if (s >= 200 && s < 300) return "text-green-600 bg-green-500/10";
119
- if (s >= 400 && s < 500) return "text-yellow-600 bg-yellow-500/10";
120
- if (s >= 500) return "text-red-600 bg-red-500/10";
121
- return "text-gray-600 bg-gray-500/10";
122
- };
123
-
124
- return (
125
- <div className="h-[calc(100vh-200px)] min-h-[600px] flex gap-6">
126
- {/* Sidebar - Collections */}
127
- <div className="w-80 flex-shrink-0 overflow-y-auto">
128
- <div className="space-y-4">
129
- {collections.map((collection) => (
130
- <div key={collection.slug} className="space-y-2">
131
- <h3 className="font-bold text-[var(--kyro-text-primary)] px-3 py-2 bg-[var(--kyro-surface-accent)] rounded-lg">
132
- {collection.name}
133
- </h3>
134
- <div className="space-y-1 pl-2">
135
- <button type="button"
136
- onClick={() =>
137
- handleEndpointClick({
138
- collection: collection.slug,
139
- method: "GET",
140
- url: `/api/${collection.slug}`,
141
- description: `List all ${collection.name}`,
142
- })
143
- }
144
- className={`w-full text-left px-3 py-2 rounded-lg text-sm transition-colors ${
145
- selectedEndpoint?.url === `/api/${collection.slug}` &&
146
- method === "GET"
147
- ? "bg-[var(--kyro-sidebar-active)] text-[var(--kyro-sidebar-text-active)]"
148
- : "hover:bg-[var(--kyro-surface-accent)] text-[var(--kyro-text-secondary)]"
149
- }`}
150
- >
151
- <span
152
- className={`inline-block px-1.5 py-0.5 rounded text-[10px] font-bold mr-2 ${getMethodColor("GET")}`}
153
- >
154
- GET
155
- </span>
156
- List
157
- </button>
158
- <button type="button"
159
- onClick={() =>
160
- handleEndpointClick({
161
- collection: collection.slug,
162
- method: "POST",
163
- url: `/api/${collection.slug}`,
164
- description: `Create new ${collection.name}`,
165
- })
166
- }
167
- className={`w-full text-left px-3 py-2 rounded-lg text-sm transition-colors ${
168
- selectedEndpoint?.url === `/api/${collection.slug}` &&
169
- method === "POST"
170
- ? "bg-[var(--kyro-sidebar-active)] text-[var(--kyro-sidebar-text-active)]"
171
- : "hover:bg-[var(--kyro-surface-accent)] text-[var(--kyro-text-secondary)]"
172
- }`}
173
- >
174
- <span
175
- className={`inline-block px-1.5 py-0.5 rounded text-[10px] font-bold mr-2 ${getMethodColor("POST")}`}
176
- >
177
- POST
178
- </span>
179
- Create
180
- </button>
181
- <button type="button"
182
- onClick={() =>
183
- handleEndpointClick({
184
- collection: collection.slug,
185
- method: "GET",
186
- url: `/api/${collection.slug}/:id`,
187
- description: `Get ${collection.name} by ID`,
188
- })
189
- }
190
- className={`w-full text-left px-3 py-2 rounded-lg text-sm transition-colors ${
191
- selectedEndpoint?.url === `/api/${collection.slug}/:id` &&
192
- method === "GET"
193
- ? "bg-[var(--kyro-sidebar-active)] text-[var(--kyro-sidebar-text-active)]"
194
- : "hover:bg-[var(--kyro-surface-accent)] text-[var(--kyro-text-secondary)]"
195
- }`}
196
- >
197
- <span
198
- className={`inline-block px-1.5 py-0.5 rounded text-[10px] font-bold mr-2 ${getMethodColor("GET")}`}
199
- >
200
- GET
201
- </span>
202
- Get by ID
203
- </button>
204
- <button type="button"
205
- onClick={() =>
206
- handleEndpointClick({
207
- collection: collection.slug,
208
- method: "PATCH",
209
- url: `/api/${collection.slug}/:id`,
210
- description: `Update ${collection.name}`,
211
- })
212
- }
213
- className={`w-full text-left px-3 py-2 rounded-lg text-sm transition-colors ${
214
- selectedEndpoint?.url === `/api/${collection.slug}/:id` &&
215
- method === "PATCH"
216
- ? "bg-[var(--kyro-sidebar-active)] text-[var(--kyro-sidebar-text-active)]"
217
- : "hover:bg-[var(--kyro-surface-accent)] text-[var(--kyro-text-secondary)]"
218
- }`}
219
- >
220
- <span
221
- className={`inline-block px-1.5 py-0.5 rounded text-[10px] font-bold mr-2 ${getMethodColor("PATCH")}`}
222
- >
223
- PATCH
224
- </span>
225
- Update
226
- </button>
227
- <button type="button"
228
- onClick={() =>
229
- handleEndpointClick({
230
- collection: collection.slug,
231
- method: "DELETE",
232
- url: `/api/${collection.slug}/:id`,
233
- description: `Delete ${collection.name}`,
234
- })
235
- }
236
- className={`w-full text-left px-3 py-2 rounded-lg text-sm transition-colors ${
237
- selectedEndpoint?.url === `/api/${collection.slug}/:id` &&
238
- method === "DELETE"
239
- ? "bg-[var(--kyro-sidebar-active)] text-[var(--kyro-sidebar-text-active)]"
240
- : "hover:bg-[var(--kyro-surface-accent)] text-[var(--kyro-text-secondary)]"
241
- }`}
242
- >
243
- <span
244
- className={`inline-block px-1.5 py-0.5 rounded text-[10px] font-bold mr-2 ${getMethodColor("DELETE")}`}
245
- >
246
- DEL
247
- </span>
248
- Delete
249
- </button>
250
- </div>
251
- </div>
252
- ))}
253
- </div>
254
- </div>
255
-
256
- {/* Main Content */}
257
- <div className="flex-1 flex flex-col min-w-0">
258
- {/* URL Bar */}
259
- <div className="flex gap-3 mb-4">
260
- <select
261
- value={method}
262
- onChange={(e) =>
263
- setMethod(e.target.value as "GET" | "POST" | "PATCH" | "DELETE")
264
- }
265
- className={`px-4 py-2 rounded-lg font-bold text-sm border ${getMethodColor(method)}`}
266
- >
267
- <option value="GET">GET</option>
268
- <option value="POST">POST</option>
269
- <option value="PATCH">PATCH</option>
270
- <option value="DELETE">DELETE</option>
271
- </select>
272
- <input
273
- type="text"
274
- value={url}
275
- onChange={(e) => setUrl(e.target.value)}
276
- placeholder="/api/collection"
277
- className="flex-1 px-4 py-2 bg-[var(--kyro-surface-accent)] border border-[var(--kyro-border)] rounded-lg text-sm font-mono focus:outline-none focus:border-[var(--kyro-sidebar-active)]"
278
- />
279
- <button type="button"
280
- onClick={executeRequest}
281
- disabled={loading || !url}
282
- className="px-6 py-2 bg-[var(--kyro-sidebar-active)] text-[var(--kyro-sidebar-text-active)] rounded-lg font-bold text-sm hover:opacity-90 transition-opacity disabled:opacity-50"
283
- >
284
- {loading ? "Sending..." : "Send"}
285
- </button>
286
- </div>
287
-
288
- {/* Request Body (for POST/PATCH) */}
289
- {["POST", "PATCH"].includes(method) && (
290
- <div className="mb-4">
291
- <label className="block text-sm font-bold text-[var(--kyro-text-secondary)] mb-2">
292
- Request Body (JSON)
293
- </label>
294
- <textarea
295
- value={requestBody}
296
- onChange={(e) => setRequestBody(e.target.value)}
297
- placeholder='{"field": "value"}'
298
- rows={6}
299
- className="w-full px-4 py-3 bg-[var(--kyro-surface)] border border-[var(--kyro-border)] rounded-lg text-sm font-mono focus:outline-none focus:border-[var(--kyro-sidebar-active)] resize-y"
300
- />
301
- </div>
302
- )}
303
-
304
- {/* Response */}
305
- <div className="flex-1 flex flex-col min-h-0">
306
- <div className="flex items-center justify-between mb-2">
307
- <label className="text-sm font-bold text-[var(--kyro-text-secondary)]">
308
- Response
309
- </label>
310
- {status && (
311
- <span
312
- className={`px-3 py-1 rounded-lg text-sm font-bold ${getStatusColor(status)}`}
313
- >
314
- {status}
315
- </span>
316
- )}
317
- </div>
318
- <pre className="flex-1 p-4 bg-[var(--kyro-surface)] border border-[var(--kyro-border)] rounded-lg text-xs font-mono overflow-auto whitespace-pre-wrap">
319
- {response || "Send a request to see the response"}
320
- </pre>
321
- </div>
322
- </div>
323
- </div>
324
- );
325
- }