@kyro-cms/admin 0.1.5 → 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 (164) hide show
  1. package/README.md +149 -51
  2. package/package.json +52 -5
  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 +50 -0
  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 +116 -28
  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 +286 -0
  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 +50 -20
  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 +82 -0
  153. package/src/pages/media.astro +10 -0
  154. package/src/pages/preview/[collection]/[id].astro +178 -0
  155. package/src/pages/register.astro +102 -0
  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
  164. package/src/pages/index.astro +0 -225
package/README.md CHANGED
@@ -4,7 +4,8 @@ Admin dashboard for Kyro CMS — a React-based admin interface built with Astro.
4
4
 
5
5
  ## Features
6
6
 
7
- - **Authentication** — JWT-based login/logout with SQLite auth backend
7
+ - **Multi-Database Support** — SQLite, PostgreSQL, MySQL, MongoDB
8
+ - **Authentication** — JWT-based login/logout with auto-selecting auth adapter
8
9
  - **Collection Management** — Create, edit, and manage content collections
9
10
  - **User Management** — Manage users, roles, and permissions
10
11
  - **Settings** — Configure CMS settings, globals, and plugins
@@ -34,6 +35,9 @@ npm run preview
34
35
 
35
36
  # Type check
36
37
  npm run check
38
+
39
+ # Run tests
40
+ npm run test:run
37
41
  ```
38
42
 
39
43
  ### Integration with Kyro CMS
@@ -78,9 +82,85 @@ The admin dashboard is designed to work alongside a Kyro CMS project. In your As
78
82
  });
79
83
  ```
80
84
 
85
+ ## Database Configuration
86
+
87
+ The admin supports multiple database backends. The auth system automatically uses the same database as your content data.
88
+
89
+ ### SQLite (Default)
90
+
91
+ ```bash
92
+ # No configuration needed - uses ./data/kyro.db
93
+ DB_TYPE=sqlite
94
+ ```
95
+
96
+ ### PostgreSQL
97
+
98
+ ```bash
99
+ DB_TYPE=postgres
100
+ DB_HOST=localhost
101
+ DB_PORT=5432
102
+ DB_NAME=kyro
103
+ DB_USER=admin
104
+ DB_PASSWORD=secret
105
+ # Or use connection string
106
+ DB_CONNECTION_STRING=postgresql://admin:secret@localhost:5432/kyro
107
+ ```
108
+
109
+ ### MySQL
110
+
111
+ ```bash
112
+ DB_TYPE=mysql
113
+ DB_HOST=localhost
114
+ DB_PORT=3306
115
+ DB_NAME=kyro
116
+ DB_USER=root
117
+ DB_PASSWORD=secret
118
+ ```
119
+
120
+ ### MongoDB
121
+
122
+ ```bash
123
+ DB_TYPE=mongodb
124
+ DB_HOST=localhost
125
+ DB_PORT=27017
126
+ DB_NAME=kyro
127
+ # Or use connection string
128
+ DB_CONNECTION_STRING=mongodb://admin:secret@localhost:27017/kyro
129
+ ```
130
+
131
+ ### Connection Pooling
132
+
133
+ For PostgreSQL and MySQL, configure connection pool:
134
+
135
+ ```bash
136
+ DB_POOL_MIN=5
137
+ DB_POOL_MAX=20
138
+ ```
139
+
140
+ ### Environment Variables
141
+
142
+ | Variable | Description | Default |
143
+ | ------------------------- | --------------------------------------------- | ------------------------- |
144
+ | `DB_TYPE` | Database type (sqlite/postgres/mysql/mongodb) | `sqlite` |
145
+ | `DB_CONNECTION_STRING` | Full connection URI | - |
146
+ | `DB_HOST` | Database host | `localhost` |
147
+ | `DB_PORT` | Database port | 5432/3306/27017 |
148
+ | `DB_NAME` | Database name | `kyro` |
149
+ | `DB_USER` | Database username | - |
150
+ | `DB_PASSWORD` | Database password | - |
151
+ | `DB_POOL_MIN` | Minimum pool connections | 5 |
152
+ | `DB_POOL_MAX` | Maximum pool connections | 20 |
153
+ | `JWT_SECRET` | Secret for signing JWT tokens | `change-me-in-production` |
154
+ | `JWT_EXPIRES_IN` | Token expiration time | `24h` |
155
+ | `KYRO_ALLOW_REGISTRATION` | Allow public registration | `true` |
156
+
81
157
  ## Authentication
