@kyro-cms/admin 0.9.1 → 0.9.3

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 (37) hide show
  1. package/dist/index.cjs +1196 -1727
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +4 -3
  4. package/dist/index.d.ts +4 -3
  5. package/dist/index.js +891 -1422
  6. package/dist/index.js.map +1 -1
  7. package/package.json +2 -2
  8. package/src/components/ActionBar.tsx +25 -174
  9. package/src/components/Admin.tsx +1 -3
  10. package/src/components/AuditLogsPage.tsx +2 -13
  11. package/src/components/AutoForm.tsx +160 -265
  12. package/src/components/DetailView.tsx +38 -66
  13. package/src/components/FieldRenderer.tsx +1 -1
  14. package/src/components/ListView.tsx +26 -198
  15. package/src/components/MediaGallery.tsx +117 -175
  16. package/src/components/RestPlayground.tsx +54 -47
  17. package/src/components/fields/BlocksField.tsx +8 -10
  18. package/src/components/fields/RelationshipBlockField.tsx +2 -3
  19. package/src/components/fields/RelationshipField.tsx +2 -3
  20. package/src/components/fix_imports.cjs +23 -0
  21. package/src/components/fix_imports2.cjs +19 -0
  22. package/src/components/replace_svgs.cjs +63 -0
  23. package/src/components/ui/Dropdown.tsx +7 -2
  24. package/src/components/ui/Modal.tsx +24 -27
  25. package/src/components/ui/PromptModal.tsx +2 -10
  26. package/src/components/ui/SlidePanel.tsx +2 -10
  27. package/src/components/ui/SplitButton.tsx +107 -0
  28. package/src/components/ui/Toaster.tsx +0 -1
  29. package/src/components/ui/icons.tsx +1 -0
  30. package/src/components/users/UsersList.tsx +8 -85
  31. package/src/hooks/useAutoFormState.ts +89 -161
  32. package/src/hooks/useQueue.ts +60 -0
  33. package/src/layouts/AdminLayout.astro +22 -2
  34. package/src/layouts/AuthLayout.astro +66 -18
  35. package/src/lib/autoform-store.ts +6 -2
  36. package/src/lib/globals.ts +5 -3
  37. package/src/pages/auth/register.astro +5 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kyro-cms/admin",
3
- "version": "0.9.1",
3
+ "version": "0.9.3",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },
@@ -120,7 +120,7 @@
120
120
  "vitest": "^4.1.4"
121
121
  },
122
122
  "peerDependencies": {
123
- "@kyro-cms/core": "^0.6.0",
123
+ "@kyro-cms/core": "^0.9.2",
124
124
  "react": "^19.0.0",
125
125
  "react-dom": "^19.0.0"
126
126
  },
@@ -1,5 +1,7 @@
1
- import React, { useState, useEffect, useCallback } from "react";
2
- import { Dropdown, DropdownItem, DropdownSeparator } from "./ui/Dropdown";
1
+ import React from "react";
2
+ import { IconPlus, IconSend, IconClock, IconArchive, IconUndo, IconCopy, IconEye, IconTrash2 } from "./ui/icons";
3
+ import { DropdownItem, DropdownSeparator } from "./ui/Dropdown";
4
+ import { SplitButton } from "./ui/SplitButton";
3
5
  import { Spinner } from "./ui/Spinner";
4
6
 
5
7
  export type DocumentStatus = "draft" | "published" | "scheduled" | "archived";
