@kyro-cms/admin 0.9.4 → 0.9.6

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 (44) hide show
  1. package/dist/index.cjs +966 -585
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.css +29 -9
  4. package/dist/index.css.map +1 -1
  5. package/dist/index.d.cts +3 -1
  6. package/dist/index.d.ts +3 -1
  7. package/dist/index.js +649 -268
  8. package/dist/index.js.map +1 -1
  9. package/package.json +2 -2
  10. package/src/components/ActionBar.tsx +254 -70
  11. package/src/components/Admin.tsx +10 -17
  12. package/src/components/ApiKeysManager.tsx +1 -0
  13. package/src/components/AuditLogsPage.tsx +3 -3
  14. package/src/components/AutoForm.tsx +51 -34
  15. package/src/components/DetailView.tsx +37 -13
  16. package/src/components/GraphQLPlayground.tsx +460 -224
  17. package/src/components/ListView.tsx +3 -3
  18. package/src/components/LoginPage.tsx +5 -30
  19. package/src/components/MediaGallery.tsx +122 -15
  20. package/src/components/RestPlayground.tsx +443 -519
  21. package/src/components/Sidebar.astro +6 -2
  22. package/src/components/UserManagement.tsx +4 -4
  23. package/src/components/WebhookManager.tsx +4 -4
  24. package/src/components/blocks/AccordionBlock.tsx +1 -1
  25. package/src/components/blocks/ArrayBlock.tsx +1 -1
  26. package/src/components/blocks/ChildBlocksTree.tsx +6 -6
  27. package/src/components/blocks/CodeBlock.tsx +1 -1
  28. package/src/components/blocks/FileBlock.tsx +1 -1
  29. package/src/components/blocks/HeroBlock.tsx +1 -1
  30. package/src/components/blocks/ListBlock.tsx +1 -1
  31. package/src/components/blocks/RelationshipBlock.tsx +1 -1
  32. package/src/components/blocks/RichTextBlock.tsx +1 -1
  33. package/src/components/blocks/VideoBlock.tsx +1 -1
  34. package/src/components/fields/BlocksField.tsx +17 -19
  35. package/src/components/ui/PageHeader.tsx +205 -83
  36. package/src/components/ui/Pagination.tsx +2 -2
  37. package/src/components/ui/SlidePanel.tsx +4 -4
  38. package/src/layouts/AdminLayout.astro +64 -4
  39. package/src/lib/useResourceManager.ts +1 -0
  40. package/src/pages/graphql-explorer.astro +7 -51
  41. package/src/pages/graphql.astro +7 -119
  42. package/src/pages/index.astro +4 -63
  43. package/src/pages/rest-playground.astro +3 -29
  44. package/src/styles/main.css +32 -9
