@nextsparkjs/theme-default 0.1.0-beta.44 → 0.1.0-beta.45

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 (48) hide show
  1. package/components/ai-chat/ChatPanel.tsx +7 -7
  2. package/components/ai-chat/Message.tsx +2 -2
  3. package/components/ai-chat/MessageInput.tsx +3 -3
  4. package/components/ai-chat/MessageList.tsx +3 -3
  5. package/components/ai-chat/TypingIndicator.tsx +2 -2
  6. package/entities/customers/api/docs.md +107 -0
  7. package/entities/customers/api/presets.ts +80 -0
  8. package/entities/pages/api/docs.md +114 -0
  9. package/entities/pages/api/presets.ts +72 -0
  10. package/entities/posts/api/docs.md +120 -0
  11. package/entities/posts/api/presets.ts +74 -0
  12. package/entities/tasks/api/docs.md +126 -0
  13. package/entities/tasks/api/presets.ts +84 -0
  14. package/lib/selectors.ts +2 -2
  15. package/messages/de/admin.json +45 -0
  16. package/messages/en/admin.json +45 -0
  17. package/messages/es/admin.json +45 -0
  18. package/messages/fr/admin.json +45 -0
  19. package/messages/it/admin.json +45 -0
  20. package/messages/pt/admin.json +45 -0
  21. package/package.json +3 -3
  22. package/styles/globals.css +24 -0
  23. package/tests/cypress/e2e/_utils/selectors/block-editor.bdd.md +491 -0
  24. package/tests/cypress/e2e/_utils/selectors/block-editor.cy.ts +475 -0
  25. package/tests/cypress/e2e/_utils/selectors/dashboard-container.cy.ts +52 -0
  26. package/tests/cypress/e2e/_utils/selectors/dashboard-mobile.cy.ts +14 -14
  27. package/tests/cypress/e2e/_utils/selectors/dashboard-navigation.cy.ts +3 -3
  28. package/tests/cypress/e2e/_utils/selectors/dashboard-sidebar.bdd.md +38 -73
  29. package/tests/cypress/e2e/_utils/selectors/dashboard-sidebar.cy.ts +21 -42
  30. package/tests/cypress/e2e/_utils/selectors/dashboard-topnav.bdd.md +117 -38
  31. package/tests/cypress/e2e/_utils/selectors/dashboard-topnav.cy.ts +35 -12
  32. package/tests/cypress/e2e/_utils/selectors/settings-layout.bdd.md +50 -59
  33. package/tests/cypress/e2e/_utils/selectors/settings-layout.cy.ts +15 -23
  34. package/tests/cypress/e2e/_utils/selectors/tasks.bdd.md +395 -155
  35. package/tests/cypress/e2e/_utils/selectors/tasks.cy.ts +795 -174
  36. package/tests/cypress/e2e/api/_core/teams/teams-security.cy.ts +415 -0
  37. package/tests/cypress/e2e/uat/_core/teams/inline-edit.cy.ts +278 -0
  38. package/tests/cypress/src/core/BlockEditorBasePOM.ts +269 -99
  39. package/tests/cypress/src/core/DashboardEntityPOM.ts +1 -1
  40. package/tests/cypress/src/features/DashboardPOM.ts +49 -28
  41. package/tests/cypress/src/features/PageBuilderPOM.ts +20 -0
  42. package/tests/cypress/src/features/SettingsPOM.ts +511 -166
  43. package/tests/cypress/src/features/SuperadminPOM.ts +679 -159
  44. package/tests/cypress/src/features/index.ts +10 -10
  45. package/tests/cypress/e2e/_utils/selectors/pages-editor.bdd.md +0 -207
  46. package/tests/cypress/e2e/_utils/selectors/pages-editor.cy.ts +0 -211
  47. package/tests/cypress/e2e/_utils/selectors/posts-editor.bdd.md +0 -184
  48. package/tests/cypress/e2e/_utils/selectors/posts-editor.cy.ts +0 -350
@@ -8,7 +8,7 @@ import { MessageInput } from './MessageInput'
8
8
  import { MarkdownRenderer } from './MarkdownRenderer'