82
158
 
83
- The admin uses SQLite for auth by default (stored at `./data/auth.db`). No Redis or external services required.
159
+ Auth is automatically handled by the adapter matching your `DB_TYPE`:
160
+
161
+ - **SQLite** → Uses SQLiteAuthAdapter (local file-based)
162
+ - **Postgres** → Uses PostgresAuthAdapter (same DB)
163
+ - **MongoDB** → Uses MongoDBAuthAdapter (same DB)
84
164
 
85
165
  ### Creating Your First Admin User
86
166
 
@@ -89,15 +169,6 @@ The admin uses SQLite for auth by default (stored at `./data/auth.db`). No Redis
89
169
  3. Register with your email and password
90
170
  4. The first user automatically gets the `super_admin` role
91
171
 
92
- ### Environment Variables
93
-
94
- | Variable | Description | Default |
95
- | ------------------------- | ------------------------------------------ | ------------------------- |
96
- | `JWT_SECRET` | Secret for signing JWT tokens | `change-me-in-production` |
97
- | `JWT_EXPIRES_IN` | Token expiration time | `24h` |
98
- | `KYRO_AUTH_DB_PATH` | Path to auth SQLite database | `./data/auth.db` |
99
- | `KYRO_ALLOW_REGISTRATION` | Allow public registration after first user | `true` |
100
-
101
172
  ### Auth API Endpoints
102
173
 
103
174
  | Endpoint | Method | Description |
@@ -114,58 +185,85 @@ The admin uses SQLite for auth by default (stored at `./data/auth.db`). No Redis
114
185
  ```
115
186
  admin/
116
187
  ├── src/
117
- │ ├── components/ # React UI components
118
- │ ├── pages/ # Astro pages + API routes
119
- │ │ └── api/ # REST API endpoints
120
- │ │ └── auth/ # Authentication endpoints
121
- │ ├── collections/ # Auth collection config
122
- └── middleware.ts # Auth middleware
123
- ├── public/ # Static assets
124
- └── astro.config.mjs # Astro configuration
188
+ │ ├── lib/
189
+ ├── db/ # Database adapters
190
+ │ │ │ ├── adapter.ts # DatabaseAdapter interface
191
+ │ │ │ ├── index.ts # Factory & config
192
+ │ │ ├── sqlite-adapter.ts
193
+ │ │ ├── postgres-adapter.ts
194
+ │ │ │ ├── mysql-adapter.ts
195
+ │ │ │ ├── mongodb-adapter.ts
196
+ │ │ │ └── mongodb-auth-adapter.ts
197
+ │ │ ├── auth/ # Auth adapter
198
+ │ │ │ └── sqlite-adapter.ts
199
+ │ │ ├── dataStore.ts # Content data wrapper
200
+ │ │ └── config.ts # Collection/global config
201
+ │ ├── components/ # React UI components
202
+ │ ├── pages/ # Astro pages + API routes
203
+ │ │ └── api/ # REST API endpoints
204
+ │ │ └── auth/ # Authentication endpoints
205
+ │ └── collections/ # Auth collection config
206
+ ├── test/ # Vitest tests
207
+ ├── public/ # Static assets
208
+ └── astro.config.mjs # Astro configuration
209
+ ```
210
+
211
+ ## Adapter Architecture
212
+
213
+ ```
214
+ ┌─────────────────────────────────────────────────────────────┐
215
+ │ Application Code │
216
+ └─────────────────────────┬───────────────────────────────────┘
217
+
218
+
219
+ ┌─────────────────────────────────────────────────────────────┐
220
+ │ dataStore.ts (Wrapper) │
221
+ └─────────────────────────┬───────────────────────────────────┘
222
+
223
+
224
+ ┌─────────────────────────────────────────────────────────────┐
225
+ │ db/index.ts (Factory/Registry) │
226
+ │ - Reads DB_TYPE from env │
227
+ │ - Lazy-loads appropriate adapter │
228
+ │ - Auto-selects matching auth adapter │
229
+ └─────────────────────────┬───────────────────────────────────┘
230
+
231
+
232
+ ┌─────────────────────────────────────────────────────────────┐
233
+ │ adapter.ts (DatabaseAdapter Interface) │
234
+ │ - find, findById, create, update, delete │
235
+ │ - findGlobal, updateGlobal, seedGlobal │
236
+ │ - seed, isSeeded, count │
237
+ └─────────────────────────┬───────────────────────────────────┘
238
+
239
+ ┌─────────────────────┼─────────────────────┐
240
+ ▼ ▼ ▼
241
+ ┌─────────┐ ┌─────────┐ ┌─────────┐
242
+ │ SQLite │ │ PG │ │ MySQL │
243
+ └─────────┘ └─────────┘ └─────────┘
244
+ ┌─────────┐
245
+ │ MongoDB │
246
+ └─────────┘
125
247
  ```