@@ -92,9 +92,10 @@ function isActive(item: NavItem): boolean {
92
92
  ---
93
93
 
94
94
  <aside
95
- class="surface-tile w-[320px] flex flex-col flex-shrink-0 overflow-hidden"
95
+ id="kyro-sidebar"
96
+ class="surface-tile w-[280px] md:w-[320px] flex flex-col flex-shrink-0 overflow-hidden fixed md:static inset-y-0 left-0 z-50 md:z-auto transition-transform duration-300 -translate-x-full md:translate-x-0 h-[100dvh] md:h-auto border-r md:border-r-0 border-[var(--kyro-border)] rounded-r-2xl md:rounded-3xl rounded-l-none md:rounded-l-3xl shadow-2xl md:shadow-none"
96
97
  >
97
- <div class="px-8 py-8 flex items-center gap-3">
98
+ <div class="px-6 md:px-8 py-6 md:py-8 flex items-center justify-between gap-3">
98
99
  {
99
100
  siteLogo ? (
100
101
  <img
@@ -113,6 +114,9 @@ function isActive(item: NavItem): boolean {
113
114
  </span>
114
115
  )
115
116
  }
117
+ <button id="mobile-close-btn" class="md:hidden p-2 -mr-2 rounded-lg text-[var(--kyro-text-secondary)] hover:bg-[var(--kyro-surface-accent)] transition-colors">
118
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
119
+ </button>
116
120
  </div>
117
121
 
118
122
  <nav class="flex-1 px-4 overflow-y-auto" id="sidebar-nav">
@@ -132,7 +132,7 @@ export function UserManagement() {
132
132
  );
133
133
 
134
134
  return (
135
- <div className="w-full space-y-6 animate-in fade-in slide-in-from-bottom-4 duration-700 px-8 pb-12">
135
+ <div className="w-full space-y-6 animate-in fade-in slide-in-from-bottom-4 duration-700 px-4 md:px-8 pb-12">
136
136
  {/* Header */}
137
137
  <PageHeader
138
138
  title="Identity & Access"
@@ -169,10 +169,10 @@ export function UserManagement() {
169
169
  </div>
170
170
 
171
171
  {/* User Table */}
172
- <div className="surface-tile overflow-hidden">
173
- <table className="w-full text-left table-fixed">
172
+ <div className="surface-tile overflow-x-auto">
173
+ <table className="w-full text-left">
174
174
  <thead>
175
- <tr className="text-[var(--kyro-text-secondary)] font-bold text-[9px] tracking-[0.2em] uppercase border-b border-[var(--kyro-border)]">
175
+ <tr className="text-[var(--kyro-text-secondary)] font-bold text-[9px] tracking-[0.2em] uppercase border-b border-[var(--kyro-border)] whitespace-nowrap">
176
176
  <th className="px-6 py-4 w-64">Member Identity</th>
177
177
  <th className="px-6 py-4">Administrative Role</th>
178
178
  <th className="px-6 py-4">Security Status</th>
@@ -246,14 +246,14 @@ export function WebhookManager() {
246
246
  </div>
247
247
  </div>
248
248
 
249
- <div className="grid sm:grid-cols-3 gap-6 pt-2">
249
+ <div className="grid grid-cols-1 sm:grid-cols-3 gap-6 pt-2">
250
250
  <div className="space-y-1">
251
251
  <span className="text-[9px] font-bold uppercase tracking-widest opacity-30">Destination</span>
252
252
  <div className="font-mono text-xs opacity-60 truncate max-w-[200px]" title={webhook.url}>
253
253
  {webhook.url}
254
254
  </div>
255
255
  </div>
256
- <div className="space-y-1 border-l border-[var(--kyro-border)] pl-6">
256
+ <div className="space-y-1 sm:border-l border-t sm:border-t-0 border-[var(--kyro-border)] pt-4 sm:pt-0 sm:pl-6 mt-4 sm:mt-0">
257
257
  <span className="text-[9px] font-bold uppercase tracking-widest opacity-30">Events</span>
258
258
  <div className="flex flex-wrap gap-1">
259
259
  {webhook.events.slice(0, 2).map((event) => (
@@ -266,7 +266,7 @@ export function WebhookManager() {
266
266
  )}
267
267
  </div>
268
268
  </div>
269
- <div className="space-y-1 border-l border-[var(--kyro-border)] pl-6">
269
+ <div className="space-y-1 sm:border-l border-t sm:border-t-0 border-[var(--kyro-border)] pt-4 sm:pt-0 sm:pl-6 mt-4 sm:mt-0">
270
270
  <span className="text-[9px] font-bold uppercase tracking-widest opacity-30">Activity</span>
271
271
  <div className="text-[10px] font-bold opacity-60 flex items-center gap-1.5">
272
272
  <Clock className="w-3 h-3" />
@@ -367,7 +367,7 @@ export function WebhookManager() {
367
367
 
368
368
  <div className="space-y-4">
369
369
  <label className="block text-xs font-bold mb-1.5 text-[var(--kyro-text-secondary)] uppercase tracking-wider">Subscribed Events</label>
370
- <div className="grid grid-cols-2 gap-4">
370
+ <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
371
371
  {eventOptions.map((opt) => (
372
372
  <button
373
373
  type="button"
@@ -51,7 +51,7 @@ export const AccordionBlock: React.FC<{ block: Record<string, unknown>; index: n
51
51
  <button
52
52
  type="button"
53
53
  onClick={() => removeBlock(block.id)}
54
- className="p-1 hover:bg-red-50 rounded text-red-500"
54
+ className="p-1 hover:bg-[var(--kyro-danger-bg)] rounded text-[var(--kyro-danger)]"
55
55
  title="Remove"
56
56
  >
57
57
  <X className="w-3 h-3" />
@@ -47,7 +47,7 @@ export const ArrayBlock: React.FC<{ block: Record<string, unknown>; index: numbe
47
47
  <button
48
48
  type="button"
49
49
  onClick={() => removeBlock(block.id)}
50
- className="p-1 hover:bg-red-50 rounded text-red-500"
50
+ className="p-1 hover:bg-[var(--kyro-danger-bg)] rounded text-[var(--kyro-danger)]"
51
51
  title="Remove"
52
52
  >
53
53
  <X className="w-3 h-3" />
@@ -165,7 +165,7 @@ export const ChildBlocksTree: React.FC<ChildBlocksTreeProps> = ({
165
165
  handleRemoveChild(child.id);
166
166
  setConfirmDeleteId(null);
167
167
  }}
168
- className="px-2 py-1 text-xs bg-red-500 text-white rounded hover:bg-red-600"
168
+ className="px-2 py-1 text-xs bg-[var(--kyro-danger)] text-white rounded"
169
169
  >
170
170
  Remove
171
171
  </button>
@@ -184,9 +184,9 @@ export const ChildBlocksTree: React.FC<ChildBlocksTreeProps> = ({
184
184
  e.stopPropagation();
185
185
  setConfirmDeleteId(child.id);
186
186
  }}
187
- className="p-1.5 rounded-md transition-opacity cursor-pointer hover:bg-red-50"
187
+ className="p-1.5 rounded-md transition-opacity cursor-pointer hover:bg-[var(--kyro-danger-bg)]"
188
188
  >
189
- <X className="w-3.5 h-3.5 text-red-500 invisible group-hover/column:visible" />
189
+ <X className="w-3.5 h-3.5 text-[var(--kyro-danger)] invisible group-hover/column:visible" />
190
190
  </button>
191
191
  )}
192
192
  </div>
@@ -445,7 +445,7 @@ const NestedChildBlocks: React.FC<NestedChildBlocksProps> = ({
445
445
  handleRemoveChild(child.id);
446
446
  setConfirmDeleteId(null);
447
447
  }}
448
- className="px-2 py-1 text-xs bg-red-500 text-white rounded hover:bg-red-600"
448
+ className="px-2 py-1 text-xs bg-[var(--kyro-danger)] text-white rounded"
449
449
  >
450
450
  Remove
451
451
  </button>
@@ -464,9 +464,9 @@ const NestedChildBlocks: React.FC<NestedChildBlocksProps> = ({
464
464
  e.stopPropagation();
465
465
  setConfirmDeleteId(child.id);
466
466
  }}
467
- className="p-1.5 rounded-md invisible group-hover:visible transition-opacity cursor-pointer hover:bg-red-50"
467
+ className="p-1.5 rounded-md invisible group-hover:visible transition-opacity cursor-pointer hover:bg-[var(--kyro-danger-bg)]"
468
468
  >
469
- <X className="w-3.5 h-3.5 text-red-500" />
469
+ <X className="w-3.5 h-3.5 text-[var(--kyro-danger)]" />
470
470
  </button>
471
471
  )}
472
472
  </div>
@@ -47,7 +47,7 @@ export const CodeBlock: React.FC<{ block: Record<string, unknown>; index: number
47
47
  <button
48
48
  type="button"
49
49
  onClick={() => removeBlock(block.id)}
50
- className="p-1.5 hover:bg-red-500/10 rounded-lg transition-all text-[var(--kyro-text-muted)] hover:text-red-500"
50
+ className="p-1.5 hover:bg-[var(--kyro-danger-bg)] rounded-lg transition-all text-[var(--kyro-text-muted)] hover:text-[var(--kyro-danger)]"
51
51
  title="Remove"
52
52
  >
53
53
  <X className="w-4 h-4" />
@@ -45,7 +45,7 @@ export const FileBlock: React.FC<{ block: Record<string, unknown>; index: number
45
45
  <button
46
46
  type="button"
47
47
  onClick={() => removeBlock(block.id)}
48
- className="p-1 hover:bg-red-50 rounded text-red-500"
48
+ className="p-1 hover:bg-[var(--kyro-danger-bg)] rounded text-[var(--kyro-danger)]"
49
49
  title="Remove"
50
50
  >
51
51
  <X className="w-3 h-3" />
@@ -50,7 +50,7 @@ export const HeroBlock: React.FC<{ block: Record<string, unknown>; index: number
50
50
  <button
51
51
  type="button"
52
52
  onClick={() => removeBlock(block.id)}
53
- className="p-1 hover:bg-red-50 rounded text-red-500"
53
+ className="p-1 hover:bg-[var(--kyro-danger-bg)] rounded text-[var(--kyro-danger)]"
54
54
  title="Remove"
55
55
  >
56
56
  <X className="w-3 h-3" />
@@ -46,7 +46,7 @@ export const ListBlock: React.FC<{ block: Record<string, unknown>; index: number
46
46
  <button
47
47
  type="button"
48
48
  onClick={() => removeBlock(block.id)}
49
- className="p-1 hover:bg-red-50 rounded text-red-500"
49
+ className="p-1 hover:bg-[var(--kyro-danger-bg)] rounded text-[var(--kyro-danger)]"
50
50
  title="Remove"
51
51
  >
52
52
  <X className="w-3 h-3" />
@@ -50,7 +50,7 @@ export const RelationshipBlock: React.FC<{ block: Record<string, unknown>; index
50
50
  <button
51
51
  type="button"
52
52
  onClick={() => removeBlock(block.id)}
53
- className="p-1 hover:bg-red-50 rounded text-red-500"
53
+ className="p-1 hover:bg-[var(--kyro-danger-bg)] rounded text-[var(--kyro-danger)]"
54
54
  title="Remove"
55
55
  >
56
56
  <X className="w-3 h-3" />
@@ -48,7 +48,7 @@ export const RichTextBlock: React.FC<{ block: any; index: number }> = ({
48
48
  <button
49
49
  type="button"
50
50
  onClick={() => removeBlock(block.id)}
51
- className="p-1 hover:bg-red-50 rounded text-red-500"
51
+ className="p-1 hover:bg-[var(--kyro-danger-bg)] rounded text-[var(--kyro-danger)]"
52
52
  title="Remove"
53
53
  >
54
54
  <X className="w-3 h-3" />
@@ -45,7 +45,7 @@ export const VideoBlock: React.FC<{ block: Record<string, unknown>; index: numbe
45
45
  <button
46
46
  type="button"
47
47
  onClick={() => removeBlock(block.id)}
48
- className="p-1 hover:bg-red-50 rounded text-red-500"
48
+ className="p-1 hover:bg-[var(--kyro-danger-bg)] rounded text-[var(--kyro-danger)]"
49
49
  title="Remove"
50
50
  >
51
51
  <X className="w-3 h-3" />
@@ -106,11 +106,10 @@ const SortableBlockComponent = ({
106
106
  <div ref={setNodeRef} style={style} className="relative group">
107
107
  <div
108
108
  onClick={() => setEditingBlockId(block.id as string)}
109
- className={`flex items-center gap-1 pl-5 pr-1.5 py-1 bg-[var(--kyro-bg-secondary)] rounded-md border transition-colors cursor-pointer text-xs whitespace-nowrap ${
110
- isEditing
109
+ className={`flex items-center gap-1 pl-5 pr-1.5 py-1 bg-[var(--kyro-bg-secondary)] rounded-md border transition-colors cursor-pointer text-xs whitespace-nowrap ${isEditing
111
110
  ? `${(blockTheme[block.type as string] || blockTheme.default).border} bg-[var(--kyro-primary)]/5`
112
111
  : "border-[var(--kyro-border)] hover:border-[var(--kyro-primary)]/50 hover:bg-[var(--kyro-primary)]/5"
113
- }`}
112
+ }`}
114
113
  >
115
114
  <div
116
115
  className="absolute left-0.5 top-1/2 -translate-y-1/2 p-0.5 cursor-grab active:cursor-grabbing text-[var(--kyro-text-muted)] opacity-0 group-hover:opacity-100 transition-opacity hover:bg-[var(--kyro-surface-accent)] rounded"
@@ -139,7 +138,7 @@ const SortableBlockComponent = ({
139
138
  removeBlock(block.id as string);
140
139
  setShowDeleteConfirm(false);
141
140
  }}
142
- className="px-1.5 py-0.5 text-[9px] bg-red-500 hover:bg-red-600 text-white rounded font-semibold transition-colors"
141
+ className="px-1.5 py-0.5 text-[9px] bg-[var(--kyro-danger)] text-white rounded font-semibold transition-colors hover:brightness-90"
143
142
  >
144
143
  Remove
145
144
  </button>
@@ -170,7 +169,7 @@ const SortableBlockComponent = ({
170
169
  e.stopPropagation();
171
170
  setShowDeleteConfirm(true);
172
171
  }}
173
- className="p-0.5 hover:bg-red-50 hover:text-red-500 rounded text-[var(--kyro-text-muted)] transition-colors"
172
+ className="p-0.5 hover:bg-[var(--kyro-danger-bg)] hover:text-[var(--kyro-danger)] rounded text-[var(--kyro-text-muted)] transition-colors"
174
173
  title="Remove"
175
174
  >
176
175
  <X className="w-3 h-3" />
@@ -191,9 +190,9 @@ const SortableBlockComponent = ({
191
190
  }
192
191
 
193
192
  return (
194
- <div ref={setNodeRef} style={style} className="relative group pl-8 mb-2">
193
+ <div ref={setNodeRef} style={style} className="relative group md:pl-8 mb-2">
195
194
  <div
196
- className="absolute left-0 top-1/2 -translate-y-1/2 p-1.5 cursor-grab active:cursor-grabbing text-[var(--kyro-text-muted)] opacity-0 group-hover:opacity-100 transition-opacity hover:bg-[var(--kyro-surface-accent)] rounded"
195
+ className="hidden md:absolute left-0 top-1/2 -translate-y-1/2 p-1.5 cursor-grab active:cursor-grabbing text-[var(--kyro-text-muted)] opacity-0 group-hover:opacity-100 transition-opacity hover:bg-[var(--kyro-surface-accent)] rounded"
197
196
  {...attributes}
198
197
  {...listeners}
199
198
  >
@@ -202,11 +201,10 @@ const SortableBlockComponent = ({
202
201
 
203
202
  <div
204
203
  onClick={() => setEditingBlockId(block.id as string)}
205
- className={`flex items-center gap-3 p-3 bg-[var(--kyro-bg-secondary)] rounded-lg border transition-colors cursor-pointer ${
206
- isEditing
204
+ className={`flex items-center gap-3 p-3 bg-[var(--kyro-bg-secondary)] rounded-lg border transition-colors cursor-pointer ${isEditing
207
205
  ? `${(blockTheme[block.type as string] || blockTheme.default).border} bg-[var(--kyro-primary)]/5`
208
206
  : "border-[var(--kyro-border)] hover:border-[var(--kyro-primary)]/50 hover:bg-[var(--kyro-primary)]/5"
209
- }`}
207
+ }`}
210
208
  >
211
209
  {blockIcons[block.type as string] && (
212
210
  <span className="text-[var(--kyro-text-secondary)]">
@@ -247,7 +245,7 @@ const SortableBlockComponent = ({
247
245
  removeBlock(block.id as string);
248
246
  setShowDeleteConfirm(false);
249
247
  }}
250
- className="px-2.5 py-1 text-[10px] bg-red-500 hover:bg-red-600 text-white rounded font-semibold transition-colors"
248
+ className="px-2.5 py-1 text-[10px] bg-[var(--kyro-danger)] text-white rounded font-semibold transition-colors hover:brightness-90"
251
249
  >
252
250
  Remove
253
251
  </button>
@@ -278,7 +276,7 @@ const SortableBlockComponent = ({
278
276
  e.stopPropagation();
279
277
  setShowDeleteConfirm(true);
280
278
  }}
281
- className="p-1 hover:bg-red-50 hover:text-red-500 rounded text-[var(--kyro-text-muted)] transition-colors"
279
+ className="p-1 hover:bg-[var(--kyro-danger-bg)] hover:text-[var(--kyro-danger)] rounded text-[var(--kyro-text-muted)] transition-colors"
282
280
  title="Remove Block"
283
281
  >
284
282
  <X className="w-3.5 h-3.5" />
@@ -394,7 +392,7 @@ export const BlocksField: React.FC<BlocksFieldProps> = ({
394
392
  useEffect(() => {
395
393
  const valueArray = Array.isArray(value) ? value : [];
396
394
  const lastValueArray = lastValueRef.current || [];
397
-
395
+
398
396
  // Deep compare to catch external data changes (e.g. discard draft / auto-save restore)
399
397
  if (JSON.stringify(valueArray) !== JSON.stringify(lastValueArray)) {
400
398
  const valueArrayCopy = [...valueArray];
@@ -528,17 +526,17 @@ export const BlocksField: React.FC<BlocksFieldProps> = ({
528
526
 
529
527
  // Render active drag overlay
530
528
  const activeBlock = activeDrag
531
- ? blockCategories
529
+ ? dynamicCategories
532
530
  .flatMap((cat) => cat.blocks)
533
531
  .find((b) => `drawer-${b.type}` === activeDrag.id) ||
534
532
  blocks.find((b) => b.id === activeDrag.id)
535
533
  : null;
536
534
 
537
- const activeBlockLabel = activeBlock
538
- ? "label" in activeBlock
539
- ? (activeBlock as Record<string, unknown>).label
540
- : activeBlock.type
541
- : "Block";
535
+ const activeBlockLabel = activeBlock
536
+ ? "label" in activeBlock
537
+ ? (activeBlock as Record<string, unknown>).label
538
+ : activeBlock.type
539
+ : "Block";
542
540
 
543
541
  const borderClass = getBorderClass();
544
542