@nextsparkjs/theme-blog 0.1.0-beta.1
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.
- package/README.md +65 -0
- package/about.md +93 -0
- package/api/authors/[username]/route.ts +150 -0
- package/api/authors/route.ts +63 -0
- package/api/posts/public/route.ts +151 -0
- package/components/ExportPostsButton.tsx +102 -0
- package/components/ImportPostsDialog.tsx +284 -0
- package/components/PostsToolbar.tsx +24 -0
- package/components/editor/FeaturedImageUpload.tsx +185 -0
- package/components/editor/WysiwygEditor.tsx +340 -0
- package/components/index.ts +4 -0
- package/components/public/AuthorBio.tsx +105 -0
- package/components/public/AuthorCard.tsx +130 -0
- package/components/public/BlogFooter.tsx +185 -0
- package/components/public/BlogNavbar.tsx +201 -0
- package/components/public/PostCard.tsx +306 -0
- package/components/public/ReadingProgress.tsx +70 -0
- package/components/public/RelatedPosts.tsx +78 -0
- package/config/app.config.ts +200 -0
- package/config/billing.config.ts +146 -0
- package/config/dashboard.config.ts +333 -0
- package/config/dev.config.ts +48 -0
- package/config/features.config.ts +196 -0
- package/config/flows.config.ts +333 -0
- package/config/permissions.config.ts +101 -0
- package/config/theme.config.ts +128 -0
- package/entities/categories/categories.config.ts +60 -0
- package/entities/categories/categories.fields.ts +115 -0
- package/entities/categories/categories.service.ts +333 -0
- package/entities/categories/categories.types.ts +58 -0
- package/entities/categories/messages/en.json +33 -0
- package/entities/categories/messages/es.json +33 -0
- package/entities/posts/messages/en.json +100 -0
- package/entities/posts/messages/es.json +100 -0
- package/entities/posts/migrations/001_posts_table.sql +110 -0
- package/entities/posts/migrations/002_add_featured.sql +19 -0
- package/entities/posts/migrations/003_post_categories_pivot.sql +47 -0
- package/entities/posts/posts.config.ts +61 -0
- package/entities/posts/posts.fields.ts +234 -0
- package/entities/posts/posts.service.ts +464 -0
- package/entities/posts/posts.types.ts +80 -0
- package/lib/selectors.ts +179 -0
- package/messages/en.json +113 -0
- package/messages/es.json +113 -0
- package/migrations/002_author_profile_fields.sql +37 -0
- package/migrations/003_categories_table.sql +90 -0
- package/migrations/999_sample_data.sql +412 -0
- package/migrations/999_theme_sample_data.sql +1070 -0
- package/package.json +18 -0
- package/permissions-matrix.md +63 -0
- package/styles/article.css +333 -0
- package/styles/components.css +204 -0
- package/styles/globals.css +327 -0
- package/styles/theme.css +167 -0
- package/templates/(public)/author/[username]/page.tsx +247 -0
- package/templates/(public)/authors/page.tsx +161 -0
- package/templates/(public)/layout.tsx +44 -0
- package/templates/(public)/page.tsx +276 -0
- package/templates/(public)/posts/[slug]/page.tsx +342 -0
- package/templates/dashboard/(main)/page.tsx +385 -0
- package/templates/dashboard/(main)/posts/[id]/edit/page.tsx +529 -0
- package/templates/dashboard/(main)/posts/[id]/page.tsx +33 -0
- package/templates/dashboard/(main)/posts/create/page.tsx +353 -0
- package/templates/dashboard/(main)/posts/page.tsx +833 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blog Theme - Flows Configuration
|
|
3
|
+
*
|
|
4
|
+
* Defines user journeys/flows that span multiple features.
|
|
5
|
+
* Each flow key becomes a tag: @flow-{key}
|
|
6
|
+
*
|
|
7
|
+
* Flows are enriched at build-time with:
|
|
8
|
+
* - Feature metadata (from features.config.ts)
|
|
9
|
+
* - Test coverage (from tags-registry + test files)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { defineFlows } from '@nextsparkjs/core/lib/config/features-types'
|
|
13
|
+
|
|
14
|
+
export default defineFlows({
|
|
15
|
+
// ===========================================================================
|
|
16
|
+
// ACQUISITION FLOWS
|
|
17
|
+
// User acquisition and onboarding journeys
|
|
18
|
+
// ===========================================================================
|
|
19
|
+
|
|
20
|
+
onboarding: {
|
|
21
|
+
name: 'Author Onboarding',
|
|
22
|
+
description: 'Complete journey from signup to first published post',
|
|
23
|
+
category: 'acquisition',
|
|
24
|
+
icon: 'rocket',
|
|
25
|
+
criticalPath: true,
|
|
26
|
+
|
|
27
|
+
steps: [
|
|
28
|
+
{
|
|
29
|
+
feature: 'auth',
|
|
30
|
+
action: 'signup',
|
|
31
|
+
description: 'Author creates account with email/password',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
feature: 'auth',
|
|
35
|
+
action: 'verify-email',
|
|
36
|
+
description: 'Author verifies email address',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
feature: 'users',
|
|
40
|
+
action: 'setup-profile',
|
|
41
|
+
description: 'Author sets up their profile (bio, avatar)',
|
|
42
|
+
optional: true,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
feature: 'posts',
|
|
46
|
+
action: 'create-first-post',
|
|
47
|
+
description: 'Author creates their first blog post',
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
|
|
51
|
+
features: ['auth', 'users', 'posts'],
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
// ===========================================================================
|
|
55
|
+
// CONTENT CREATION FLOWS
|
|
56
|
+
// Post creation and publishing journeys
|
|
57
|
+
// ===========================================================================
|
|
58
|
+
|
|
59
|
+
'create-post': {
|
|
60
|
+
name: 'Create Post',
|
|
61
|
+
description: 'Complete journey to create and publish a blog post',
|
|
62
|
+
category: 'content',
|
|
63
|
+
icon: 'edit',
|
|
64
|
+
criticalPath: true,
|
|
65
|
+
|
|
66
|
+
steps: [
|
|
67
|
+
{
|
|
68
|
+
feature: 'posts',
|
|
69
|
+
action: 'create-draft',
|
|
70
|
+
description: 'Author creates a new draft post',
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
feature: 'post-editor',
|
|
74
|
+
action: 'write-content',
|
|
75
|
+
description: 'Author writes post content using the editor',
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
feature: 'post-editor',
|
|
79
|
+
action: 'add-featured-image',
|
|
80
|
+
description: 'Author adds featured image',
|
|
81
|
+
optional: true,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
feature: 'categories',
|
|
85
|
+
action: 'assign-category',
|
|
86
|
+
description: 'Author assigns categories to the post',
|
|
87
|
+
optional: true,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
feature: 'publishing',
|
|
91
|
+
action: 'publish',
|
|
92
|
+
description: 'Author publishes the post',
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
|
|
96
|
+
features: ['posts', 'post-editor', 'categories', 'publishing'],
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
'edit-post': {
|
|
100
|
+
name: 'Edit Post',
|
|
101
|
+
description: 'Edit and update an existing post',
|
|
102
|
+
category: 'content',
|
|
103
|
+
icon: 'pencil',
|
|
104
|
+
criticalPath: false,
|
|
105
|
+
|
|
106
|
+
steps: [
|
|
107
|
+
{
|
|
108
|
+
feature: 'posts',
|
|
109
|
+
action: 'select-post',
|
|
110
|
+
description: 'Author selects a post to edit',
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
feature: 'post-editor',
|
|
114
|
+
action: 'modify-content',
|
|
115
|
+
description: 'Author modifies post content',
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
feature: 'posts',
|
|
119
|
+
action: 'save-changes',
|
|
120
|
+
description: 'Author saves the updated post',
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
|
|
124
|
+
features: ['posts', 'post-editor'],
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
// ===========================================================================
|
|
128
|
+
// PUBLISHING FLOWS
|
|
129
|
+
// Content publishing workflows
|
|
130
|
+
// ===========================================================================
|
|
131
|
+
|
|
132
|
+
'publish-workflow': {
|
|
133
|
+
name: 'Publishing Workflow',
|
|
134
|
+
description: 'Manage post status from draft to published',
|
|
135
|
+
category: 'content',
|
|
136
|
+
icon: 'send',
|
|
137
|
+
criticalPath: true,
|
|
138
|
+
|
|
139
|
+
steps: [
|
|
140
|
+
{
|
|
141
|
+
feature: 'posts',
|
|
142
|
+
action: 'review-draft',
|
|
143
|
+
description: 'Author reviews draft post',
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
feature: 'publishing',
|
|
147
|
+
action: 'set-publish-date',
|
|
148
|
+
description: 'Author sets publish date (optional)',
|
|
149
|
+
optional: true,
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
feature: 'publishing',
|
|
153
|
+
action: 'publish',
|
|
154
|
+
description: 'Author publishes the post',
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
|
|
158
|
+
features: ['posts', 'publishing'],
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
unpublish: {
|
|
162
|
+
name: 'Unpublish Post',
|
|
163
|
+
description: 'Take a published post offline',
|
|
164
|
+
category: 'content',
|
|
165
|
+
icon: 'eye-off',
|
|
166
|
+
criticalPath: false,
|
|
167
|
+
|
|
168
|
+
steps: [
|
|
169
|
+
{
|
|
170
|
+
feature: 'posts',
|
|
171
|
+
action: 'select-published-post',
|
|
172
|
+
description: 'Author selects a published post',
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
feature: 'publishing',
|
|
176
|
+
action: 'unpublish',
|
|
177
|
+
description: 'Author unpublishes the post (reverts to draft)',
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
|
|
181
|
+
features: ['posts', 'publishing'],
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
// ===========================================================================
|
|
185
|
+
// DATA MANAGEMENT FLOWS
|
|
186
|
+
// Import/export workflows
|
|
187
|
+
// ===========================================================================
|
|
188
|
+
|
|
189
|
+
'export-content': {
|
|
190
|
+
name: 'Export Content',
|
|
191
|
+
description: 'Export blog content for backup or migration',
|
|
192
|
+
category: 'settings',
|
|
193
|
+
icon: 'download',
|
|
194
|
+
criticalPath: false,
|
|
195
|
+
|
|
196
|
+
steps: [
|
|
197
|
+
{
|
|
198
|
+
feature: 'post-export',
|
|
199
|
+
action: 'select-content',
|
|
200
|
+
description: 'Author selects content to export',
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
feature: 'post-export',
|
|
204
|
+
action: 'export',
|
|
205
|
+
description: 'Author downloads the exported JSON file',
|
|
206
|
+
},
|
|
207
|
+
],
|
|
208
|
+
|
|
209
|
+
features: ['post-export'],
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
'import-content': {
|
|
213
|
+
name: 'Import Content',
|
|
214
|
+
description: 'Import blog content from backup file',
|
|
215
|
+
category: 'settings',
|
|
216
|
+
icon: 'upload',
|
|
217
|
+
criticalPath: false,
|
|
218
|
+
|
|
219
|
+
steps: [
|
|
220
|
+
{
|
|
221
|
+
feature: 'post-import',
|
|
222
|
+
action: 'select-file',
|
|
223
|
+
description: 'Author selects JSON file to import',
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
feature: 'post-import',
|
|
227
|
+
action: 'review-content',
|
|
228
|
+
description: 'Author reviews content to be imported',
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
feature: 'post-import',
|
|
232
|
+
action: 'confirm-import',
|
|
233
|
+
description: 'Author confirms and executes import',
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
|
|
237
|
+
features: ['post-import'],
|
|
238
|
+
},
|
|
239
|
+
|
|
240
|
+
// ===========================================================================
|
|
241
|
+
// PUBLIC READING FLOWS
|
|
242
|
+
// Reader-facing journeys
|
|
243
|
+
// ===========================================================================
|
|
244
|
+
|
|
245
|
+
'read-post': {
|
|
246
|
+
name: 'Read Post',
|
|
247
|
+
description: 'Public reader views a blog post',
|
|
248
|
+
category: 'public',
|
|
249
|
+
icon: 'book-open',
|
|
250
|
+
criticalPath: true,
|
|
251
|
+
|
|
252
|
+
steps: [
|
|
253
|
+
{
|
|
254
|
+
feature: 'public-blog',
|
|
255
|
+
action: 'browse-posts',
|
|
256
|
+
description: 'Reader browses blog homepage or category',
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
feature: 'public-blog',
|
|
260
|
+
action: 'select-post',
|
|
261
|
+
description: 'Reader clicks on a post to read',
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
feature: 'public-blog',
|
|
265
|
+
action: 'read-content',
|
|
266
|
+
description: 'Reader reads the full post content',
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
|
|
270
|
+
features: ['public-blog'],
|
|
271
|
+
},
|
|
272
|
+
|
|
273
|
+
'view-author': {
|
|
274
|
+
name: 'View Author',
|
|
275
|
+
description: 'Reader views an author profile and their posts',
|
|
276
|
+
category: 'public',
|
|
277
|
+
icon: 'user',
|
|
278
|
+
criticalPath: false,
|
|
279
|
+
|
|
280
|
+
steps: [
|
|
281
|
+
{
|
|
282
|
+
feature: 'authors',
|
|
283
|
+
action: 'view-profile',
|
|
284
|
+
description: 'Reader views author profile page',
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
feature: 'authors',
|
|
288
|
+
action: 'browse-author-posts',
|
|
289
|
+
description: 'Reader browses posts by this author',
|
|
290
|
+
},
|
|
291
|
+
],
|
|
292
|
+
|
|
293
|
+
features: ['authors', 'public-blog'],
|
|
294
|
+
},
|
|
295
|
+
|
|
296
|
+
// ===========================================================================
|
|
297
|
+
// BILLING FLOWS
|
|
298
|
+
// Subscription and payment journeys
|
|
299
|
+
// ===========================================================================
|
|
300
|
+
|
|
301
|
+
'upgrade-plan': {
|
|
302
|
+
name: 'Upgrade Plan',
|
|
303
|
+
description: 'Upgrade subscription to a higher tier plan',
|
|
304
|
+
category: 'settings',
|
|
305
|
+
icon: 'trending-up',
|
|
306
|
+
criticalPath: false,
|
|
307
|
+
|
|
308
|
+
steps: [
|
|
309
|
+
{
|
|
310
|
+
feature: 'plans',
|
|
311
|
+
action: 'view-plans',
|
|
312
|
+
description: 'Author views available plans',
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
feature: 'plans',
|
|
316
|
+
action: 'select-plan',
|
|
317
|
+
description: 'Author selects a new plan',
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
feature: 'billing',
|
|
321
|
+
action: 'enter-payment',
|
|
322
|
+
description: 'Author enters payment information',
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
feature: 'billing',
|
|
326
|
+
action: 'confirm-upgrade',
|
|
327
|
+
description: 'Author confirms the upgrade',
|
|
328
|
+
},
|
|
329
|
+
],
|
|
330
|
+
|
|
331
|
+
features: ['plans', 'billing'],
|
|
332
|
+
},
|
|
333
|
+
})
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blog Theme - Permissions Configuration
|
|
3
|
+
*
|
|
4
|
+
* SINGLE SOURCE OF TRUTH for all permissions and roles in this theme.
|
|
5
|
+
*
|
|
6
|
+
* This file defines:
|
|
7
|
+
* - entities: Entity CRUD permissions (posts, categories)
|
|
8
|
+
* - features: Theme-specific feature permissions (export, import)
|
|
9
|
+
*
|
|
10
|
+
* All sections use unified format: { action: '...', roles: [...] }
|
|
11
|
+
*
|
|
12
|
+
* Single-user blog permissions:
|
|
13
|
+
* - Owner has all permissions
|
|
14
|
+
* - Team collaboration features are disabled
|
|
15
|
+
* - Export/Import features enabled
|
|
16
|
+
*
|
|
17
|
+
* Use PermissionService.canDoAction(role, action) to check any permission.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type { ThemePermissionsConfig } from '@nextsparkjs/core/lib/permissions/types'
|
|
21
|
+
|
|
22
|
+
export const PERMISSIONS_CONFIG_OVERRIDES: ThemePermissionsConfig = {
|
|
23
|
+
// ==========================================
|
|
24
|
+
// ENTITY PERMISSIONS
|
|
25
|
+
// ==========================================
|
|
26
|
+
// Single-user mode: only owner role
|
|
27
|
+
entities: {
|
|
28
|
+
// ------------------------------------------
|
|
29
|
+
// POSTS
|
|
30
|
+
// ------------------------------------------
|
|
31
|
+
posts: [
|
|
32
|
+
{ action: 'create', label: 'Create posts', description: 'Can create new blog posts', roles: ['owner'] },
|
|
33
|
+
{ action: 'read', label: 'View posts', description: 'Can view post details', roles: ['owner'] },
|
|
34
|
+
{ action: 'list', label: 'List posts', description: 'Can see the posts list', roles: ['owner'] },
|
|
35
|
+
{ action: 'update', label: 'Edit posts', description: 'Can modify post content', roles: ['owner'] },
|
|
36
|
+
{ action: 'delete', label: 'Delete posts', description: 'Can delete posts', roles: ['owner'], dangerous: true },
|
|
37
|
+
{ action: 'publish', label: 'Publish posts', description: 'Can publish posts to make them public', roles: ['owner'] },
|
|
38
|
+
{ action: 'unpublish', label: 'Unpublish posts', description: 'Can unpublish posts to hide them', roles: ['owner'] },
|
|
39
|
+
],
|
|
40
|
+
|
|
41
|
+
// ------------------------------------------
|
|
42
|
+
// CATEGORIES
|
|
43
|
+
// ------------------------------------------
|
|
44
|
+
categories: [
|
|
45
|
+
{ action: 'create', label: 'Create categories', description: 'Can create new categories', roles: ['owner'] },
|
|
46
|
+
{ action: 'read', label: 'View categories', description: 'Can view category details', roles: ['owner'] },
|
|
47
|
+
{ action: 'list', label: 'List categories', description: 'Can see the categories list', roles: ['owner'] },
|
|
48
|
+
{ action: 'update', label: 'Edit categories', description: 'Can modify category information', roles: ['owner'] },
|
|
49
|
+
{ action: 'delete', label: 'Delete categories', description: 'Can delete categories', roles: ['owner'], dangerous: true },
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
// ==========================================
|
|
54
|
+
// FEATURE PERMISSIONS
|
|
55
|
+
// ==========================================
|
|
56
|
+
// Unified format: uses 'action' instead of 'id'
|
|
57
|
+
features: [
|
|
58
|
+
{
|
|
59
|
+
action: 'blog.export',
|
|
60
|
+
label: 'Export posts',
|
|
61
|
+
description: 'Export all posts to JSON format for backup or migration',
|
|
62
|
+
category: 'Blog',
|
|
63
|
+
roles: ['owner'],
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
action: 'blog.import',
|
|
67
|
+
label: 'Import posts',
|
|
68
|
+
description: 'Import posts from JSON file',
|
|
69
|
+
category: 'Blog',
|
|
70
|
+
roles: ['owner'],
|
|
71
|
+
dangerous: true,
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
|
|
75
|
+
// ==========================================
|
|
76
|
+
// DISABLED FEATURES
|
|
77
|
+
// ==========================================
|
|
78
|
+
// These don't apply in single-user mode
|
|
79
|
+
disabled: [
|
|
80
|
+
'teams.invite',
|
|
81
|
+
'teams.remove_member',
|
|
82
|
+
'teams.change_roles',
|
|
83
|
+
'teams.delete',
|
|
84
|
+
'settings.api_keys',
|
|
85
|
+
'settings.billing',
|
|
86
|
+
],
|
|
87
|
+
|
|
88
|
+
// ==========================================
|
|
89
|
+
// UI SECTIONS
|
|
90
|
+
// ==========================================
|
|
91
|
+
uiSections: [
|
|
92
|
+
{
|
|
93
|
+
id: 'blog-features',
|
|
94
|
+
label: 'Blog Features',
|
|
95
|
+
description: 'Permissions for blog-specific functionality',
|
|
96
|
+
categories: ['Blog'],
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export default PERMISSIONS_CONFIG_OVERRIDES
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blog Theme Configuration
|
|
3
|
+
*
|
|
4
|
+
* A simple personal blog theme with single-user mode.
|
|
5
|
+
* No collaboration, just a writer and their content.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ThemeConfig } from '@nextsparkjs/core/types/theme'
|
|
9
|
+
|
|
10
|
+
export const blogThemeConfig: ThemeConfig = {
|
|
11
|
+
name: 'blog',
|
|
12
|
+
displayName: 'Personal Blog',
|
|
13
|
+
version: '2.0.0',
|
|
14
|
+
description: 'A multi-author blog platform with single-user mode',
|
|
15
|
+
author: 'NextSpark Team',
|
|
16
|
+
|
|
17
|
+
// Teams configuration for multi-author platform with single-user mode
|
|
18
|
+
teams: {
|
|
19
|
+
mode: 'single-user',
|
|
20
|
+
options: {
|
|
21
|
+
maxTeamsPerUser: 1,
|
|
22
|
+
maxMembersPerTeam: 1,
|
|
23
|
+
allowLeaveAllTeams: false
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
plugins: [],
|
|
28
|
+
|
|
29
|
+
// Styles configuration
|
|
30
|
+
styles: {
|
|
31
|
+
globals: 'globals.css',
|
|
32
|
+
components: 'components.css',
|
|
33
|
+
variables: {
|
|
34
|
+
'--spacing-xs': '0.125rem',
|
|
35
|
+
'--spacing-sm': '0.25rem',
|
|
36
|
+
'--spacing-md': '0.5rem',
|
|
37
|
+
'--spacing-lg': '1rem',
|
|
38
|
+
'--spacing-xl': '1.5rem',
|
|
39
|
+
'--spacing-2xl': '2rem'
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
// Google Fonts for editorial typography
|
|
44
|
+
fonts: [
|
|
45
|
+
{ family: 'Oxanium', weights: ['400', '500', '600', '700'] },
|
|
46
|
+
{ family: 'Merriweather', weights: ['400', '700'], styles: ['normal', 'italic'] },
|
|
47
|
+
{ family: 'Fira Code', weights: ['400', '500'] }
|
|
48
|
+
],
|
|
49
|
+
|
|
50
|
+
// Theme configuration - warm amber/orange editorial aesthetic
|
|
51
|
+
config: {
|
|
52
|
+
colors: {
|
|
53
|
+
// Warm cream background
|
|
54
|
+
background: 'oklch(0.9885 0.0057 84.5659)',
|
|
55
|
+
foreground: 'oklch(0.3660 0.0251 49.6085)',
|
|
56
|
+
card: 'oklch(0.9686 0.0091 78.2818)',
|
|
57
|
+
'card-foreground': 'oklch(0.3660 0.0251 49.6085)',
|
|
58
|
+
popover: 'oklch(0.9686 0.0091 78.2818)',
|
|
59
|
+
'popover-foreground': 'oklch(0.3660 0.0251 49.6085)',
|
|
60
|
+
// Warm amber primary
|
|
61
|
+
primary: 'oklch(0.5553 0.1455 48.9975)',
|
|
62
|
+
'primary-foreground': 'oklch(1.0000 0 0)',
|
|
63
|
+
secondary: 'oklch(0.8276 0.0752 74.4400)',
|
|
64
|
+
'secondary-foreground': 'oklch(0.4444 0.0096 73.6390)',
|
|
65
|
+
muted: 'oklch(0.9363 0.0218 83.2637)',
|
|
66
|
+
'muted-foreground': 'oklch(0.5534 0.0116 58.0708)',
|
|
67
|
+
accent: 'oklch(0.9000 0.0500 74.9889)',
|
|
68
|
+
'accent-foreground': 'oklch(0.4444 0.0096 73.6390)',
|
|
69
|
+
destructive: 'oklch(0.4437 0.1613 26.8994)',
|
|
70
|
+
'destructive-foreground': 'oklch(1.0000 0 0)',
|
|
71
|
+
border: 'oklch(0.8866 0.0404 89.6994)',
|
|
72
|
+
input: 'oklch(0.8866 0.0404 89.6994)',
|
|
73
|
+
ring: 'oklch(0.5553 0.1455 48.9975)',
|
|
74
|
+
|
|
75
|
+
// Chart colors
|
|
76
|
+
'chart-1': 'oklch(0.5553 0.1455 48.9975)',
|
|
77
|
+
'chart-2': 'oklch(0.5534 0.0116 58.0708)',
|
|
78
|
+
'chart-3': 'oklch(0.5538 0.1207 66.4416)',
|
|
79
|
+
'chart-4': 'oklch(0.5534 0.0116 58.0708)',
|
|
80
|
+
'chart-5': 'oklch(0.6806 0.1423 75.8340)',
|
|
81
|
+
|
|
82
|
+
// Sidebar
|
|
83
|
+
sidebar: 'oklch(0.9363 0.0218 83.2637)',
|
|
84
|
+
'sidebar-foreground': 'oklch(0.3660 0.0251 49.6085)',
|
|
85
|
+
'sidebar-primary': 'oklch(0.5553 0.1455 48.9975)',
|
|
86
|
+
'sidebar-primary-foreground': 'oklch(1.0000 0 0)',
|
|
87
|
+
'sidebar-accent': 'oklch(0.5538 0.1207 66.4416)',
|
|
88
|
+
'sidebar-accent-foreground': 'oklch(1.0000 0 0)',
|
|
89
|
+
'sidebar-border': 'oklch(0.8866 0.0404 89.6994)',
|
|
90
|
+
'sidebar-ring': 'oklch(0.5553 0.1455 48.9975)'
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
// Typography - Oxanium for UI, Merriweather for content
|
|
94
|
+
fonts: {
|
|
95
|
+
sans: 'Oxanium, sans-serif',
|
|
96
|
+
serif: 'Merriweather, serif',
|
|
97
|
+
mono: 'Fira Code, monospace'
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
// Small radius for clean look
|
|
101
|
+
spacing: {
|
|
102
|
+
radius: '0.3rem',
|
|
103
|
+
'radius-sm': 'calc(0.3rem - 4px)',
|
|
104
|
+
'radius-md': 'calc(0.3rem - 2px)',
|
|
105
|
+
'radius-lg': '0.3rem',
|
|
106
|
+
'radius-xl': 'calc(0.3rem + 4px)'
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
// Warm shadows
|
|
110
|
+
breakpoints: {
|
|
111
|
+
'shadow-2xs': '0px 2px 3px 0px hsl(28 18% 25% / 0.09)',
|
|
112
|
+
'shadow-xs': '0px 2px 3px 0px hsl(28 18% 25% / 0.09)',
|
|
113
|
+
'shadow-sm': '0px 2px 3px 0px hsl(28 18% 25% / 0.18), 0px 1px 2px -1px hsl(28 18% 25% / 0.18)',
|
|
114
|
+
shadow: '0px 2px 3px 0px hsl(28 18% 25% / 0.18), 0px 1px 2px -1px hsl(28 18% 25% / 0.18)',
|
|
115
|
+
'shadow-md': '0px 2px 3px 0px hsl(28 18% 25% / 0.18), 0px 2px 4px -1px hsl(28 18% 25% / 0.18)',
|
|
116
|
+
'shadow-lg': '0px 2px 3px 0px hsl(28 18% 25% / 0.18), 0px 4px 6px -1px hsl(28 18% 25% / 0.18)',
|
|
117
|
+
'shadow-xl': '0px 2px 3px 0px hsl(28 18% 25% / 0.18), 0px 8px 10px -1px hsl(28 18% 25% / 0.18)',
|
|
118
|
+
'shadow-2xl': '0px 2px 3px 0px hsl(28 18% 25% / 0.45)'
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
components: {
|
|
123
|
+
overrides: {},
|
|
124
|
+
custom: {}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export default blogThemeConfig
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Categories Entity Configuration
|
|
3
|
+
*
|
|
4
|
+
* Categories for blog posts in the multi-author platform.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Tag } from 'lucide-react'
|
|
8
|
+
import type { EntityConfig } from '@nextsparkjs/core/lib/entities/types'
|
|
9
|
+
import { categoryFields } from './categories.fields'
|
|
10
|
+
|
|
11
|
+
export const categoryEntityConfig: EntityConfig = {
|
|
12
|
+
// ==========================================
|
|
13
|
+
// 1. BASIC IDENTIFICATION
|
|
14
|
+
// ==========================================
|
|
15
|
+
slug: 'categories',
|
|
16
|
+
enabled: true,
|
|
17
|
+
names: {
|
|
18
|
+
singular: 'category',
|
|
19
|
+
plural: 'Categories'
|
|
20
|
+
},
|
|
21
|
+
icon: Tag,
|
|
22
|
+
|
|
23
|
+
// ==========================================
|
|
24
|
+
// 2. ACCESS AND SCOPE CONFIGURATION
|
|
25
|
+
// ==========================================
|
|
26
|
+
access: {
|
|
27
|
+
public: true, // Categories are publicly accessible
|
|
28
|
+
api: true, // API access enabled
|
|
29
|
+
metadata: false, // No metadata needed
|
|
30
|
+
shared: false // User-owned categories (each user manages their own)
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
// ==========================================
|
|
34
|
+
// 3. UI/UX FEATURES
|
|
35
|
+
// ==========================================
|
|
36
|
+
ui: {
|
|
37
|
+
dashboard: {
|
|
38
|
+
showInMenu: true,
|
|
39
|
+
showInTopbar: false
|
|
40
|
+
},
|
|
41
|
+
public: {
|
|
42
|
+
hasArchivePage: false, // No dedicated archive page
|
|
43
|
+
hasSinglePage: true // /category/[slug] - posts by category
|
|
44
|
+
},
|
|
45
|
+
features: {
|
|
46
|
+
searchable: true,
|
|
47
|
+
sortable: true,
|
|
48
|
+
filterable: false,
|
|
49
|
+
bulkOperations: true,
|
|
50
|
+
importExport: false
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
// ==========================================
|
|
55
|
+
// FIELDS
|
|
56
|
+
// ==========================================
|
|
57
|
+
fields: categoryFields,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export default categoryEntityConfig
|