@nextsparkjs/theme-default 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/about/business.md +49 -0
- package/about/features.json +302 -0
- package/about/team.md +79 -0
- package/api/ai/chat/stream/route.ts +212 -0
- package/api/ai/orchestrator/route.ts +226 -0
- package/api/ai/single-agent/route.ts +291 -0
- package/api/ai/usage/route.ts +122 -0
- package/blocks/benefits/component.tsx +100 -0
- package/blocks/benefits/config.ts +11 -0
- package/blocks/benefits/examples.ts +85 -0
- package/blocks/benefits/fields.ts +156 -0
- package/blocks/benefits/schema.ts +33 -0
- package/blocks/cta-section/component.tsx +100 -0
- package/blocks/cta-section/config.ts +11 -0
- package/blocks/cta-section/examples.ts +41 -0
- package/blocks/cta-section/fields.ts +89 -0
- package/blocks/cta-section/index.ts +6 -0
- package/blocks/cta-section/schema.ts +32 -0
- package/blocks/cta-section/thumbnail.png +1 -0
- package/blocks/faq-accordion/component.tsx +156 -0
- package/blocks/faq-accordion/config.ts +11 -0
- package/blocks/faq-accordion/examples.ts +77 -0
- package/blocks/faq-accordion/fields.ts +119 -0
- package/blocks/faq-accordion/index.ts +6 -0
- package/blocks/faq-accordion/schema.ts +45 -0
- package/blocks/features-grid/component.tsx +112 -0
- package/blocks/features-grid/config.ts +11 -0
- package/blocks/features-grid/examples.ts +63 -0
- package/blocks/features-grid/fields.ts +97 -0
- package/blocks/features-grid/index.ts +6 -0
- package/blocks/features-grid/schema.ts +40 -0
- package/blocks/features-grid/thumbnail.png +1 -0
- package/blocks/hero/component.tsx +100 -0
- package/blocks/hero/config.ts +11 -0
- package/blocks/hero/examples.ts +35 -0
- package/blocks/hero/fields.ts +60 -0
- package/blocks/hero/index.ts +6 -0
- package/blocks/hero/schema.ts +32 -0
- package/blocks/hero/thumbnail.png +1 -0
- package/blocks/hero/thumbnail.png.txt +6 -0
- package/blocks/hero-with-form/component.tsx +232 -0
- package/blocks/hero-with-form/config.ts +11 -0
- package/blocks/hero-with-form/examples.ts +16 -0
- package/blocks/hero-with-form/fields.ts +207 -0
- package/blocks/hero-with-form/index.ts +6 -0
- package/blocks/hero-with-form/schema.ts +54 -0
- package/blocks/jumbotron/component.tsx +136 -0
- package/blocks/jumbotron/config.ts +11 -0
- package/blocks/jumbotron/examples.ts +36 -0
- package/blocks/jumbotron/fields.ts +202 -0
- package/blocks/jumbotron/index.ts +6 -0
- package/blocks/jumbotron/schema.ts +55 -0
- package/blocks/logo-cloud/component.tsx +154 -0
- package/blocks/logo-cloud/config.ts +11 -0
- package/blocks/logo-cloud/examples.ts +34 -0
- package/blocks/logo-cloud/fields.ts +133 -0
- package/blocks/logo-cloud/index.ts +6 -0
- package/blocks/logo-cloud/schema.ts +46 -0
- package/blocks/post-content/component.tsx +197 -0
- package/blocks/post-content/config.ts +11 -0
- package/blocks/post-content/examples.ts +33 -0
- package/blocks/post-content/fields.ts +165 -0
- package/blocks/post-content/index.ts +4 -0
- package/blocks/post-content/schema.ts +46 -0
- package/blocks/pricing-table/component.tsx +154 -0
- package/blocks/pricing-table/config.ts +11 -0
- package/blocks/pricing-table/examples.ts +96 -0
- package/blocks/pricing-table/fields.ts +161 -0
- package/blocks/pricing-table/index.ts +4 -0
- package/blocks/pricing-table/schema.ts +50 -0
- package/blocks/split-content/component.tsx +135 -0
- package/blocks/split-content/config.ts +11 -0
- package/blocks/split-content/examples.ts +38 -0
- package/blocks/split-content/fields.ts +198 -0
- package/blocks/split-content/index.ts +6 -0
- package/blocks/split-content/schema.ts +67 -0
- package/blocks/stats-counter/component.tsx +124 -0
- package/blocks/stats-counter/config.ts +11 -0
- package/blocks/stats-counter/examples.ts +61 -0
- package/blocks/stats-counter/fields.ts +134 -0
- package/blocks/stats-counter/index.ts +6 -0
- package/blocks/stats-counter/schema.ts +47 -0
- package/blocks/testimonials/component.tsx +114 -0
- package/blocks/testimonials/config.ts +11 -0
- package/blocks/testimonials/examples.ts +65 -0
- package/blocks/testimonials/fields.ts +105 -0
- package/blocks/testimonials/index.ts +6 -0
- package/blocks/testimonials/schema.ts +41 -0
- package/blocks/testimonials/thumbnail.png +1 -0
- package/blocks/text-content/component.tsx +97 -0
- package/blocks/text-content/config.ts +11 -0
- package/blocks/text-content/examples.ts +30 -0
- package/blocks/text-content/fields.ts +88 -0
- package/blocks/text-content/index.ts +6 -0
- package/blocks/text-content/schema.ts +30 -0
- package/blocks/text-content/thumbnail.png +1 -0
- package/blocks/timeline/component.tsx +267 -0
- package/blocks/timeline/config.ts +11 -0
- package/blocks/timeline/examples.ts +68 -0
- package/blocks/timeline/fields.ts +147 -0
- package/blocks/timeline/index.ts +6 -0
- package/blocks/timeline/schema.ts +49 -0
- package/blocks/video-hero/component.tsx +270 -0
- package/blocks/video-hero/config.ts +11 -0
- package/blocks/video-hero/examples.ts +24 -0
- package/blocks/video-hero/fields.ts +98 -0
- package/blocks/video-hero/index.ts +6 -0
- package/blocks/video-hero/schema.ts +39 -0
- package/components/ai-chat/ChatPanel.tsx +575 -0
- package/components/ai-chat/ConversationItem.tsx +266 -0
- package/components/ai-chat/ConversationSidebar.tsx +99 -0
- package/components/ai-chat/MarkdownRenderer.tsx +15 -0
- package/components/ai-chat/Message.tsx +42 -0
- package/components/ai-chat/MessageInput.tsx +49 -0
- package/components/ai-chat/MessageList.tsx +46 -0
- package/components/ai-chat/TypingIndicator.tsx +11 -0
- package/config/app.config.ts +367 -0
- package/config/billing.config.ts +349 -0
- package/config/dashboard.config.ts +506 -0
- package/config/dev.config.ts +104 -0
- package/config/features.config.ts +203 -0
- package/config/flows.config.ts +129 -0
- package/config/permissions.config.ts +245 -0
- package/config/theme.config.ts +74 -0
- package/docs/01-overview/01-introduction.md +335 -0
- package/docs/01-overview/02-customization.md +671 -0
- package/docs/02-features/01-components.md +155 -0
- package/docs/02-features/02-styling.md +139 -0
- package/docs/02-features/03-tasks-entity.md +407 -0
- package/docs/03-ai/01-overview.md +211 -0
- package/docs/03-ai/02-customization.md +436 -0
- package/entities/customers/customers.config.ts +75 -0
- package/entities/customers/customers.fields.ts +165 -0
- package/entities/customers/customers.service.ts +516 -0
- package/entities/customers/customers.types.ts +83 -0
- package/entities/customers/messages/en.json +66 -0
- package/entities/customers/messages/es.json +66 -0
- package/entities/customers/migrations/001_customers_table.sql +102 -0
- package/entities/customers/migrations/002_customers_metas.sql +92 -0
- package/entities/pages/messages/en.json +41 -0
- package/entities/pages/messages/es.json +41 -0
- package/entities/pages/migrations/001_pages_table.sql +112 -0
- package/entities/pages/migrations/002_pages_metas.sql +56 -0
- package/entities/pages/migrations/003_add_status.sql +50 -0
- package/entities/pages/pages-management.service.ts +610 -0
- package/entities/pages/pages.config.ts +94 -0
- package/entities/pages/pages.fields.ts +101 -0
- package/entities/pages/pages.service.ts +290 -0
- package/entities/pages/pages.types.ts +124 -0
- package/entities/posts/components/post-header.tsx +97 -0
- package/entities/posts/messages/en.json +55 -0
- package/entities/posts/messages/es.json +55 -0
- package/entities/posts/migrations/001_posts_table.sql +115 -0
- package/entities/posts/migrations/003_add_status.sql +44 -0
- package/entities/posts/migrations/004_entity_taxonomy_relations.sql +129 -0
- package/entities/posts/migrations/006_posts_metas.sql +56 -0
- package/entities/posts/posts.config.ts +101 -0
- package/entities/posts/posts.fields.ts +116 -0
- package/entities/posts/posts.service.ts +376 -0
- package/entities/posts/posts.types.ts +74 -0
- package/entities/tasks/messages/en.json +204 -0
- package/entities/tasks/messages/es.json +204 -0
- package/entities/tasks/migrations/001_tasks_table.sql +105 -0
- package/entities/tasks/migrations/002_task_metas.sql +85 -0
- package/entities/tasks/migrations/sample_data.json +77 -0
- package/entities/tasks/tasks.config.ts +79 -0
- package/entities/tasks/tasks.fields.ts +196 -0
- package/entities/tasks/tasks.service.ts +541 -0
- package/entities/tasks/tasks.types.ts +56 -0
- package/lib/hooks/useAiChat.ts +114 -0
- package/lib/hooks/useConversations.ts +376 -0
- package/lib/hooks/useOrchestratorChat.ts +122 -0
- package/lib/hooks/usePersistentChat.ts +315 -0
- package/lib/hooks/useStreamingChat.ts +127 -0
- package/lib/hooks/useTokenUsage.ts +63 -0
- package/lib/langchain/agents/customer-assistant.md +69 -0
- package/lib/langchain/agents/index.ts +61 -0
- package/lib/langchain/agents/orchestrator.md +59 -0
- package/lib/langchain/agents/page-assistant.md +85 -0
- package/lib/langchain/agents/single-agent.md +46 -0
- package/lib/langchain/agents/task-assistant.md +55 -0
- package/lib/langchain/config.ts +45 -0
- package/lib/langchain/handlers/customer-handler.ts +338 -0
- package/lib/langchain/handlers/page-handler.ts +232 -0
- package/lib/langchain/handlers/task-handler.ts +323 -0
- package/lib/langchain/langchain.config.ts +223 -0
- package/lib/langchain/observability.config.ts +30 -0
- package/lib/langchain/orchestrator.ts +562 -0
- package/lib/langchain/tools/customers.ts +176 -0
- package/lib/langchain/tools/index.ts +10 -0
- package/lib/langchain/tools/orchestrator.ts +92 -0
- package/lib/langchain/tools/pages.ts +289 -0
- package/lib/langchain/tools/tasks.ts +167 -0
- package/lib/scheduled-actions/billing.ts +149 -0
- package/lib/scheduled-actions/index.ts +170 -0
- package/lib/scheduled-actions/webhook.ts +231 -0
- package/lib/selectors.ts +197 -0
- package/messages/de/admin.json +219 -0
- package/messages/de/aiUsage.json +36 -0
- package/messages/de/buttons.json +19 -0
- package/messages/de/categories.json +35 -0
- package/messages/de/common.json +16 -0
- package/messages/de/dev.json +101 -0
- package/messages/de/docs.json +27 -0
- package/messages/de/entities.json +7 -0
- package/messages/de/features.json +119 -0
- package/messages/de/footer.json +22 -0
- package/messages/de/home.json +57 -0
- package/messages/de/index.ts +39 -0
- package/messages/de/mobileNav.json +13 -0
- package/messages/de/navigation.json +8 -0
- package/messages/de/observability.json +74 -0
- package/messages/de/posts.json +54 -0
- package/messages/de/pricing.json +102 -0
- package/messages/de/support.json +9 -0
- package/messages/de/teams.json +8 -0
- package/messages/en/admin.json +219 -0
- package/messages/en/aiUsage.json +36 -0
- package/messages/en/buttons.json +19 -0
- package/messages/en/categories.json +35 -0
- package/messages/en/common.json +16 -0
- package/messages/en/dev.json +106 -0
- package/messages/en/docs.json +27 -0
- package/messages/en/entities.json +7 -0
- package/messages/en/features.json +119 -0
- package/messages/en/footer.json +22 -0
- package/messages/en/home.json +57 -0
- package/messages/en/index.ts +39 -0
- package/messages/en/mobileNav.json +13 -0
- package/messages/en/navigation.json +8 -0
- package/messages/en/observability.json +74 -0
- package/messages/en/posts.json +54 -0
- package/messages/en/pricing.json +102 -0
- package/messages/en/support.json +9 -0
- package/messages/en/teams.json +8 -0
- package/messages/es/admin.json +219 -0
- package/messages/es/aiUsage.json +36 -0
- package/messages/es/buttons.json +19 -0
- package/messages/es/categories.json +35 -0
- package/messages/es/common.json +16 -0
- package/messages/es/dev.json +101 -0
- package/messages/es/docs.json +27 -0
- package/messages/es/entities.json +7 -0
- package/messages/es/features.json +119 -0
- package/messages/es/footer.json +22 -0
- package/messages/es/home.json +57 -0
- package/messages/es/index.ts +39 -0
- package/messages/es/mobileNav.json +13 -0
- package/messages/es/navigation.json +8 -0
- package/messages/es/observability.json +74 -0
- package/messages/es/posts.json +54 -0
- package/messages/es/pricing.json +102 -0
- package/messages/es/support.json +9 -0
- package/messages/es/teams.json +8 -0
- package/messages/fr/admin.json +219 -0
- package/messages/fr/aiUsage.json +36 -0
- package/messages/fr/buttons.json +19 -0
- package/messages/fr/categories.json +35 -0
- package/messages/fr/common.json +16 -0
- package/messages/fr/dev.json +101 -0
- package/messages/fr/docs.json +27 -0
- package/messages/fr/entities.json +7 -0
- package/messages/fr/features.json +119 -0
- package/messages/fr/footer.json +22 -0
- package/messages/fr/home.json +57 -0
- package/messages/fr/index.ts +39 -0
- package/messages/fr/mobileNav.json +13 -0
- package/messages/fr/navigation.json +8 -0
- package/messages/fr/observability.json +74 -0
- package/messages/fr/posts.json +54 -0
- package/messages/fr/pricing.json +102 -0
- package/messages/fr/support.json +9 -0
- package/messages/fr/teams.json +8 -0
- package/messages/it/admin.json +219 -0
- package/messages/it/aiUsage.json +36 -0
- package/messages/it/buttons.json +19 -0
- package/messages/it/categories.json +35 -0
- package/messages/it/common.json +16 -0
- package/messages/it/dev.json +101 -0
- package/messages/it/docs.json +27 -0
- package/messages/it/entities.json +7 -0
- package/messages/it/features.json +119 -0
- package/messages/it/footer.json +22 -0
- package/messages/it/home.json +57 -0
- package/messages/it/index.ts +39 -0
- package/messages/it/mobileNav.json +13 -0
- package/messages/it/navigation.json +8 -0
- package/messages/it/observability.json +74 -0
- package/messages/it/posts.json +54 -0
- package/messages/it/pricing.json +102 -0
- package/messages/it/support.json +9 -0
- package/messages/it/teams.json +8 -0
- package/messages/pt/admin.json +219 -0
- package/messages/pt/aiUsage.json +36 -0
- package/messages/pt/buttons.json +19 -0
- package/messages/pt/categories.json +35 -0
- package/messages/pt/common.json +16 -0
- package/messages/pt/dev.json +101 -0
- package/messages/pt/docs.json +27 -0
- package/messages/pt/entities.json +7 -0
- package/messages/pt/features.json +119 -0
- package/messages/pt/footer.json +22 -0
- package/messages/pt/home.json +57 -0
- package/messages/pt/index.ts +39 -0
- package/messages/pt/mobileNav.json +13 -0
- package/messages/pt/navigation.json +8 -0
- package/messages/pt/observability.json +74 -0
- package/messages/pt/posts.json +54 -0
- package/messages/pt/pricing.json +102 -0
- package/messages/pt/support.json +9 -0
- package/messages/pt/teams.json +8 -0
- package/migrations/089_add_editor_team_role.sql +39 -0
- package/migrations/090_demo_users_teams.sql +540 -0
- package/migrations/091_greek_teams_billing.sql +523 -0
- package/migrations/092_billing_sample_data.sql +774 -0
- package/migrations/093_pages_sample_data.sql +1158 -0
- package/migrations/094_posts_sample_data.sql +278 -0
- package/migrations/095_tasks_sample_data.sql +440 -0
- package/migrations/096_customers_sample_data.sql +358 -0
- package/migrations/097_scheduled_actions_sample_data.sql +111 -0
- package/package.json +22 -0
- package/public/docs/desktop-layout-example.png +0 -0
- package/styles/components.css +11 -0
- package/styles/globals.css +179 -0
- package/templates/(public)/blog/[slug]/page.tsx +65 -0
- package/templates/(public)/layout.tsx +25 -0
- package/templates/(public)/page.tsx +200 -0
- package/templates/(public)/support/page.tsx +321 -0
- package/templates/dashboard/(main)/agent-multi/page.tsx +63 -0
- package/templates/dashboard/(main)/agent-single/page.tsx +142 -0
- package/templates/dashboard/(main)/settings/ai-usage/page.tsx +157 -0
- package/templates/superadmin/ai-observability/[traceId]/page.tsx +27 -0
- package/templates/superadmin/ai-observability/page.tsx +17 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"entity": {
|
|
3
|
+
"singular": "Post",
|
|
4
|
+
"plural": "Posts"
|
|
5
|
+
},
|
|
6
|
+
"fields": {
|
|
7
|
+
"title": {
|
|
8
|
+
"label": "Title",
|
|
9
|
+
"placeholder": "Enter post title...",
|
|
10
|
+
"description": "Post title"
|
|
11
|
+
},
|
|
12
|
+
"slug": {
|
|
13
|
+
"label": "Slug",
|
|
14
|
+
"placeholder": "post-slug",
|
|
15
|
+
"description": "URL-friendly identifier"
|
|
16
|
+
},
|
|
17
|
+
"status": {
|
|
18
|
+
"label": "Status",
|
|
19
|
+
"description": "Publication status",
|
|
20
|
+
"options": {
|
|
21
|
+
"draft": "Draft",
|
|
22
|
+
"published": "Published",
|
|
23
|
+
"scheduled": "Scheduled",
|
|
24
|
+
"archived": "Archived"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"excerpt": {
|
|
28
|
+
"label": "Excerpt",
|
|
29
|
+
"placeholder": "Brief description...",
|
|
30
|
+
"description": "Short summary shown in listings"
|
|
31
|
+
},
|
|
32
|
+
"featuredImage": {
|
|
33
|
+
"label": "Featured Image",
|
|
34
|
+
"description": "Main image for the post"
|
|
35
|
+
},
|
|
36
|
+
"categories": {
|
|
37
|
+
"label": "Categories",
|
|
38
|
+
"placeholder": "Select categories...",
|
|
39
|
+
"description": "Post categories"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"actions": {
|
|
43
|
+
"create": "Create Post",
|
|
44
|
+
"edit": "Edit Post",
|
|
45
|
+
"delete": "Delete Post",
|
|
46
|
+
"publish": "Publish",
|
|
47
|
+
"unpublish": "Unpublish"
|
|
48
|
+
},
|
|
49
|
+
"messages": {
|
|
50
|
+
"created": "Post created successfully",
|
|
51
|
+
"updated": "Post updated successfully",
|
|
52
|
+
"deleted": "Post deleted successfully",
|
|
53
|
+
"published": "Post published successfully"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"entity": {
|
|
3
|
+
"singular": "Publicacion",
|
|
4
|
+
"plural": "Publicaciones"
|
|
5
|
+
},
|
|
6
|
+
"fields": {
|
|
7
|
+
"title": {
|
|
8
|
+
"label": "Titulo",
|
|
9
|
+
"placeholder": "Ingresa el titulo...",
|
|
10
|
+
"description": "Titulo de la publicacion"
|
|
11
|
+
},
|
|
12
|
+
"slug": {
|
|
13
|
+
"label": "Slug",
|
|
14
|
+
"placeholder": "slug-publicacion",
|
|
15
|
+
"description": "Identificador amigable para URL"
|
|
16
|
+
},
|
|
17
|
+
"status": {
|
|
18
|
+
"label": "Estado",
|
|
19
|
+
"description": "Estado de publicacion",
|
|
20
|
+
"options": {
|
|
21
|
+
"draft": "Borrador",
|
|
22
|
+
"published": "Publicado",
|
|
23
|
+
"scheduled": "Programado",
|
|
24
|
+
"archived": "Archivado"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"excerpt": {
|
|
28
|
+
"label": "Extracto",
|
|
29
|
+
"placeholder": "Breve descripcion...",
|
|
30
|
+
"description": "Resumen corto mostrado en listados"
|
|
31
|
+
},
|
|
32
|
+
"featuredImage": {
|
|
33
|
+
"label": "Imagen Destacada",
|
|
34
|
+
"description": "Imagen principal de la publicacion"
|
|
35
|
+
},
|
|
36
|
+
"categories": {
|
|
37
|
+
"label": "Categorias",
|
|
38
|
+
"placeholder": "Selecciona categorias...",
|
|
39
|
+
"description": "Categorias de la publicacion"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"actions": {
|
|
43
|
+
"create": "Crear Publicacion",
|
|
44
|
+
"edit": "Editar Publicacion",
|
|
45
|
+
"delete": "Eliminar Publicacion",
|
|
46
|
+
"publish": "Publicar",
|
|
47
|
+
"unpublish": "Despublicar"
|
|
48
|
+
},
|
|
49
|
+
"messages": {
|
|
50
|
+
"created": "Publicacion creada exitosamente",
|
|
51
|
+
"updated": "Publicacion actualizada exitosamente",
|
|
52
|
+
"deleted": "Publicacion eliminada exitosamente",
|
|
53
|
+
"published": "Publicacion publicada exitosamente"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
-- Migration: 015_posts_table.sql
|
|
2
|
+
-- Description: Posts table for blog system (similar structure to pages)
|
|
3
|
+
-- Date: 2025-12-16
|
|
4
|
+
-- Updated: 2025-12-17 (Add userId, teamId system fields + team isolation RLS)
|
|
5
|
+
|
|
6
|
+
-- ============================================
|
|
7
|
+
-- TABLE: posts
|
|
8
|
+
-- ============================================
|
|
9
|
+
DROP TABLE IF EXISTS public.posts CASCADE;
|
|
10
|
+
|
|
11
|
+
CREATE TABLE IF NOT EXISTS public.posts (
|
|
12
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
13
|
+
|
|
14
|
+
-- Relational Fields (at the beginning)
|
|
15
|
+
"userId" TEXT NOT NULL REFERENCES public."users"(id) ON DELETE CASCADE,
|
|
16
|
+
"teamId" TEXT NOT NULL REFERENCES public."teams"(id) ON DELETE CASCADE,
|
|
17
|
+
|
|
18
|
+
slug VARCHAR(255) NOT NULL,
|
|
19
|
+
title VARCHAR(255) NOT NULL,
|
|
20
|
+
excerpt TEXT,
|
|
21
|
+
"featuredImage" TEXT,
|
|
22
|
+
blocks JSONB NOT NULL DEFAULT '[]'::JSONB,
|
|
23
|
+
locale VARCHAR(10) NOT NULL DEFAULT 'en',
|
|
24
|
+
|
|
25
|
+
-- SEO fields (same as pages)
|
|
26
|
+
"seoTitle" VARCHAR(255),
|
|
27
|
+
"seoDescription" TEXT,
|
|
28
|
+
"seoKeywords" TEXT,
|
|
29
|
+
"ogImage" TEXT,
|
|
30
|
+
noindex BOOLEAN DEFAULT FALSE,
|
|
31
|
+
nofollow BOOLEAN DEFAULT FALSE,
|
|
32
|
+
|
|
33
|
+
-- Meta fields
|
|
34
|
+
published BOOLEAN DEFAULT FALSE,
|
|
35
|
+
"authorId" TEXT REFERENCES public."users"(id) ON DELETE SET NULL,
|
|
36
|
+
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
37
|
+
"updatedAt" TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
38
|
+
|
|
39
|
+
-- Constraints
|
|
40
|
+
CONSTRAINT unique_post_slug_locale UNIQUE(slug, locale),
|
|
41
|
+
CONSTRAINT valid_post_slug CHECK (slug ~ '^[a-z0-9\-]+$'),
|
|
42
|
+
CONSTRAINT valid_post_locale CHECK (locale ~ '^[a-z]{2}(-[A-Z]{2})?$'),
|
|
43
|
+
CONSTRAINT post_slug_length CHECK (LENGTH(slug) >= 2 AND LENGTH(slug) <= 100),
|
|
44
|
+
CONSTRAINT post_title_not_empty CHECK (LENGTH(TRIM(title)) > 0)
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
-- ============================================
|
|
48
|
+
-- COMMENTS
|
|
49
|
+
-- ============================================
|
|
50
|
+
COMMENT ON TABLE public.posts IS 'Blog posts with block-based content';
|
|
51
|
+
COMMENT ON COLUMN public.posts."userId" IS 'User who created this post';
|
|
52
|
+
COMMENT ON COLUMN public.posts."teamId" IS 'Team context for isolation';
|
|
53
|
+
COMMENT ON COLUMN public.posts.blocks IS 'Array of block instances with props (JSONB)';
|
|
54
|
+
COMMENT ON COLUMN public.posts.slug IS 'URL-friendly identifier';
|
|
55
|
+
COMMENT ON COLUMN public.posts.excerpt IS 'Short summary for previews and SEO';
|
|
56
|
+
COMMENT ON COLUMN public.posts."featuredImage" IS 'Main image URL for post';
|
|
57
|
+
|
|
58
|
+
-- ============================================
|
|
59
|
+
-- TRIGGER: updatedAt
|
|
60
|
+
-- ============================================
|
|
61
|
+
DROP TRIGGER IF EXISTS posts_set_updated_at ON public.posts;
|
|
62
|
+
CREATE TRIGGER posts_set_updated_at
|
|
63
|
+
BEFORE UPDATE ON public.posts
|
|
64
|
+
FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
|
65
|
+
|
|
66
|
+
-- ============================================
|
|
67
|
+
-- INDEXES
|
|
68
|
+
-- ============================================
|
|
69
|
+
CREATE INDEX IF NOT EXISTS idx_posts_user_id ON public.posts("userId");
|
|
70
|
+
CREATE INDEX IF NOT EXISTS idx_posts_team_id ON public.posts("teamId");
|
|
71
|
+
CREATE INDEX IF NOT EXISTS idx_posts_slug ON public.posts(slug);
|
|
72
|
+
CREATE INDEX IF NOT EXISTS idx_posts_locale ON public.posts(locale);
|
|
73
|
+
CREATE INDEX IF NOT EXISTS idx_posts_published ON public.posts(published);
|
|
74
|
+
CREATE INDEX IF NOT EXISTS idx_posts_author ON public.posts("authorId");
|
|
75
|
+
CREATE INDEX IF NOT EXISTS idx_posts_created ON public.posts("createdAt" DESC);
|
|
76
|
+
CREATE INDEX IF NOT EXISTS idx_posts_slug_locale ON public.posts(slug, locale);
|
|
77
|
+
CREATE INDEX IF NOT EXISTS idx_posts_published_locale ON public.posts(published, locale) WHERE published = TRUE;
|
|
78
|
+
CREATE INDEX IF NOT EXISTS idx_posts_blocks_gin ON public.posts USING GIN (blocks);
|
|
79
|
+
CREATE INDEX IF NOT EXISTS idx_posts_team_created ON public.posts("teamId", "createdAt" DESC);
|
|
80
|
+
|
|
81
|
+
-- ============================================
|
|
82
|
+
-- RLS
|
|
83
|
+
-- ============================================
|
|
84
|
+
ALTER TABLE public.posts ENABLE ROW LEVEL SECURITY;
|
|
85
|
+
|
|
86
|
+
DROP POLICY IF EXISTS "posts public can select" ON public.posts;
|
|
87
|
+
DROP POLICY IF EXISTS "posts auth can do all" ON public.posts;
|
|
88
|
+
DROP POLICY IF EXISTS "Posts team can do all" ON public.posts;
|
|
89
|
+
|
|
90
|
+
-- Public can read published posts
|
|
91
|
+
CREATE POLICY "posts public can select"
|
|
92
|
+
ON public.posts
|
|
93
|
+
FOR SELECT TO anon
|
|
94
|
+
USING (published = TRUE);
|
|
95
|
+
|
|
96
|
+
-- ============================
|
|
97
|
+
-- RLS: TEAM ISOLATION ONLY
|
|
98
|
+
-- ============================
|
|
99
|
+
-- IMPORTANT: RLS only verifies team membership
|
|
100
|
+
-- access.shared logic (user isolation) is handled at APP LEVEL
|
|
101
|
+
CREATE POLICY "Posts team can do all"
|
|
102
|
+
ON public.posts
|
|
103
|
+
FOR ALL TO authenticated
|
|
104
|
+
USING (
|
|
105
|
+
-- Superadmin bypass
|
|
106
|
+
public.is_superadmin()
|
|
107
|
+
OR
|
|
108
|
+
-- Team isolation only: user must be member of the team
|
|
109
|
+
"teamId" = ANY(public.get_user_team_ids())
|
|
110
|
+
)
|
|
111
|
+
WITH CHECK (
|
|
112
|
+
public.is_superadmin()
|
|
113
|
+
OR
|
|
114
|
+
"teamId" = ANY(public.get_user_team_ids())
|
|
115
|
+
);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
-- Migration: 025_add_status_to_posts.sql
|
|
2
|
+
-- Description: Add status column to posts
|
|
3
|
+
-- Date: 2025-12-17
|
|
4
|
+
-- Session: builder-entities-unification-v2
|
|
5
|
+
|
|
6
|
+
-- ============================================
|
|
7
|
+
-- STEP 1: Add status column (replaces published boolean)
|
|
8
|
+
-- ============================================
|
|
9
|
+
ALTER TABLE public."posts"
|
|
10
|
+
ADD COLUMN IF NOT EXISTS "status" VARCHAR(50) DEFAULT 'draft';
|
|
11
|
+
|
|
12
|
+
-- Migrate existing data
|
|
13
|
+
UPDATE public."posts"
|
|
14
|
+
SET "status" = CASE
|
|
15
|
+
WHEN "published" = true THEN 'published'
|
|
16
|
+
ELSE 'draft'
|
|
17
|
+
END
|
|
18
|
+
WHERE "status" = 'draft' OR "status" IS NULL;
|
|
19
|
+
|
|
20
|
+
-- Create index for status filtering
|
|
21
|
+
CREATE INDEX IF NOT EXISTS idx_posts_status ON public."posts"("status");
|
|
22
|
+
|
|
23
|
+
-- Add check constraint
|
|
24
|
+
ALTER TABLE public."posts" DROP CONSTRAINT IF EXISTS posts_status_check;
|
|
25
|
+
ALTER TABLE public."posts"
|
|
26
|
+
ADD CONSTRAINT posts_status_check
|
|
27
|
+
CHECK ("status" IN ('draft', 'published', 'scheduled', 'archived'));
|
|
28
|
+
|
|
29
|
+
-- ============================================
|
|
30
|
+
-- STEP 2: Update existing RLS policy to use status instead of published
|
|
31
|
+
-- ============================================
|
|
32
|
+
DROP POLICY IF EXISTS "posts public can select" ON public."posts";
|
|
33
|
+
|
|
34
|
+
CREATE POLICY "posts public can select"
|
|
35
|
+
ON public."posts"
|
|
36
|
+
FOR SELECT TO anon
|
|
37
|
+
USING ("status" = 'published');
|
|
38
|
+
|
|
39
|
+
-- ============================================
|
|
40
|
+
-- COMMENTS
|
|
41
|
+
-- ============================================
|
|
42
|
+
COMMENT ON COLUMN public."posts"."status" IS 'Publication status: draft, published, scheduled, archived';
|
|
43
|
+
|
|
44
|
+
-- Note: We keep 'published' column temporarily for rollback safety
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
-- Migration: 003_entity_taxonomy_relations.sql
|
|
2
|
+
-- Description: Generic entity-taxonomy relations table
|
|
3
|
+
-- Date: 2025-12-17
|
|
4
|
+
-- Session: builder-entities-unification-v2
|
|
5
|
+
-- Note: Placed in posts entity to ensure posts and pages tables exist first
|
|
6
|
+
|
|
7
|
+
-- ============================================
|
|
8
|
+
-- TABLE: entity_taxonomy_relations
|
|
9
|
+
-- Replaces specific post_taxonomy_relations with a generic system
|
|
10
|
+
-- ============================================
|
|
11
|
+
CREATE TABLE IF NOT EXISTS public."entity_taxonomy_relations" (
|
|
12
|
+
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
13
|
+
"entityType" VARCHAR(50) NOT NULL, -- 'posts', 'pages', 'products', etc.
|
|
14
|
+
"entityId" TEXT NOT NULL,
|
|
15
|
+
"taxonomyId" TEXT NOT NULL REFERENCES public."taxonomies"(id) ON DELETE CASCADE,
|
|
16
|
+
"order" INTEGER DEFAULT 0,
|
|
17
|
+
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
18
|
+
|
|
19
|
+
CONSTRAINT unique_entity_taxonomy UNIQUE("entityType", "entityId", "taxonomyId")
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
-- ============================================
|
|
23
|
+
-- COMMENTS
|
|
24
|
+
-- ============================================
|
|
25
|
+
COMMENT ON TABLE public."entity_taxonomy_relations" IS 'Generic many-to-many relation between any entity and taxonomies';
|
|
26
|
+
COMMENT ON COLUMN public."entity_taxonomy_relations"."entityType" IS 'Type of entity (posts, pages, products, etc.)';
|
|
27
|
+
COMMENT ON COLUMN public."entity_taxonomy_relations"."entityId" IS 'ID of the entity (UUID as text)';
|
|
28
|
+
COMMENT ON COLUMN public."entity_taxonomy_relations"."taxonomyId" IS 'Reference to taxonomy';
|
|
29
|
+
COMMENT ON COLUMN public."entity_taxonomy_relations"."order" IS 'Display order of taxonomy within entity';
|
|
30
|
+
|
|
31
|
+
-- ============================================
|
|
32
|
+
-- INDEXES for common queries
|
|
33
|
+
-- ============================================
|
|
34
|
+
CREATE INDEX IF NOT EXISTS idx_etr_entity_type ON public."entity_taxonomy_relations"("entityType");
|
|
35
|
+
CREATE INDEX IF NOT EXISTS idx_etr_entity_id ON public."entity_taxonomy_relations"("entityId");
|
|
36
|
+
CREATE INDEX IF NOT EXISTS idx_etr_taxonomy_id ON public."entity_taxonomy_relations"("taxonomyId");
|
|
37
|
+
CREATE INDEX IF NOT EXISTS idx_etr_entity_lookup ON public."entity_taxonomy_relations"("entityType", "entityId");
|
|
38
|
+
|
|
39
|
+
-- ============================================
|
|
40
|
+
-- MIGRATE existing data from post_taxonomy_relations (if exists)
|
|
41
|
+
-- ============================================
|
|
42
|
+
DO $$
|
|
43
|
+
BEGIN
|
|
44
|
+
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'post_taxonomy_relations' AND table_schema = 'public') THEN
|
|
45
|
+
INSERT INTO public."entity_taxonomy_relations" ("entityType", "entityId", "taxonomyId", "order", "createdAt")
|
|
46
|
+
SELECT
|
|
47
|
+
'posts' as "entityType",
|
|
48
|
+
"postId"::text as "entityId",
|
|
49
|
+
"taxonomyId",
|
|
50
|
+
"order",
|
|
51
|
+
"createdAt"
|
|
52
|
+
FROM public."post_taxonomy_relations"
|
|
53
|
+
ON CONFLICT DO NOTHING;
|
|
54
|
+
END IF;
|
|
55
|
+
END $$;
|
|
56
|
+
|
|
57
|
+
-- ============================================
|
|
58
|
+
-- RLS
|
|
59
|
+
-- ============================================
|
|
60
|
+
ALTER TABLE public."entity_taxonomy_relations" ENABLE ROW LEVEL SECURITY;
|
|
61
|
+
|
|
62
|
+
DROP POLICY IF EXISTS "Entity taxonomy relations public read" ON public."entity_taxonomy_relations";
|
|
63
|
+
DROP POLICY IF EXISTS "Entity taxonomy relations authenticated read" ON public."entity_taxonomy_relations";
|
|
64
|
+
DROP POLICY IF EXISTS "Entity taxonomy relations authenticated insert" ON public."entity_taxonomy_relations";
|
|
65
|
+
DROP POLICY IF EXISTS "Entity taxonomy relations authenticated delete" ON public."entity_taxonomy_relations";
|
|
66
|
+
|
|
67
|
+
-- Public can read relations for published entities
|
|
68
|
+
CREATE POLICY "Entity taxonomy relations public read"
|
|
69
|
+
ON public."entity_taxonomy_relations"
|
|
70
|
+
FOR SELECT TO anon
|
|
71
|
+
USING (
|
|
72
|
+
-- Posts: check status = published
|
|
73
|
+
("entityType" = 'posts' AND EXISTS (
|
|
74
|
+
SELECT 1 FROM public.posts p WHERE p.id::text = "entityId" AND p.status = 'published'
|
|
75
|
+
))
|
|
76
|
+
OR
|
|
77
|
+
-- Pages: check status = published
|
|
78
|
+
("entityType" = 'pages' AND EXISTS (
|
|
79
|
+
SELECT 1 FROM public.pages p WHERE p.id::text = "entityId" AND p.status = 'published'
|
|
80
|
+
))
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
-- Authenticated users can manage all relations
|
|
84
|
+
CREATE POLICY "Entity taxonomy relations authenticated read"
|
|
85
|
+
ON public."entity_taxonomy_relations"
|
|
86
|
+
FOR SELECT TO authenticated
|
|
87
|
+
USING (true);
|
|
88
|
+
|
|
89
|
+
CREATE POLICY "Entity taxonomy relations authenticated insert"
|
|
90
|
+
ON public."entity_taxonomy_relations"
|
|
91
|
+
FOR INSERT TO authenticated
|
|
92
|
+
WITH CHECK (true);
|
|
93
|
+
|
|
94
|
+
CREATE POLICY "Entity taxonomy relations authenticated delete"
|
|
95
|
+
ON public."entity_taxonomy_relations"
|
|
96
|
+
FOR DELETE TO authenticated
|
|
97
|
+
USING (true);
|
|
98
|
+
|
|
99
|
+
-- ============================================
|
|
100
|
+
-- CLEANUP TRIGGER function
|
|
101
|
+
-- ============================================
|
|
102
|
+
CREATE OR REPLACE FUNCTION public.cleanup_entity_taxonomy_relations()
|
|
103
|
+
RETURNS TRIGGER AS $$
|
|
104
|
+
BEGIN
|
|
105
|
+
DELETE FROM public."entity_taxonomy_relations"
|
|
106
|
+
WHERE "entityType" = TG_ARGV[0] AND "entityId" = OLD.id::text;
|
|
107
|
+
RETURN OLD;
|
|
108
|
+
END;
|
|
109
|
+
$$ LANGUAGE plpgsql;
|
|
110
|
+
|
|
111
|
+
-- Trigger for posts
|
|
112
|
+
DROP TRIGGER IF EXISTS cleanup_post_entity_taxonomy ON public."posts";
|
|
113
|
+
CREATE TRIGGER cleanup_post_entity_taxonomy
|
|
114
|
+
AFTER DELETE ON public."posts"
|
|
115
|
+
FOR EACH ROW
|
|
116
|
+
EXECUTE FUNCTION public.cleanup_entity_taxonomy_relations('posts');
|
|
117
|
+
|
|
118
|
+
-- Trigger for pages
|
|
119
|
+
DROP TRIGGER IF EXISTS cleanup_page_entity_taxonomy ON public."pages";
|
|
120
|
+
CREATE TRIGGER cleanup_page_entity_taxonomy
|
|
121
|
+
AFTER DELETE ON public."pages"
|
|
122
|
+
FOR EACH ROW
|
|
123
|
+
EXECUTE FUNCTION public.cleanup_entity_taxonomy_relations('pages');
|
|
124
|
+
|
|
125
|
+
-- ============================================
|
|
126
|
+
-- Additional indexes using status
|
|
127
|
+
-- ============================================
|
|
128
|
+
CREATE INDEX IF NOT EXISTS idx_pages_status_locale ON public."pages"("status", locale) WHERE "status" = 'published';
|
|
129
|
+
CREATE INDEX IF NOT EXISTS idx_posts_status_locale ON public."posts"("status", locale) WHERE "status" = 'published';
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
-- Migration: 006_posts_metas.sql
|
|
2
|
+
-- Description: Create posts_metas table for flexible key-value metadata storage
|
|
3
|
+
-- Date: 2025-12-17
|
|
4
|
+
|
|
5
|
+
-- Create posts_metas table
|
|
6
|
+
CREATE TABLE IF NOT EXISTS "posts_metas" (
|
|
7
|
+
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
8
|
+
"postId" UUID NOT NULL REFERENCES public."posts"(id) ON DELETE CASCADE,
|
|
9
|
+
"metaKey" TEXT NOT NULL,
|
|
10
|
+
"metaValue" JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
11
|
+
"dataType" TEXT,
|
|
12
|
+
"isPublic" BOOLEAN NOT NULL DEFAULT FALSE,
|
|
13
|
+
"isSearchable" BOOLEAN NOT NULL DEFAULT FALSE,
|
|
14
|
+
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
15
|
+
"updatedAt" TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
16
|
+
CONSTRAINT posts_metas_unique_key UNIQUE ("postId", "metaKey")
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
-- Indices
|
|
20
|
+
CREATE INDEX IF NOT EXISTS idx_posts_metas_post_id ON "posts_metas"("postId");
|
|
21
|
+
CREATE INDEX IF NOT EXISTS idx_posts_metas_key ON "posts_metas"("metaKey");
|
|
22
|
+
CREATE INDEX IF NOT EXISTS idx_posts_metas_composite ON "posts_metas"("postId", "metaKey", "isPublic");
|
|
23
|
+
CREATE INDEX IF NOT EXISTS idx_posts_metas_searchable ON "posts_metas"("isSearchable") WHERE "isSearchable" = true;
|
|
24
|
+
CREATE INDEX IF NOT EXISTS idx_posts_metas_public ON "posts_metas"("isPublic") WHERE "isPublic" = true;
|
|
25
|
+
CREATE INDEX IF NOT EXISTS idx_posts_metas_value_gin ON "posts_metas" USING GIN ("metaValue");
|
|
26
|
+
|
|
27
|
+
-- Trigger for updatedAt
|
|
28
|
+
DROP TRIGGER IF EXISTS posts_metas_set_updated_at ON "posts_metas";
|
|
29
|
+
CREATE TRIGGER posts_metas_set_updated_at
|
|
30
|
+
BEFORE UPDATE ON "posts_metas"
|
|
31
|
+
FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
|
32
|
+
|
|
33
|
+
-- ============================================
|
|
34
|
+
-- RLS POLICIES
|
|
35
|
+
-- ============================================
|
|
36
|
+
ALTER TABLE "posts_metas" ENABLE ROW LEVEL SECURITY;
|
|
37
|
+
|
|
38
|
+
-- Public can read public metadata
|
|
39
|
+
DROP POLICY IF EXISTS "posts_metas_public_select" ON "posts_metas";
|
|
40
|
+
CREATE POLICY "posts_metas_public_select"
|
|
41
|
+
ON "posts_metas" FOR SELECT TO anon
|
|
42
|
+
USING ("isPublic" = TRUE);
|
|
43
|
+
|
|
44
|
+
-- Authenticated users can manage all metadata
|
|
45
|
+
DROP POLICY IF EXISTS "posts_metas_auth_all" ON "posts_metas";
|
|
46
|
+
CREATE POLICY "posts_metas_auth_all"
|
|
47
|
+
ON "posts_metas" FOR ALL TO authenticated
|
|
48
|
+
USING (true)
|
|
49
|
+
WITH CHECK (true);
|
|
50
|
+
|
|
51
|
+
-- Comments
|
|
52
|
+
COMMENT ON TABLE "posts_metas" IS 'Flexible key-value metadata storage for posts';
|
|
53
|
+
COMMENT ON COLUMN "posts_metas"."postId" IS 'Reference to the parent post';
|
|
54
|
+
COMMENT ON COLUMN "posts_metas"."metaKey" IS 'Unique key identifier for the metadata';
|
|
55
|
+
COMMENT ON COLUMN "posts_metas"."metaValue" IS 'JSONB value for flexible data storage';
|
|
56
|
+
COMMENT ON COLUMN "posts_metas"."isPublic" IS 'If true, this metadata can be read by anyone';
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Posts Entity Configuration
|
|
3
|
+
*
|
|
4
|
+
* Posts are builder-enabled entities with taxonomies support that render at /blog/[slug].
|
|
5
|
+
* Includes sidebar fields for excerpt and featured image, plus category management.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Newspaper } from 'lucide-react'
|
|
9
|
+
import type { EntityConfig } from '@nextsparkjs/core/lib/entities/types'
|
|
10
|
+
import { postsFields } from './posts.fields'
|
|
11
|
+
|
|
12
|
+
export const postsEntityConfig: EntityConfig = {
|
|
13
|
+
// ==========================================
|
|
14
|
+
// 1. BASIC IDENTIFICATION
|
|
15
|
+
// ==========================================
|
|
16
|
+
slug: 'posts',
|
|
17
|
+
enabled: true,
|
|
18
|
+
|
|
19
|
+
names: {
|
|
20
|
+
singular: 'post',
|
|
21
|
+
plural: 'Posts',
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
icon: Newspaper,
|
|
25
|
+
tableName: 'posts',
|
|
26
|
+
|
|
27
|
+
// ==========================================
|
|
28
|
+
// 2. ACCESS AND SCOPE CONFIGURATION
|
|
29
|
+
// ==========================================
|
|
30
|
+
access: {
|
|
31
|
+
public: true, // Blog posts are public
|
|
32
|
+
api: true, // Has external API endpoints
|
|
33
|
+
metadata: true, // Posts support metadata
|
|
34
|
+
shared: true, // All authenticated users can see all posts
|
|
35
|
+
basePath: '/blog', // Posts render at /blog/[slug]
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
// ==========================================
|
|
39
|
+
// 3. UI/UX FEATURES
|
|
40
|
+
// ==========================================
|
|
41
|
+
ui: {
|
|
42
|
+
dashboard: {
|
|
43
|
+
showInMenu: true,
|
|
44
|
+
showInTopbar: true,
|
|
45
|
+
filters: [
|
|
46
|
+
{ field: 'status', type: 'multiSelect' },
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
public: {
|
|
50
|
+
hasArchivePage: true, // /blog shows list of posts
|
|
51
|
+
hasSinglePage: true, // /blog/[slug] shows individual post
|
|
52
|
+
},
|
|
53
|
+
features: {
|
|
54
|
+
searchable: true,
|
|
55
|
+
sortable: true,
|
|
56
|
+
filterable: true,
|
|
57
|
+
bulkOperations: true,
|
|
58
|
+
importExport: true,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
// ==========================================
|
|
63
|
+
// 4. PERMISSIONS SYSTEM
|
|
64
|
+
// ==========================================
|
|
65
|
+
// Permissions are now centralized in permissions.config.ts
|
|
66
|
+
// See: contents/themes/default/permissions.config.ts -> entities.posts
|
|
67
|
+
|
|
68
|
+
// ==========================================
|
|
69
|
+
// 6. BUILDER CONFIGURATION
|
|
70
|
+
// ==========================================
|
|
71
|
+
builder: {
|
|
72
|
+
enabled: true,
|
|
73
|
+
sidebarFields: ['excerpt', 'featuredImage'], // Extra fields shown in sidebar
|
|
74
|
+
seo: true, // Enable SEO fields panel in editor
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
// ==========================================
|
|
78
|
+
// 7. TAXONOMIES CONFIGURATION
|
|
79
|
+
// ==========================================
|
|
80
|
+
taxonomies: {
|
|
81
|
+
enabled: true,
|
|
82
|
+
types: [
|
|
83
|
+
{
|
|
84
|
+
type: 'post_category',
|
|
85
|
+
field: 'categories',
|
|
86
|
+
multiple: true,
|
|
87
|
+
label: 'Categories',
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
// ==========================================
|
|
93
|
+
// FIELDS
|
|
94
|
+
// ==========================================
|
|
95
|
+
fields: postsFields,
|
|
96
|
+
|
|
97
|
+
// ==========================================
|
|
98
|
+
// METADATA
|
|
99
|
+
// ==========================================
|
|
100
|
+
source: 'theme',
|
|
101
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Posts Entity Fields Configuration
|
|
3
|
+
*
|
|
4
|
+
* Fields required for builder-enabled posts with sidebar support.
|
|
5
|
+
* Note: 'blocks' field is NOT in fields[] - it's managed by the builder view automatically.
|
|
6
|
+
* Note: 'categories' is managed by taxonomies system, not a regular field.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { EntityField } from '@nextsparkjs/core/lib/entities/types'
|
|
10
|
+
|
|
11
|
+
export const postsFields: EntityField[] = [
|
|
12
|
+
{
|
|
13
|
+
name: 'title',
|
|
14
|
+
type: 'text',
|
|
15
|
+
required: true,
|
|
16
|
+
display: {
|
|
17
|
+
label: 'Title',
|
|
18
|
+
description: 'Post title',
|
|
19
|
+
placeholder: 'Enter post title...',
|
|
20
|
+
showInList: true,
|
|
21
|
+
showInDetail: true,
|
|
22
|
+
showInForm: true,
|
|
23
|
+
order: 1,
|
|
24
|
+
},
|
|
25
|
+
api: {
|
|
26
|
+
searchable: true,
|
|
27
|
+
sortable: true,
|
|
28
|
+
readOnly: false,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'slug',
|
|
33
|
+
type: 'text',
|
|
34
|
+
required: true,
|
|
35
|
+
display: {
|
|
36
|
+
label: 'Slug',
|
|
37
|
+
description: 'URL-friendly identifier',
|
|
38
|
+
placeholder: 'post-slug',
|
|
39
|
+
showInList: true,
|
|
40
|
+
showInDetail: true,
|
|
41
|
+
showInForm: true,
|
|
42
|
+
order: 2,
|
|
43
|
+
},
|
|
44
|
+
api: {
|
|
45
|
+
searchable: true,
|
|
46
|
+
sortable: true,
|
|
47
|
+
readOnly: false,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'status',
|
|
52
|
+
type: 'select',
|
|
53
|
+
required: true,
|
|
54
|
+
defaultValue: 'draft',
|
|
55
|
+
options: [
|
|
56
|
+
{ value: 'draft', label: 'Draft' },
|
|
57
|
+
{ value: 'published', label: 'Published' },
|
|
58
|
+
{ value: 'scheduled', label: 'Scheduled' },
|
|
59
|
+
{ value: 'archived', label: 'Archived' },
|
|
60
|
+
],
|
|
61
|
+
display: {
|
|
62
|
+
label: 'Status',
|
|
63
|
+
description: 'Publication status',
|
|
64
|
+
showInList: true,
|
|
65
|
+
showInDetail: true,
|
|
66
|
+
showInForm: true,
|
|
67
|
+
order: 3,
|
|
68
|
+
},
|
|
69
|
+
api: {
|
|
70
|
+
searchable: false,
|
|
71
|
+
sortable: true,
|
|
72
|
+
filterable: true,
|
|
73
|
+
readOnly: false,
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'excerpt',
|
|
78
|
+
type: 'textarea',
|
|
79
|
+
required: false,
|
|
80
|
+
display: {
|
|
81
|
+
label: 'Excerpt',
|
|
82
|
+
description: 'Short summary of the post',
|
|
83
|
+
placeholder: 'Brief description...',
|
|
84
|
+
showInList: false,
|
|
85
|
+
showInDetail: true,
|
|
86
|
+
showInForm: true, // Shown in builder sidebar
|
|
87
|
+
order: 4,
|
|
88
|
+
},
|
|
89
|
+
api: {
|
|
90
|
+
searchable: true,
|
|
91
|
+
sortable: false,
|
|
92
|
+
readOnly: false,
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: 'featuredImage',
|
|
97
|
+
type: 'image',
|
|
98
|
+
required: false,
|
|
99
|
+
display: {
|
|
100
|
+
label: 'Featured Image',
|
|
101
|
+
description: 'Main image for the post',
|
|
102
|
+
showInList: false,
|
|
103
|
+
showInDetail: true,
|
|
104
|
+
showInForm: true, // Shown in builder sidebar
|
|
105
|
+
order: 5,
|
|
106
|
+
},
|
|
107
|
+
api: {
|
|
108
|
+
searchable: false,
|
|
109
|
+
sortable: false,
|
|
110
|
+
readOnly: false,
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
// Note: 'categories' is managed by taxonomies system, not a regular field
|
|
114
|
+
// Note: SEO fields (seoTitle, seoDescription, etc.) are managed by the builder SEO panel
|
|
115
|
+
// Note: 'blocks' field is a system field for builder-enabled entities - NOT defined here
|
|
116
|
+
]
|