@@ -42,81 +44,27 @@ export function ActionBar({
42
44
  return null;
43
45
  };
44
46
 
45
- const getSaveButtonClass = () => {
46
- const base = "kyro-btn kyro-btn-md text-[11px] font-bold tracking-widest transition-all duration-300";
47
- if (saveStatus === "saving") return `${base} bg-[var(--kyro-gray-400)] text-white opacity-70 cursor-wait`;
48
- if (saveStatus === "saved") return `${base} bg-[var(--kyro-success)] border-[var(--kyro-success)] text-white shadow-[0_0_15px_rgba(34,197,94,0.3)]`;
49
- if (saveStatus === "error") return `${base} bg-[var(--kyro-error)] border-[var(--kyro-error)] text-white shadow-[0_0_15px_rgba(239,68,68,0.3)]`;
50
- if (hasChanges) return `${base} bg-[var(--kyro-warning)] border-[var(--kyro-warning)] text-black shadow-[0_0_15px_rgba(255,174,0,0.3)] animate-pulse`;
51
- return `${base} bg-[var(--kyro-gray-500)] border-[var(--kyro-gray-500)] text-white hover:bg-[var(--kyro-gray-600)]`;
52
- };
53
-
54
47
  const getStatusBadge = () => {
55
48
  const statusConfig = {
56
49
  draft: {
57
50
  label: "Draft",
58
51
  class: "bg-gray-100 text-gray-600 border-gray-200",
59
- icon: (
60
- <svg
61
- width="12"
62
- height="12"
63
- viewBox="0 0 24 24"
64
- fill="none"
65
- stroke="currentColor"
66
- strokeWidth="2.5"
67
- >
68
- <path d="M12 2v20M2 12h20" />
69
- </svg>
70
- ),
52
+ icon: <IconPlus className="w-3 h-3" />,
71
53
  },
72
54
  published: {
73
55
  label: "Published",
74
56
  class: "bg-green-100 text-green-700 border-green-200",
75
- icon: (
76
- <svg
77
- width="12"
78
- height="12"
79
- viewBox="0 0 24 24"
80
- fill="none"
81
- stroke="currentColor"
82
- strokeWidth="2.5"
83
- >
84
- <path d="M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z" />
85
- </svg>
86
- ),
57
+ icon: <IconSend className="w-3 h-3" />,
87
58
  },
88
59
  scheduled: {
89
60
  label: "Scheduled",
90
61
  class: "bg-blue-100 text-blue-700 border-blue-200",
91
- icon: (
92
- <svg
93
- width="12"
94
- height="12"
95
- viewBox="0 0 24 24"
96
- fill="none"
97
- stroke="currentColor"
98
- strokeWidth="2.5"
99
- >
100
- <circle cx="12" cy="12" r="10" />
101
- <path d="M12 6v6l4 2" />
102
- </svg>
103
- ),
62
+ icon: <IconClock className="w-3 h-3" />,
104
63
  },
105
64
  archived: {
106
65
  label: "Archived",
107
66
  class: "bg-yellow-100 text-yellow-700 border-yellow-200",
108
- icon: (
109
- <svg
110
- width="12"
111
- height="12"
112
- viewBox="0 0 24 24"
113
- fill="none"
114
- stroke="currentColor"
115
- strokeWidth="2.5"
116
- >
117
- <path d="M21 8v13H3V8M1 3h22v5H1zM10 12h4" />
118
- </svg>
119
- ),
67
+ icon: <IconArchive className="w-3 h-3" />,
120
68
  },
121
69
  };
122
70
  const config = statusConfig[status];
@@ -142,7 +90,7 @@ export function ActionBar({
142
90
  {getStatusBadge()}
143
91
  {getSaveStatusText() && (
144
92
  <span
145
- className={`text-sm ${saveStatus === "error" ? "text-red-500" : "text-gray-500"}`}
93
+ className={`text-sm ${saveStatus === "error" ? "text-[var(--kyro-error)]" : "text-[var(--kyro-text-muted)]"}`}
146
94
  >
147
95
  {saveStatus === "saving" ? (
148
96
  <Spinner size="sm" className="inline mr-1" />
@@ -153,7 +101,7 @@ export function ActionBar({
153
101
  </div>
154
102
  <div className="text-xs space-y-0.5">
155
103
  {updatedAt && (
156
- <div className="text-gray-400">
104
+ <div className="text-[var(--kyro-text-muted)]">
157
105
  Updated: {formatDate(updatedAt)}
158
106
  </div>
159
107
  )}
@@ -170,18 +118,9 @@ export function ActionBar({
170
118
  <button type="button"
171
119
  onClick={onPublish}
172
120
  disabled={saveStatus === "saving"}
173
- className="kyro-btn-success hover:opacity-90 kyro-btn-md flex items-center gap-2"
121
+ className="kyro-btn kyro-btn-primary kyro-btn-md flex items-center gap-2"
174
122
  >
175
- <svg
176
- width="16"
177
- height="16"
178
- viewBox="0 0 24 24"
179
- fill="none"
180
- stroke="currentColor"
181
- strokeWidth="2.5"
182
- >
183
- <path d="M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z" />
184
- </svg>
123
+ <IconSend className="w-4 h-4" />
185
124
  Publish
186
125
  </button>
187
126
  )}
@@ -189,116 +128,37 @@ export function ActionBar({
189
128
  <button type="button"
190
129
  onClick={onUnpublish}
191
130
  disabled={saveStatus === "saving"}
192
- className="kyro-btn kyro-btn-warning kyro-btn-md flex items-center gap-2"
131
+ className="kyro-btn kyro-btn-secondary kyro-btn-md flex items-center gap-2"
193
132
  >
194
- <svg
195
- width="16"
196
- height="16"
197
- viewBox="0 0 24 24"
198
- fill="none"
199
- stroke="currentColor"
200
- strokeWidth="2.5"
201
- >
202
- <path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8M3 3v5h5" />
203
- </svg>
133
+ <IconUndo className="w-4 h-4" />
204
134
  Unpublish
205
135
  </button>
206
136
  )}
207
- {status === "published" && !hasChanges && saveStatus !== "saving" && saveStatus !== "error" ? (
208
- <span className="inline-flex items-center gap-1.5 px-4 py-2 rounded-lg text-[11px] font-bold tracking-widest bg-green-100 text-green-700 border border-green-200 cursor-not-allowed shadow-sm">
209
- <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
210
- <path d="M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z" />
211
- </svg>
212
- Published
213
- </span>
214
- ) : (
215
- <button type="button"
216
- onClick={onSave}
217
- disabled={
218
- saveStatus === "saving" || (!hasChanges && saveStatus !== "error")
219
- }
220
- className={getSaveButtonClass()}
221
- >
222
- {saveStatus === "saving"
223
- ? "Saving..."
224
- : saveStatus === "saved"
225
- ? "Saved"
226
- : hasChanges && status === "published"
227
- ? "Save Draft"
228
- : status === "draft" && hasChanges
229
- ? "Save Draft"
230
- : "Save"}
231
- </button>
232
- )}
233
137
 
234
- <Dropdown
235
- trigger={
236
- <button type="button" className="kyro-btn kyro-btn-ghost kyro-btn-md p-2">
237
- <svg
238
- width="16"
239
- height="16"
240
- viewBox="0 0 24 24"
241
- fill="none"
242
- stroke="currentColor"
243
- strokeWidth="2"
244
- >
245
- <circle cx="12" cy="12" r="1" />
246
- <circle cx="12" cy="5" r="1" />
247
- <circle cx="12" cy="19" r="1" />
248
- </svg>
249
- </button>
250
- }
138
+ {/* Button Group: Save + Dropdown */}
139
+ <SplitButton
140
+ status={status}
141
+ saveStatus={saveStatus}
142
+ hasChanges={hasChanges}
143
+ onPublish={onSave}
251
144
  >
252
145
  {onDuplicate && (
253
146
  <DropdownItem
254
- onClick={onDuplicate}
255
- icon={
256
- <svg
257
- viewBox="0 0 24 24"
258
- fill="none"
259
- stroke="currentColor"
260
- strokeWidth="2"
261
- >
262
- <rect x="9" y="9" width="13" height="13" rx="2" />
263
- <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
264
- </svg>
265
- }
147
+ icon={<IconCopy className="w-4 h-4" />}
266
148
  >
267
149
  Duplicate
268
150
  </DropdownItem>
269
151
  )}
270
152
  {onViewHistory && (
271
153
  <DropdownItem
272
- onClick={onViewHistory}
273
- icon={
274
- <svg
275
- viewBox="0 0 24 24"
276
- fill="none"
277
- stroke="currentColor"
278
- strokeWidth="2"
279
- >
280
- <circle cx="12" cy="12" r="10" />
281
- <polyline points="12,6 12,12 16,14" />
282
- </svg>
283
- }
154
+ icon={<IconClock className="w-4 h-4" />}
284
155
  >
285
156
  View History
286
157
  </DropdownItem>
287
158
  )}
288
159
  {onPreview && (
289
160
  <DropdownItem
290
- onClick={onPreview}
291
- icon={
292
- <svg
293
- viewBox="0 0 24 24"
294
- fill="none"
295
- stroke="currentColor"
296
- strokeWidth="2"
297
- >
298
- <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
299
- <circle cx="12" cy="12" r="3" />
300
- </svg>
301
- }
161
+ icon={<IconEye className="w-4 h-4" />}
302
162
  >
303
163
  Preview
304
164
  </DropdownItem>
@@ -308,21 +168,12 @@ export function ActionBar({
308
168
  <DropdownItem
309
169
  onClick={onDelete}
310
170
  danger
311
- icon={
312
- <svg
313
- viewBox="0 0 24 24"
314
- fill="none"
315
- stroke="currentColor"
316
- strokeWidth="2"
317
- >
318
- <path d="M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
319
- </svg>
320
- }
171
+ icon={<IconTrash2 className="w-4 h-4" />}
321
172
  >
322
173
  Delete
323
174
  </DropdownItem>
324
175
  )}
325
- </Dropdown>
176
+ </SplitButton>
326
177
  </div>
327
178
  </div>
328
179
  );
@@ -14,7 +14,7 @@ import { WebhookManager } from "./WebhookManager";
14
14
  import { MediaGallery } from "./MediaGallery";
15
15
  import { CommandPalette } from "./ui/CommandPalette";
16
16
  import { GlobalModal } from "./ui/GlobalModal";
17
- import { Toast, ToastProvider } from "./ui/Toast";
17
+ import { Toast } from "./ui/Toast";
18
18
  import { ThemeProvider, type ThemeMode } from "./ThemeProvider";
19
19
  import { toArray, toCollectionMap, toGlobalMap } from "../lib/config";
20
20
  import "../styles/main.css";
@@ -199,7 +199,6 @@ const toasts = useToastStore((state) => state.toasts);
199
199
 
200
200
  return (
201
201
  <ThemeProvider {...({ mode: theme, onChange: onThemeChange } as any)}>
202
- <ToastProvider>
203
202
  <div className="kyro-admin min-h-screen bg-[var(--kyro-bg)] text-[var(--kyro-text-primary)]">
204
203
  <div className="flex h-screen overflow-hidden">
205
204
  <main className="flex-1 flex flex-col min-w-0 overflow-hidden">
@@ -228,7 +227,6 @@ const toasts = useToastStore((state) => state.toasts);
228
227
  ))}
229
228
  </div>
230
229
  </div>
231
- </ToastProvider>
232
230
  </ThemeProvider>
233
231
  );
234
232
  }
@@ -1,3 +1,4 @@
1
+ import { Search } from "./ui/icons";
1
2
  import React, { useState, useEffect, useCallback } from "react";
2
3
  import { fetchWithAuth } from "../lib/api";
3
4
  import { Modal } from "./ui/Modal";
@@ -230,19 +231,7 @@ export function AuditLogsPage() {
230
231
  {/* Filters */}
231
232
  <div className="surface-tile p-4 flex flex-wrap items-center gap-3">
232
233
  <div className="relative flex-1 min-w-48">
233
- <svg
234
- className="absolute left-3.5 top-1/2 -translate-y-1/2 w-4 h-4 text-[var(--kyro-text-muted)]"
235
- fill="none"
236
- stroke="currentColor"
237
- viewBox="0 0 24 24"
238
- >
239
- <path
240
- strokeLinecap="round"
241
- strokeLinejoin="round"
242
- strokeWidth="2"
243
- d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
244
- />
245
- </svg>
234
+ <Search className="w-4 h-4" />
246
235
  <input
247
236
  type="text"
248
237
  placeholder="Search by user email..."