126
248
 
127
249
  ## Security
128
250
 
129
- - **Password Hashing** — bcryptjs with 12 salt rounds
251
+ - **Password Hashing** — bcryptjs with 10 salt rounds
130
252
  - **JWT Tokens** — Signed tokens with configurable expiration
131
- - **Session Management** — Server-side session tracking via SQLite
253
+ - **Session Management** — Server-side session tracking via DB adapter
132
254
  - **Middleware Protection** — All non-auth routes require valid JWT
133
- - **Password Policy** — Minimum 12 characters with complexity requirements
255
+ - **Password Policy** — Minimum 8 characters
134
256
 
135
- ## Scalability
136
-
137
- ### Default Setup (Single Instance)
138
-
139
- SQLite auth adapter handles everything in a single `./data/auth.db` file. Perfect for:
140
-
141
- - Development
142
- - Small to medium projects
143
- - Single-server deployments
144
-
145
- ### Multi-Instance / Horizontal Scaling
146
-
147
- When running multiple Kyro CMS instances behind a load balancer, configure:
257
+ ## Testing
148
258
 
149
259
  ```bash
150
- # Shared auth database path (mounted volume, NFS, etc.)
151
- KYRO_AUTH_DB_PATH=/shared/data/auth.db
260
+ # Run all tests
261
+ npm run test:run
152
262
 
153
- # Enable write-ahead logging for concurrent access
154
- # (automatically enabled by SQLiteAuthAdapter)
263
+ # Run tests with watch mode
264
+ npm run test
155
265
  ```
156
266
 
157
- ### High-Scale Production
158
-
159
- For high-traffic deployments with many concurrent users:
160
-
161
- 1. **Connection Pooling** — SQLite handles concurrent reads well, but writes are serialized. For write-heavy workloads, consider PostgreSQL with the Drizzle auth adapter.
162
-
163
- 2. **Session Caching** — JWT tokens are self-contained, so session validation doesn't require database reads on every request.
164
-
165
- 3. **Rate Limiting** — Currently in-memory per instance. For distributed rate limiting, use Redis or a shared SQLite file on fast storage.
166
-
167
- 4. **Audit Logs** — Stored in SQLite. For high-volume audit logging, consider exporting to a dedicated log store.
168
-
169
267
  ## License
170
268
 
171
269
  MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kyro-cms/admin",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Admin dashboard for Kyro CMS",
@@ -20,23 +20,70 @@
20
20
  "dev": "astro dev",
21
21
  "build": "astro build",
22
22
  "preview": "astro preview",
23
- "check": "astro check"
23
+ "check": "astro check",
24
+ "test": "vitest",
25
+ "test:run": "vitest run"
24
26
  },
