@kyro-cms/admin 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/README.md +149 -51
  2. package/package.json +53 -6
  3. package/src/collections/auth/index.ts +2 -2
  4. package/src/collections/portfolio/index.ts +343 -0
  5. package/src/components/ActionBar.tsx +153 -16
  6. package/src/components/Admin.tsx +136 -27
  7. package/src/components/ApiExplorer.tsx +325 -0
  8. package/src/components/ApiKeysManager.tsx +563 -0
  9. package/src/components/AuditLogsPage.tsx +664 -0
  10. package/src/components/AutoForm.tsx +1417 -661
  11. package/src/components/BrandingHub.tsx +267 -0
  12. package/src/components/BulkActionsBar.tsx +3 -3
  13. package/src/components/CreateView.tsx +3 -3
  14. package/src/components/Dashboard.tsx +393 -0
  15. package/src/components/DetailView.tsx +199 -57
  16. package/src/components/DeveloperCenter.tsx +403 -0
  17. package/src/components/EnhancedListView.tsx +786 -0
  18. package/src/components/GraphQLExplorer.tsx +675 -0
  19. package/src/components/GraphQLPlayground.tsx +627 -0
  20. package/src/components/ListView.tsx +191 -53
  21. package/src/components/MediaGallery.tsx +1569 -0
  22. package/src/components/Modal.tsx +149 -0
  23. package/src/components/RestPlayground.tsx +951 -0
  24. package/src/components/Sidebar.astro +237 -0
  25. package/src/components/UserManagement.tsx +204 -0
  26. package/src/components/VersionHistoryPanel.tsx +3 -3
  27. package/src/components/WebhookManager.tsx +608 -0
  28. package/src/components/blocks/AccordionBlock.tsx +97 -0
  29. package/src/components/blocks/ArrayBlock.tsx +75 -0
  30. package/src/components/blocks/BlockEditModal.MARKER +12 -0
  31. package/src/components/blocks/BlockEditModal.tsx +774 -0
  32. package/src/components/blocks/ButtonBlock.tsx +165 -0
  33. package/src/components/blocks/ChildBlocksTree.tsx +551 -0
  34. package/src/components/blocks/CodeBlock.tsx +66 -0
  35. package/src/components/blocks/ColumnsBlock.tsx +151 -0
  36. package/src/components/blocks/DividerBlock.tsx +43 -0
  37. package/src/components/blocks/FileBlock.tsx +64 -0
  38. package/src/components/blocks/HeadingBlock.tsx +81 -0
  39. package/src/components/blocks/HeroBlock.tsx +157 -0
  40. package/src/components/blocks/ImageBlock.tsx +83 -0
  41. package/src/components/blocks/LinkBlock.tsx +71 -0
  42. package/src/components/blocks/ListBlock.tsx +39 -0
  43. package/src/components/blocks/ParagraphBlock.tsx +61 -0
  44. package/src/components/blocks/RelationshipBlock.tsx +279 -0
  45. package/src/components/blocks/VStackBlock.tsx +75 -0
  46. package/src/components/blocks/VideoBlock.tsx +45 -0
  47. package/src/components/blocks/index.ts +10 -0
  48. package/src/components/fields/BlocksField.tsx +323 -0
  49. package/src/components/fields/CheckboxField.tsx +15 -9
  50. package/src/components/fields/CodeField.tsx +234 -0
  51. package/src/components/fields/DateField.tsx +38 -11
  52. package/src/components/fields/EditorClient.tsx +271 -0
  53. package/src/components/fields/FileField.tsx +390 -0
  54. package/src/components/fields/HybridContentField.tsx +109 -0
  55. package/src/components/fields/ImageField.tsx +429 -0
  56. package/src/components/fields/JSONField.tsx +361 -0
  57. package/src/components/fields/MarkdownField.tsx +282 -0
  58. package/src/components/fields/NumberField.tsx +42 -12
  59. package/src/components/fields/PortableTextField.tsx +143 -0
  60. package/src/components/fields/PortableTextRenderer.tsx +68 -0
  61. package/src/components/fields/RelationshipField.tsx +231 -59
  62. package/src/components/fields/SelectField.tsx +25 -15
  63. package/src/components/fields/TextField.tsx +45 -14
  64. package/src/components/fields/extensions/blockComponents.tsx +237 -0
  65. package/src/components/fields/extensions/blocksStore.ts +273 -0
  66. package/src/components/fields/index.ts +13 -0
  67. package/src/components/index.ts +1 -2
  68. package/src/components/layout/Header.tsx +2 -2
  69. package/src/components/layout/Layout.tsx +2 -2
  70. package/src/components/ui/Badge.tsx +9 -4
  71. package/src/components/ui/BlockDrawer.tsx +79 -0
  72. package/src/components/ui/Button.tsx +1 -1
  73. package/src/components/ui/CommandPalette.tsx +362 -0
  74. package/src/components/ui/CommandPaletteWrapper.tsx +97 -0
  75. package/src/components/ui/Dropdown.tsx +1 -1
  76. package/src/components/ui/Modal.tsx +37 -12
  77. package/src/components/ui/PromptModal.tsx +94 -0
  78. package/src/components/ui/SlidePanel.tsx +43 -16
  79. package/src/components/ui/Toast.tsx +80 -14
  80. package/src/env.d.ts +16 -0
  81. package/src/env.ts +20 -0
  82. package/src/index.ts +0 -1
  83. package/src/layouts/AdminLayout.astro +164 -170
  84. package/src/layouts/AuthLayout.astro +23 -6
  85. package/src/lib/MediaService.ts +541 -0
  86. package/src/lib/auth/sqlite-adapter.ts +319 -0
  87. package/src/lib/config.ts +22 -6
  88. package/src/lib/dataStore.ts +132 -74
  89. package/src/lib/db/adapter.ts +54 -0
  90. package/src/lib/db/drizzle-mysql-adapter.ts +194 -0
  91. package/src/lib/db/drizzle-mysql-auth-adapter.ts +327 -0
  92. package/src/lib/db/drizzle-postgres-adapter.ts +202 -0
  93. package/src/lib/db/drizzle-postgres-auth-adapter.ts +304 -0
  94. package/src/lib/db/drizzle-sqlite-adapter.ts +227 -0
  95. package/src/lib/db/drizzle-sqlite-auth-adapter.ts +548 -0
  96. package/src/lib/db/index.ts +449 -0
  97. package/src/lib/db/mongodb-adapter.ts +207 -0
  98. package/src/lib/db/mongodb-auth-adapter.ts +305 -0
  99. package/src/lib/db/schema/mysql-auth.ts +113 -0
  100. package/src/lib/db/schema/mysql-content.ts +20 -0
  101. package/src/lib/db/schema/postgres-auth.ts +116 -0
  102. package/src/lib/db/schema/postgres-content.ts +35 -0
  103. package/src/lib/db/schema/postgres-media.ts +52 -0
  104. package/src/lib/db/schema/postgres-settings.ts +11 -0
  105. package/src/lib/db/schema/sqlite-auth.ts +112 -0
  106. package/src/lib/db/schema/sqlite-content.ts +20 -0
  107. package/src/lib/graphql/index.ts +1 -0
  108. package/src/lib/graphql/schema.ts +443 -0
  109. package/src/lib/rate-limit.ts +267 -0
  110. package/src/lib/storage.ts +374 -0
  111. package/src/lib/store.ts +85 -0
  112. package/src/middleware.ts +70 -11
  113. package/src/pages/[collection]/[id].astro +178 -122
  114. package/src/pages/[collection]/index.astro +24 -156
  115. package/src/pages/admin/api-explorer.astro +98 -0
  116. package/src/pages/admin/graphql-explorer.astro +40 -0
  117. package/src/pages/admin/graphql.astro +97 -0
  118. package/src/pages/admin/index.astro +200 -139
  119. package/src/pages/admin/keys.astro +8 -0
  120. package/src/pages/admin/rest-playground.astro +44 -0
  121. package/src/pages/admin/webhooks.astro +8 -0
  122. package/src/pages/api/[collection]/[id]/publish.ts +44 -0
  123. package/src/pages/api/[collection]/[id]/unpublish.ts +42 -0
  124. package/src/pages/api/[collection]/[id]/versions.ts +36 -0
  125. package/src/pages/api/[collection]/[id].ts +102 -159
  126. package/src/pages/api/[collection]/index.ts +151 -230
  127. package/src/pages/api/auth/[id].ts +48 -69
  128. package/src/pages/api/auth/audit-logs.ts +20 -43
  129. package/src/pages/api/auth/login.ts +159 -45
  130. package/src/pages/api/auth/logout.ts +42 -24
  131. package/src/pages/api/auth/refresh.ts +119 -0
  132. package/src/pages/api/auth/register.ts +110 -40
  133. package/src/pages/api/auth/users.ts +22 -97
  134. package/src/pages/api/collections.ts +59 -0
  135. package/src/pages/api/globals/[slug]/test.ts +172 -0
  136. package/src/pages/api/globals/[slug].ts +42 -0
  137. package/src/pages/api/graphql.ts +90 -0
  138. package/src/pages/api/health.ts +417 -40
  139. package/src/pages/api/keys/[id].ts +26 -0
  140. package/src/pages/api/keys/index.ts +75 -0
  141. package/src/pages/api/media/[id].ts +309 -0
  142. package/src/pages/api/media/folders.ts +609 -0
  143. package/src/pages/api/media/index.ts +146 -0
  144. package/src/pages/api/media/resize.ts +267 -0
  145. package/src/pages/api/search.ts +82 -0
  146. package/src/pages/api/slug-availability.ts +70 -0
  147. package/src/pages/api/storage-config.ts +20 -0
  148. package/src/pages/api/storage-status.ts +206 -0
  149. package/src/pages/api/upload.ts +334 -0
  150. package/src/pages/api/webhooks/index.ts +71 -0
  151. package/src/pages/audit/index.astro +2 -104
  152. package/src/pages/login.astro +11 -11
  153. package/src/pages/media.astro +10 -0
  154. package/src/pages/preview/[collection]/[id].astro +178 -0
  155. package/src/pages/register.astro +13 -13
  156. package/src/pages/roles/index.astro +21 -21
  157. package/src/pages/settings/[slug].astro +162 -0
  158. package/src/pages/settings/index.astro +9 -0
  159. package/src/pages/users/[id].astro +29 -21
  160. package/src/pages/users/index.astro +22 -17
  161. package/src/pages/users/new.astro +18 -17
  162. package/src/styles/main.css +553 -128
  163. package/src/components/layout/Sidebar.tsx +0 -497