9
9
  import { TypingIndicator } from './TypingIndicator'
10
10
  import { ConversationSidebar } from './ConversationSidebar'
11
- import { createCyId } from '@nextsparkjs/testing/utils'
11
+ import { sel } from '@nextsparkjs/core/selectors'
12
12
  import { cn } from '@nextsparkjs/core/lib/utils'
13
13
  import { Bot, User, Loader2, Trash2, ListTodo, Users, FileText, Sparkles, X } from 'lucide-react'
14
14
  import type { ConversationInfo } from '../../lib/hooks/useConversations'
@@ -163,7 +163,7 @@ function ChatMessageItem({
163
163
 
164
164
  return (
165
165
  <div
166
- data-cy={createCyId(cyPrefix, `message-${message.role}`)}
166
+ data-cy={sel(message.role === 'user' ? 'common.aiChat.messageUser' : 'common.aiChat.messageAssistant')}
167
167
  className={cn(
168
168
  'flex w-full gap-3 p-4',
169
169
  isUser ? 'flex-row-reverse' : 'flex-row'
@@ -351,7 +351,7 @@ export function ChatPanel({
351
351
  return (
352
352
  <div className="h-[calc(100vh-4rem)] p-4 md:p-6 lg:p-8">
353
353
  <Card
354
- data-cy={createCyId(cyPrefix, 'panel')}
354
+ data-cy={sel('common.aiChat.panel')}
355
355
  className={cn(
356
356
  'flex h-full mx-auto overflow-hidden shadow-lg',
357
357
  layoutClass
@@ -408,7 +408,7 @@ export function ChatPanel({
408
408
  {/* Clear button */}
409
409
  {header.showClearButton && header.onClear && (
410
410
  <Button
411
- data-cy={createCyId(cyPrefix, 'clear-btn')}
411
+ data-cy={sel('common.aiChat.clearBtn')}
412
412
  variant="ghost"
413
413
  size="sm"
414
414
  onClick={header.onClear}
@@ -470,13 +470,13 @@ export function ChatPanel({
470
470
  ) : (
471
471
  /* Messages */
472
472
  <ScrollArea
473
- data-cy={createCyId(cyPrefix, 'message-list')}
473
+ data-cy={sel('common.aiChat.messageList')}
474
474
  className="flex-1 p-4"
475
475
  >
476
476
  <div className="flex flex-col gap-4">
477
477
  {error && (
478
478
  <div
479
- data-cy={createCyId(cyPrefix, 'error-message')}
479
+ data-cy={sel('common.aiChat.errorMessage')}
480
480
  className="p-4 mb-2 text-sm text-red-600 bg-red-50 dark:bg-red-900/20 dark:text-red-400 rounded-lg"
481
481
  >
482
482
  {error}
@@ -517,7 +517,7 @@ export function ChatPanel({
517
517
  variant="outline"
518
518
  size="sm"
519
519
  onClick={onCancelStream}
520
- data-cy="chat-cancel-button"
520
+ data-cy={sel('common.aiChat.cancelBtn')}
521
521
  >
522
522
  <X className="h-4 w-4 mr-1" />
523
523
  Stop generating
@@ -2,7 +2,7 @@ import { cn } from '@nextsparkjs/core/lib/utils'
2
2
  import { Message as MessageType } from '../../lib/hooks/useAiChat'
3
3
  import { MarkdownRenderer } from './MarkdownRenderer'
4
4
  import { User, Bot } from 'lucide-react'
5
- import { createCyId } from '@nextsparkjs/testing/utils'
5
+ import { sel } from '@nextsparkjs/core/selectors'
6
6
 
7
7
  interface MessageProps {
8
8
  message: MessageType
@@ -13,7 +13,7 @@ export function Message({ message }: MessageProps) {
13
13
 
14
14
  return (
15
15
  <div
16
- data-cy={createCyId('ai-chat', `message-${message.role}`)}
16
+ data-cy={sel(message.role === 'user' ? 'common.aiChat.messageUser' : 'common.aiChat.messageAssistant')}
17
17
  className={cn(
18
18
  'flex w-full gap-3 p-4',
19
19
  isUser ? 'flex-row-reverse' : 'flex-row'
@@ -2,7 +2,7 @@ import { KeyboardEvent } from 'react'
2
2
  import { Button } from '@nextsparkjs/core/components/ui/button'
3
3
  import { Textarea } from '@nextsparkjs/core/components/ui/textarea'
4
4
  import { Send } from 'lucide-react'
5
- import { createCyId } from '@nextsparkjs/testing/utils'
5
+ import { sel } from '@nextsparkjs/core/selectors'
6
6
 
7
7
  interface MessageInputProps {
8
8
  value: string
@@ -23,7 +23,7 @@ export function MessageInput({ value, onChange, onSend, isLoading }: MessageInpu
23
23
  <div className="p-4 border-t bg-background">
24
24
  <div className="flex gap-2">
25
25
  <Textarea
26
- data-cy={createCyId('ai-chat', 'message-input')}
26
+ data-cy={sel('common.aiChat.messageInput')}
27
27
  value={value}
28
28
  onChange={(e) => onChange(e.target.value)}
29
29
  onKeyDown={handleKeyDown}
@@ -32,7 +32,7 @@ export function MessageInput({ value, onChange, onSend, isLoading }: MessageInpu
32
32
  disabled={isLoading}
33
33
  />
34
34
  <Button
35
- data-cy={createCyId('ai-chat', 'send-btn')}
35
+ data-cy={sel('common.aiChat.sendBtn')}
36
36
  size="icon"
37
37
  onClick={onSend}
38
38
  disabled={!value.trim() || isLoading}
@@ -3,7 +3,7 @@ import { ScrollArea } from '@nextsparkjs/core/components/ui/scroll-area'
3
3
  import { Message as MessageType } from '../../lib/hooks/useAiChat'
4
4
  import { Message } from './Message'
5
5
  import { TypingIndicator } from './TypingIndicator'
6
- import { createCyId } from '@nextsparkjs/testing/utils'
6
+ import { sel } from '@nextsparkjs/core/selectors'
7
7
 
8
8
  interface MessageListProps {
9
9
  messages: MessageType[]
@@ -21,11 +21,11 @@ export function MessageList({ messages, isLoading, error }: MessageListProps) {
21
21
  }, [messages, isLoading])
22
22
 
23
23
  return (
24
- <ScrollArea data-cy={createCyId('ai-chat', 'message-list')} className="flex-1 p-4">
24
+ <ScrollArea data-cy={sel('common.aiChat.messageList')} className="flex-1 p-4">
25
25
  <div className="flex flex-col gap-4">
26
26
  {error && (
27
27
  <div
28
- data-cy={createCyId('ai-chat', 'error-message')}
28
+ data-cy={sel('common.aiChat.errorMessage')}
29
29
  className="p-4 mb-2 text-sm text-red-600 bg-red-50 dark:bg-red-900/20 dark:text-red-400 rounded-lg"
30
30
  >
31
31
  {error}
@@ -1,8 +1,8 @@
1
- import { createCyId } from '@nextsparkjs/testing/utils'
1
+ import { sel } from '@nextsparkjs/core/selectors'
2
2
 
3
3
  export function TypingIndicator() {
4
4
  return (
5
- <div data-cy={createCyId('ai-chat', 'typing-indicator')} className="flex items-center gap-1 p-2">
5
+ <div data-cy={sel('common.aiChat.typingIndicator')} className="flex items-center gap-1 p-2">
6
6
  <div className="w-2 h-2 bg-muted-foreground/40 rounded-full animate-bounce [animation-delay:-0.3s]" />
7
7
  <div className="w-2 h-2 bg-muted-foreground/40 rounded-full animate-bounce [animation-delay:-0.15s]" />
8
8
  <div className="w-2 h-2 bg-muted-foreground/40 rounded-full animate-bounce" />
@@ -0,0 +1,107 @@
1
+ # Customers API
2
+
3
+ Manage customer records in the system. This endpoint supports full CRUD operations with team-based multi-tenancy.
4
+
5
+ ## Overview
6
+
7
+ The Customers API allows you to create, read, update, and delete customer records. Each customer belongs to a team and is automatically filtered based on the authenticated user's team context.
8
+
9
+ ## Authentication
10
+
11
+ All endpoints require authentication via:
12
+ - **Session cookie** (for browser-based requests)
13
+ - **API Key** header (for server-to-server requests)
14
+
15
+ ## Endpoints
16
+
17
+ ### List Customers
18
+ `GET /api/v1/customers`
19
+
20
+ Returns a paginated list of customers for the current team.
21
+
22
+ **Query Parameters:**
23
+ - `limit` (number, optional): Maximum records to return. Default: 20
24
+ - `offset` (number, optional): Number of records to skip. Default: 0
25
+ - `status` (string, optional): Filter by status (active, inactive, pending)
26
+ - `search` (string, optional): Search term for partial matching
27
+ - `searchField` (string, optional): Field to search in (name, email)
28
+ - `sortBy` (string, optional): Field to sort by
29
+ - `sortOrder` (string, optional): Sort direction (asc, desc)
30
+
31
+ **Example Response:**
32
+ ```json
33
+ {
34
+ "data": [
35
+ {
36
+ "id": "cust_123",
37
+ "name": "John Doe",
38
+ "email": "john@example.com",
39
+ "status": "active",
40
+ "createdAt": "2024-01-15T10:30:00Z"
41
+ }
42
+ ],
43
+ "pagination": {
44
+ "total": 42,
45
+ "limit": 20,
46
+ "offset": 0
47
+ }
48
+ }
49
+ ```
50
+
51
+ ### Get Single Customer
52
+ `GET /api/v1/customers/[id]`
53
+
54
+ Returns a single customer by ID.
55
+
56
+ **Path Parameters:**
57
+ - `id` (string, required): Customer ID
58
+
59
+ ### Create Customer
60
+ `POST /api/v1/customers`
61
+
62
+ Create a new customer record.
63
+
64
+ **Request Body:**
65
+ ```json
66
+ {
67
+ "name": "Jane Smith",
68
+ "email": "jane@example.com",
69
+ "phone": "+1234567890",
70
+ "status": "active"
71
+ }
72
+ ```
73
+
74
+ ### Update Customer
75
+ `PATCH /api/v1/customers/[id]`
76
+
77
+ Update an existing customer.
78
+
79
+ **Path Parameters:**
80
+ - `id` (string, required): Customer ID
81
+
82
+ **Request Body:**
83
+ Any fields to update (partial update supported).
84
+
85
+ ### Delete Customer
86
+ `DELETE /api/v1/customers/[id]`
87
+
88
+ Delete a customer record.
89
+
90
+ **Path Parameters:**
91
+ - `id` (string, required): Customer ID
92
+
93
+ ## Admin Bypass
94
+
95
+ Superadmin users can access customers across all teams by sending:
96
+ - Header: `x-admin-bypass: confirm-cross-team-access`
97
+ - Optional header: `x-team-id: {team_id}` to filter a specific team
98
+
99
+ ## Error Responses
100
+
101
+ | Status | Description |
102
+ |--------|-------------|
103
+ | 400 | Bad Request - Invalid parameters |
104
+ | 401 | Unauthorized - Missing or invalid auth |
105
+ | 403 | Forbidden - Insufficient permissions |
106
+ | 404 | Not Found - Customer doesn't exist |
107
+ | 422 | Validation Error - Invalid data |
@@ -0,0 +1,80 @@
1
+ /**
2
+ * API Presets for Customers Entity
3
+ *
4
+ * These presets appear in the DevTools API Explorer's "Presets" tab.
5
+ * The endpoint is automatically derived from the entity name: /api/v1/customers
6
+ */
7
+
8
+ import { defineApiEndpoint } from '@nextsparkjs/core/types/api-presets'
9
+
10
+ export default defineApiEndpoint({
11
+ // endpoint is optional - derived from entity folder name
12
+ summary: 'Manage customer records in the system',
13
+ presets: [
14
+ {
15
+ id: 'list-all',
16
+ title: 'List All Customers',
17
+ description: 'Fetch all customers with default pagination',
18
+ method: 'GET',
19
+ params: {
20
+ limit: 10,
21
+ offset: 0
22
+ },
23
+ tags: ['read', 'list']
24
+ },
25
+ {
26
+ id: 'list-by-status',
27
+ title: 'List Active Customers',
28
+ description: 'Fetch only customers with active status',
29
+ method: 'GET',
30
+ params: {
31
+ status: 'active',
32
+ limit: 20
33
+ },
34
+ tags: ['read', 'filter']
35
+ },
36
+ {
37
+ id: 'create-test',
38
+ title: 'Create Test Customer',
39
+ description: 'Create a new customer with sample data for testing',
40
+ method: 'POST',
41
+ payload: {
42
+ name: 'Test Customer',
43
+ email: 'test@example.com',
44
+ phone: '+1234567890',
45
+ status: 'active'
46
+ },
47
+ sessionConfig: {
48
+ crossTeam: false
49
+ },
50
+ tags: ['write', 'create']
51
+ },
52
+ {
53
+ id: 'create-bypass',
54
+ title: 'Create (Bypass Mode)',
55
+ description: 'Create customer using admin bypass for cross-team access',
56
+ method: 'POST',
57
+ payload: {
58
+ name: 'Cross-Team Customer',
59
+ email: 'crossteam@example.com',
60
+ status: 'pending'
61
+ },
62
+ sessionConfig: {
63
+ crossTeam: true,
64
+ teamId: '{{FIRST_TEAM_ID}}'
65
+ },
66
+ tags: ['write', 'admin', 'bypass']
67
+ },
68
+ {
69
+ id: 'search-name',
70
+ title: 'Search by Name',
71
+ description: 'Search customers by name using partial match',
72
+ method: 'GET',
73
+ params: {
74
+ search: 'John',
75
+ searchField: 'name'
76
+ },
77
+ tags: ['read', 'search']
78
+ }
79
+ ]
80
+ })
@@ -0,0 +1,114 @@
1
+ # Pages API
2
+
3
+ Manage builder-enabled pages in the system. Pages render at root URLs (e.g., /about, /contact) and support the page builder for content creation.
4
+
5
+ ## Overview
6
+
7
+ The Pages API allows you to create, read, update, and delete pages with builder content. Pages are public content and can be accessed by all authenticated users.
8
+
9
+ ## Authentication
10
+
11
+ All endpoints require authentication via:
12
+ - **Session cookie** (for browser-based requests)
13
+ - **API Key** header (for server-to-server requests)
14
+
15
+ ## Endpoints
16
+
17
+ ### List Pages
18
+ `GET /api/v1/pages`
19
+
20
+ Returns a paginated list of pages.
21
+
22
+ **Query Parameters:**
23
+ - `limit` (number, optional): Maximum records to return. Default: 20
24
+ - `offset` (number, optional): Number of records to skip. Default: 0
25
+ - `status` (string, optional): Filter by status (draft, published, scheduled, archived)
26
+ - `locale` (string, optional): Filter by locale (en, es)
27
+ - `search` (string, optional): Search term for title/slug
28
+ - `sortBy` (string, optional): Field to sort by
29
+ - `sortOrder` (string, optional): Sort direction (asc, desc)
30
+
31
+ **Example Response:**
32
+ ```json
33
+ {
34
+ "data": [
35
+ {
36
+ "id": "page_123",
37
+ "title": "About Us",
38
+ "slug": "about",
39
+ "status": "published",
40
+ "locale": "en",
41
+ "blocks": [...],
42
+ "createdAt": "2024-01-15T10:30:00Z"
43
+ }
44
+ ],
45
+ "pagination": {
46
+ "total": 15,
47
+ "limit": 20,
48
+ "offset": 0
49
+ }
50
+ }
51
+ ```
52
+
53
+ ### Get Single Page
54
+ `GET /api/v1/pages/[id]`
55
+
56
+ Returns a single page by ID.
57
+
58
+ **Path Parameters:**
59
+ - `id` (string, required): Page ID
60
+
61
+ ### Create Page
62
+ `POST /api/v1/pages`
63
+
64
+ Create a new page record.
65
+
66
+ **Request Body:**
67
+ ```json
68
+ {
69
+ "title": "Contact Us",
70
+ "slug": "contact",
71
+ "status": "draft",
72
+ "locale": "en",
73
+ "blocks": []
74
+ }
75
+ ```
76
+
77
+ ### Update Page
78
+ `PATCH /api/v1/pages/[id]`
79
+
80
+ Update an existing page.
81
+
82
+ **Path Parameters:**
83
+ - `id` (string, required): Page ID
84
+
85
+ **Request Body:**
86
+ Any fields to update (partial update supported).
87
+
88
+ ### Delete Page
89
+ `DELETE /api/v1/pages/[id]`
90
+
91
+ Delete a page record.
92
+
93
+ **Path Parameters:**
94
+ - `id` (string, required): Page ID
95
+
96
+ ## Fields
97
+
98
+ | Field | Type | Required | Description |
99
+ |-------|------|----------|-------------|
100
+ | title | text | Yes | Page title |
101
+ | slug | text | Yes | URL-friendly identifier |
102
+ | status | select | Yes | draft, published, scheduled, archived |
103
+ | locale | select | Yes | en, es |
104
+ | blocks | json | No | Page builder content |
105
+
106
+ ## Error Responses
107
+
108
+ | Status | Description |
109
+ |--------|-------------|
110
+ | 400 | Bad Request - Invalid parameters |
111
+ | 401 | Unauthorized - Missing or invalid auth |
112
+ | 403 | Forbidden - Insufficient permissions |
113
+ | 404 | Not Found - Page doesn't exist |
114
+ | 422 | Validation Error - Invalid data |
@@ -0,0 +1,72 @@
1
+ /**
2
+ * API Presets for Pages Entity
3
+ *
4
+ * These presets appear in the DevTools API Explorer's "Presets" tab.
5
+ * The endpoint is automatically derived from the entity name: /api/v1/pages
6
+ */
7
+
8
+ import { defineApiEndpoint } from '@nextsparkjs/core/types/api-presets'
9
+
10
+ export default defineApiEndpoint({
11
+ summary: 'Manage builder-enabled pages',
12
+ presets: [
13
+ {
14
+ id: 'list-all',
15
+ title: 'List All Pages',
16
+ description: 'Fetch all pages with default pagination',
17
+ method: 'GET',
18
+ params: {
19
+ limit: 10,
20
+ offset: 0
21
+ },
22
+ tags: ['read', 'list']
23
+ },
24
+ {
25
+ id: 'list-published',
26
+ title: 'List Published Pages',
27
+ description: 'Fetch only published pages',
28
+ method: 'GET',
29
+ params: {
30
+ status: 'published',
31
+ limit: 20
32
+ },
33
+ tags: ['read', 'filter']
34
+ },
35
+ {
36
+ id: 'list-by-locale',
37
+ title: 'List Spanish Pages',
38
+ description: 'Fetch pages in Spanish locale',
39
+ method: 'GET',
40
+ params: {
41
+ locale: 'es',
42
+ limit: 10
43
+ },
44
+ tags: ['read', 'filter', 'i18n']
45
+ },
46
+ {
47
+ id: 'create-draft',
48
+ title: 'Create Draft Page',
49
+ description: 'Create a new page as draft',
50
+ method: 'POST',
51
+ payload: {
52
+ title: 'New Page',
53
+ slug: 'new-page',
54
+ status: 'draft',
55
+ locale: 'en',
56
+ blocks: []
57
+ },
58
+ tags: ['write', 'create']
59
+ },
60
+ {
61
+ id: 'search-title',
62
+ title: 'Search by Title',
63
+ description: 'Search pages by title',
64
+ method: 'GET',
65
+ params: {
66
+ search: 'About',
67
+ searchField: 'title'
68
+ },
69
+ tags: ['read', 'search']
70
+ }
71
+ ]
72
+ })
@@ -0,0 +1,120 @@
1
+ # Posts API
2
+
3
+ Manage blog posts with builder support and taxonomies. Posts render at /blog/[slug] and support categories, featured images, and excerpts.
4
+
5
+ ## Overview
6
+
7
+ The Posts API allows you to create, read, update, and delete blog posts with builder content. Posts support the page builder for content creation and include taxonomy management for categories.
8
+
9
+ ## Authentication
10
+
11
+ All endpoints require authentication via:
12
+ - **Session cookie** (for browser-based requests)
13
+ - **API Key** header (for server-to-server requests)
14
+
15
+ ## Endpoints
16
+
17
+ ### List Posts
18
+ `GET /api/v1/posts`
19
+
20
+ Returns a paginated list of posts.
21
+
22
+ **Query Parameters:**
23
+ - `limit` (number, optional): Maximum records to return. Default: 20
24
+ - `offset` (number, optional): Number of records to skip. Default: 0
25
+ - `status` (string, optional): Filter by status (draft, published, scheduled, archived)
26
+ - `search` (string, optional): Search term for title/excerpt
27
+ - `sortBy` (string, optional): Field to sort by
28
+ - `sortOrder` (string, optional): Sort direction (asc, desc)
29
+
30
+ **Example Response:**
31
+ ```json
32
+ {
33
+ "data": [
34
+ {
35
+ "id": "post_123",
36
+ "title": "Getting Started with NextSpark",
37
+ "slug": "getting-started",
38
+ "status": "published",
39
+ "excerpt": "Learn how to build amazing apps with NextSpark",
40
+ "featuredImage": "/uploads/hero.jpg",
41
+ "blocks": [...],
42
+ "createdAt": "2024-01-15T10:30:00Z"
43
+ }
44
+ ],
45
+ "pagination": {
46
+ "total": 42,
47
+ "limit": 20,
48
+ "offset": 0
49
+ }
50
+ }
51
+ ```
52
+
53
+ ### Get Single Post
54
+ `GET /api/v1/posts/[id]`
55
+
56
+ Returns a single post by ID.
57
+
58
+ **Path Parameters:**
59
+ - `id` (string, required): Post ID
60
+
61
+ ### Create Post
62
+ `POST /api/v1/posts`
63
+
64
+ Create a new post record.
65
+
66
+ **Request Body:**
67
+ ```json
68
+ {
69
+ "title": "My New Blog Post",
70
+ "slug": "my-new-blog-post",
71
+ "status": "draft",
72
+ "excerpt": "A brief introduction to my post",
73
+ "blocks": []
74
+ }
75
+ ```
76
+
77
+ ### Update Post
78
+ `PATCH /api/v1/posts/[id]`
79
+
80
+ Update an existing post.
81
+
82
+ **Path Parameters:**
83
+ - `id` (string, required): Post ID
84
+
85
+ **Request Body:**
86
+ Any fields to update (partial update supported).
87
+
88
+ ### Delete Post
89
+ `DELETE /api/v1/posts/[id]`
90
+
91
+ Delete a post record.
92
+
93
+ **Path Parameters:**
94
+ - `id` (string, required): Post ID
95
+
96
+ ## Fields
97
+
98
+ | Field | Type | Required | Description |
99
+ |-------|------|----------|-------------|
100
+ | title | text | Yes | Post title |
101
+ | slug | text | Yes | URL-friendly identifier |
102
+ | status | select | Yes | draft, published, scheduled, archived |
103
+ | excerpt | textarea | No | Short summary of the post |
104
+ | featuredImage | image | No | Main image for the post |
105
+ | blocks | json | No | Page builder content |
106
+
107
+ ## Taxonomies
108
+
109
+ Posts support the following taxonomies:
110
+ - **categories** (post_category): Multiple categories can be assigned
111
+
112
+ ## Error Responses
113
+
114
+ | Status | Description |
115
+ |--------|-------------|
116
+ | 400 | Bad Request - Invalid parameters |
117
+ | 401 | Unauthorized - Missing or invalid auth |
118
+ | 403 | Forbidden - Insufficient permissions |
119
+ | 404 | Not Found - Post doesn't exist |
120
+ | 422 | Validation Error - Invalid data |