25
27
  "dependencies": {
26
28
  "@astrojs/node": "^9.5.5",
27
29
  "@astrojs/react": "^4.2.0",
28
- "@kyro-cms/core": "^0.1.2",
30
+ "@codemirror/lang-cpp": "^6.0.3",
31
+ "@codemirror/lang-css": "^6.3.1",
32
+ "@codemirror/lang-html": "^6.4.11",
33
+ "@codemirror/lang-java": "^6.0.2",
34
+ "@codemirror/lang-javascript": "^6.2.5",
35
+ "@codemirror/lang-json": "^6.0.2",
36
+ "@codemirror/lang-markdown": "^6.5.0",
37
+ "@codemirror/lang-php": "^6.0.2",
38
+ "@codemirror/lang-python": "^6.2.1",
39
+ "@codemirror/lang-rust": "^6.0.2",
40
+ "@codemirror/lang-sql": "^6.10.0",
41
+ "@codemirror/state": "^6.6.0",
42
+ "@codemirror/view": "^6.41.0",
43
+ "@dnd-kit/core": "^6.3.1",
44
+ "@dnd-kit/sortable": "^10.0.0",
45
+ "@dnd-kit/utilities": "^3.2.2",
46
+ "@graphiql/react": "^0.37.3",
47
+ "@kyro-cms/core": "^0.1.7",
48
+ "@platejs/dnd": "^53.0.0",
49
+ "@portabletext/editor": "^6.6.3",
50
+ "@portabletext/react": "^6.0.3",
51
+ "@portabletext/schema": "^2.1.1",
52
+ "@portabletext/types": "^4.0.2",
29
53
  "@tailwindcss/vite": "^4.0.0",
54
+ "@types/pg": "^8.20.0",
55
+ "@udecode/plate-dnd": "^49.0.0",
56
+ "@uiw/codemirror-theme-aura": "^4.25.9",
57
+ "@uiw/codemirror-theme-dracula": "^4.25.9",
58
+ "@uiw/codemirror-theme-github": "^4.25.9",
59
+ "@uiw/react-codemirror": "^4.25.9",
30
60
  "astro": "^5.4.0",
61
+ "better-sqlite3": "^11.10.0",
62
+ "drizzle-orm": "^0.45.2",
63
+ "graphiql": "^5.2.2",
31
64
  "lucide-react": "^0.475.0",
65
+ "mongodb": "^7.1.1",
66
+ "mysql2": "^3.21.0",
67
+ "pg": "^8.20.0",
68
+ "platejs": "^53.0.0",
32
69
  "react": "^19.0.0",
33
70
  "react-dom": "^19.0.0",
34
- "tailwindcss": "^4.0.0"
71
+ "react-image-crop": "^11.0.10",
72
+ "sharp": "^0.34.5",
73
+ "slate": "^0.124.1",
74
+ "slate-history": "^0.113.1",
75
+ "slate-react": "^0.124.0",
76
+ "tailwindcss": "^4.0.0",
77
+ "zustand": "^5.0.3"
35
78
  },
36
79
  "devDependencies": {
37
80
  "@types/react": "^19.0.0",
38
81
  "@types/react-dom": "^19.0.0",
39
- "typescript": "^5.7.0"
82
+ "dotenv": "^17.4.2",
83
+ "dotenv-cli": "^11.0.0",
84
+ "drizzle-kit": "^0.31.10",
85
+ "typescript": "^5.7.0",
86
+ "vitest": "^4.1.4"
40
87
  },
41
88
  "peerDependencies": {
42
89
  "@kyro-cms/core": "^0.1.2"
@@ -73,7 +73,7 @@ export const rolesCollection: CollectionConfig = {
73
73
  items: { type: "text" },
74
74
  label: "Inherits From",
75
75
  },
76
- { name: "description", type: "textarea" },
76
+ { name: "description", type: "richtext" },
77
77
  {
78
78
  name: "permissions",
79
79
  type: "array",
@@ -124,7 +124,7 @@ export const auditLogsCollection: CollectionConfig = {
124
124
  { name: "ipAddress", type: "text", label: "IP Address" },
125
125
  { name: "userAgent", type: "text", label: "User Agent" },
126
126
  { name: "success", type: "boolean" },
127
- { name: "error", type: "textarea" },
127
+ { name: "error", type: "richtext" },
128
128
  { name: "metadata", type: "json", label: "Metadata" },
129
129
  { name: "timestamp", type: "datetime", required: true, readonly: true },
130
130
  ],
@@ -0,0 +1,343 @@
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
+ };