@@ -0,0 +1,267 @@
1
+ import React, { useState, useEffect } from "react";
2
+ import {
3
+ Palette,
4
+ Tag,
5
+ Layout,
6
+ Type,
7
+ Image as ImageIcon,
8
+ Save,
9
+ Check,
10
+ RefreshCcw,
11
+ Sparkles,
12
+ } from "lucide-react";
13
+
14
+ export function BrandingHub() {
15
+ const [siteName, setSiteName] = useState("Kyro CMS");
16
+ const [adminTitle, setAdminTitle] = useState("Command Center");
17
+ const [primaryColor, setPrimaryColor] = useState("#6366f1");
18
+ const [dashboardGreeting, setDashboardGreeting] = useState(
19
+ "Welcome back to your Command Center.",
20
+ );
21
+ const [saving, setSaving] = useState(false);
22
+ const [saved, setSaved] = useState(false);
23
+
24
+ useEffect(() => {
25
+ const fetchBranding = async () => {
26
+ try {
27
+ const res = await fetch("/api/globals/site");
28
+ if (res.ok) {
29
+ const result = await res.json();
30
+ const data = result.data || result;
31
+ if (data && Object.keys(data).length > 0) {
32
+ if (data.siteName) setSiteName(data.siteName);
33
+ if (data.adminTitle) setAdminTitle(data.adminTitle);
34
+ if (data.primaryColor) setPrimaryColor(data.primaryColor);
35
+ if (data.dashboardGreeting)
36
+ setDashboardGreeting(data.dashboardGreeting);
37
+ }
38
+ }
39
+ } catch (err) {
40
+ console.error("Failed to load branding:", err);
41
+ }
42
+ };
43
+ fetchBranding();
44
+ }, []);
45
+
46
+ const handleSave = async () => {
47
+ setSaving(true);
48
+ try {
49
+ const res = await fetch("/api/globals/site", {
50
+ method: "PATCH",
51
+ headers: { "Content-Type": "application/json" },
52
+ body: JSON.stringify({
53
+ siteName,
54
+ adminTitle,
55
+ primaryColor,
56
+ dashboardGreeting,
57
+ }),
58
+ });
59
+ if (res.ok) {
60
+ setSaved(true);
61
+ setTimeout(() => setSaved(false), 3000);
62
+ document.documentElement.style.setProperty(
63
+ "--kyro-primary",
64
+ primaryColor,
65
+ );
66
+ } else {
67
+ throw new Error("Failed to save");
68
+ }
69
+ } catch (e) {
70
+ console.error(e);
71
+ } finally {
72
+ setSaving(false);
73
+ }
74
+ };
75
+
76
+ const colors = [
77
+ { name: "Indigo", hex: "#6366f1" },
78
+ { name: "Emerald", hex: "#10b981" },
79
+ { name: "Rose", hex: "#f43f5e" },
80
+ { name: "Amber", hex: "#f59e0b" },
81
+ { name: "Sky", hex: "#0ea5e9" },
82
+ { name: "Violet", hex: "#8b5cf6" },
83
+ ];
84
+
85
+ return (
86
+ <div className="w-full space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-700 px-8 pb-32">
87
+ {/* Header */}
88
+ <div className="flex flex-col lg:flex-row lg:items-center justify-between gap-6 pt-4">
89
+ <div>
90
+ <h1 className="text-4xl font-black tracking-tighter text-[var(--kyro-text-primary)]">
91
+ Branding <span className="text-[var(--kyro-primary)]">Hub</span>
92
+ </h1>
93
+ <p className="text-[var(--kyro-text-secondary)] mt-1 font-medium opacity-60">
94
+ Customize the identity and aesthetic of your administrative
95
+ ecosystem.
96
+ </p>
97
+ </div>
98
+ <div className="flex items-center gap-3">
99
+ <button type="button"
100
+ onClick={handleSave}
101
+ disabled={saving}
102
+ className={`flex items-center gap-2 px-8 py-3 rounded-2xl font-black text-sm shadow-xl transition-all active:scale-95 ${
103
+ saved
104
+ ? "bg-green-500 text-white"
105
+ : "bg-[var(--kyro-primary)] text-white hover:shadow-[var(--kyro-primary)]"
106
+ }`}
107
+ >
108
+ {saving ? (
109
+ <RefreshCcw className="w-4 h-4 animate-spin" />
110
+ ) : saved ? (
111
+ <Check className="w-4 h-4" />
112
+ ) : (
113
+ <Save className="w-4 h-4" />
114
+ )}
115
+ {saving
116
+ ? "Saving..."
117
+ : saved
118
+ ? "Identity Updated"
119
+ : "Publish Branding"}
120
+ </button>
121
+ </div>
122
+ </div>
123
+
124
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
125
+ {/* Identity Settings */}
126
+ <section className="surface-tile p-8 space-y-8">
127
+ <div className="flex items-center gap-3 mb-2">
128
+ <Tag className="w-5 h-5 text-[var(--kyro-primary)]" />
129
+ <h2 className="text-xl font-black tracking-tight">Core Identity</h2>
130
+ </div>
131
+
132
+ <div className="space-y-6">
133
+ <div className="space-y-2">
134
+ <label className="text-[10px] font-black uppercase tracking-[0.2em] opacity-40">
135
+ Site Public Name
136
+ </label>
137
+ <input
138
+ type="text"
139
+ value={siteName}
140
+ onChange={(e) => setSiteName(e.target.value)}
141
+ className="w-full bg-[var(--kyro-bg-secondary)] border border-[var(--kyro-border)] rounded-xl py-3 px-4 text-sm font-bold focus:outline-none focus:ring-2 focus:ring-[var(--kyro-primary)] transition-all"
142
+ placeholder="e.g. Acme Corp CMS"
143
+ />
144
+ </div>
145
+
146
+ <div className="space-y-2">
147
+ <label className="text-[10px] font-black uppercase tracking-[0.2em] opacity-40">
148
+ Admin Dashboard Title
149
+ </label>
150
+ <input
151
+ type="text"
152
+ value={adminTitle}
153
+ onChange={(e) => setAdminTitle(e.target.value)}
154
+ className="w-full bg-[var(--kyro-bg-secondary)] border border-[var(--kyro-border)] rounded-xl py-3 px-4 text-sm font-bold focus:outline-none focus:ring-2 focus:ring-[var(--kyro-primary)] transition-all"
155
+ placeholder="e.g. Command Center"
156
+ />
157
+ </div>
158
+
159
+ <div className="space-y-2">
160
+ <label className="text-[10px] font-black uppercase tracking-[0.2em] opacity-40">
161
+ System Greeting
162
+ </label>
163
+ <textarea
164
+ value={dashboardGreeting}
165
+ onChange={(e) => setDashboardGreeting(e.target.value)}
166
+ rows={3}
167
+ className="w-full bg-[var(--kyro-bg-secondary)] border border-[var(--kyro-border)] rounded-xl py-3 px-4 text-sm font-bold focus:outline-none focus:ring-2 focus:ring-[var(--kyro-primary)] transition-all resize-none"
168
+ placeholder="Greeting text for the dashboard..."
169
+ />
170
+ </div>
171
+ </div>
172
+ </section>
173
+
174
+ {/* Visual Aesthetic */}
175
+ <section className="surface-tile p-8 space-y-8">
176
+ <div className="flex items-center gap-3 mb-2">
177
+ <Palette className="w-5 h-5 text-[var(--kyro-primary)]" />
178
+ <h2 className="text-xl font-black tracking-tight">
179
+ Visual Aesthetic
180
+ </h2>
181
+ </div>
182
+
183
+ <div className="space-y-8">
184
+ <div className="space-y-4">
185
+ <label className="text-[10px] font-black uppercase tracking-[0.2em] opacity-40">
186
+ Primary Brand Color
187
+ </label>
188
+ <div className="grid grid-cols-6 gap-3">
189
+ {colors.map((c) => (
190
+ <button type="button"
191
+ key={c.name}
192
+ onClick={() => setPrimaryColor(c.hex)}
193
+ className={`aspect-square rounded-xl transition-all border-4 ${primaryColor === c.hex ? "border-white ring-2 ring-[var(--kyro-primary)]" : "border-transparent opacity-60 hover:opacity-100"}`}
194
+ style={{ backgroundColor: c.hex }}
195
+ title={c.name}
196
+ />
197
+ ))}
198
+ </div>
199
+ </div>
200
+
201
+ <div className="space-y-4 pt-4 border-t border-[var(--kyro-border)]">
202
+ <label className="text-[10px] font-black uppercase tracking-[0.2em] opacity-40">
203
+ Project Logo (SVG/PNG)
204
+ </label>
205
+ <div className="flex items-center gap-6">
206
+ <div className="w-20 h-20 rounded-2xl bg-[var(--kyro-bg-secondary)] border-2 border-dashed border-[var(--kyro-border)] flex flex-col items-center justify-center text-[var(--kyro-text-secondary)] hover:border-[var(--kyro-primary)] hover:text-[var(--kyro-primary)] cursor-pointer transition-all">
207
+ <ImageIcon className="w-6 h-6 mb-1 opacity-40" />
208
+ <span className="text-[8px] font-black uppercase">
209
+ Upload
210
+ </span>
211
+ </div>
212
+ <div className="flex-1">
213
+ <p className="text-xs font-bold mb-1">
214
+ Upload global CMS logo
215
+ </p>
216
+ <p className="text-[10px] opacity-40 leading-relaxed">
217
+ This will replace the Kyro brand in the sidebar and login
218
+ screens.
219
+ </p>
220
+ </div>
221
+ </div>
222
+ </div>
223
+ </div>
224
+ </section>
225
+
226
+ {/* Live Preview of Dashboard Card */}
227
+ <section className="lg:col-span-2 surface-tile p-8 overflow-hidden relative group">
228
+ <div className="absolute top-0 right-0 p-8 flex items-center gap-2 text-[var(--kyro-primary)]">
229
+ <Sparkles className="w-4 h-4" />
230
+ <span className="text-[10px] font-black uppercase tracking-widest">
231
+ Live Preview
232
+ </span>
233
+ </div>
234
+
235
+ <div className="max-w-2xl mx-auto py-12 text-center space-y-6">
236
+ <div className="inline-flex items-center gap-3 px-4 py-2 bg-[var(--kyro-bg-secondary)] rounded-full border border-[var(--kyro-border)]">
237
+ <span className="w-2 h-2 rounded-full bg-green-500 animate-pulse" />
238
+ <span className="text-[10px] font-black uppercase tracking-widest opacity-60">
239
+ {adminTitle} Online
240
+ </span>
241
+ </div>
242
+ <h2 className="text-5xl font-black tracking-tighter leading-none italic">
243
+ Welcome to {siteName}.
244
+ </h2>
245
+ <p className="text-xl font-medium text-[var(--kyro-text-secondary)] opacity-60">
246
+ {dashboardGreeting}
247
+ </p>
248
+ </div>
249
+
250
+ {/* Simulated Palette Update */}
251
+ <style
252
+ dangerouslySetInnerHTML={{
253
+ __html: `
254
+ :root {
255
+ --kyro-primary-temp: ${primaryColor};
256
+ }
257
+ .preview-btn {
258
+ background-color: var(--kyro-primary-temp);
259
+ }
260
+ `,
261
+ }}
262
+ />
263
+ </section>
264
+ </div>
265
+ </div>
266
+ );
267
+ }
@@ -31,14 +31,14 @@ export function BulkActionsBar({
31
31
  <CountBadge count={selectedCount} />
32
32
  <span className="text-sm text-gray-600">selected</span>
33
33
  </div>
34
- <button
34
+ <button type="button"
35
35
  onClick={onClearSelection}
36
36
  className="text-sm text-gray-500 hover:text-gray-700"
37
37
  >
38
38
  Clear selection
39
39
  </button>
40
40
  {onSelectAll && (
41
- <button
41
+ <button type="button"
42
42
  onClick={onSelectAll}
43
43
  className="text-sm text-gray-500 hover:text-gray-700"
44
44
  >
@@ -49,7 +49,7 @@ export function BulkActionsBar({
49
49
 
50
50
  <Dropdown
51
51
  trigger={
52
- <button className="kyro-btn kyro-btn-secondary kyro-btn-sm">
52
+ <button type="button" className="kyro-btn kyro-btn-secondary kyro-btn-sm">
53
53
  Actions
54
54
  <svg
55
55
  width="12"
@@ -50,7 +50,7 @@ export function CreateView({
50
50
  return (
51
51
  <div className="kyro-detail">
52
52
  <div className="kyro-detail-header">
53
- <button className="kyro-detail-back" onClick={onCancel}>
53
+ <button type="button" className="kyro-detail-back" onClick={onCancel}>
54
54
  <svg
55
55
  width="18"
56
56
  height="18"
@@ -64,14 +64,14 @@ export function CreateView({
64
64
  </button>
65
65
  <h2 className="kyro-detail-title">Create {label}</h2>
66
66
  <div className="kyro-detail-actions">
67
- <button
67
+ <button type="button"
68
68
  className="kyro-btn kyro-btn-secondary kyro-btn-md"
69
69
  onClick={onCancel}
70
70
  disabled={saving}
71
71
  >
72
72
  Cancel
73
73
  </button>
74
- <button
74
+ <button type="button"
75
75
  className="kyro-btn kyro-btn-primary kyro-btn-md"
76
76
  onClick={handleSubmit}
77
77
  disabled={saving}