@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.
- package/components/ai-chat/ChatPanel.tsx +7 -7
- package/components/ai-chat/Message.tsx +2 -2
- package/components/ai-chat/MessageInput.tsx +3 -3
- package/components/ai-chat/MessageList.tsx +3 -3
- package/components/ai-chat/TypingIndicator.tsx +2 -2
- package/entities/customers/api/docs.md +107 -0
- package/entities/customers/api/presets.ts +80 -0
- package/entities/pages/api/docs.md +114 -0
- package/entities/pages/api/presets.ts +72 -0
- package/entities/posts/api/docs.md +120 -0
- package/entities/posts/api/presets.ts +74 -0
- package/entities/tasks/api/docs.md +126 -0
- package/entities/tasks/api/presets.ts +84 -0
- package/lib/selectors.ts +2 -2
- package/messages/de/admin.json +45 -0
- package/messages/en/admin.json +45 -0
- package/messages/es/admin.json +45 -0
- package/messages/fr/admin.json +45 -0
- package/messages/it/admin.json +45 -0
- package/messages/pt/admin.json +45 -0
- package/package.json +3 -3
- package/styles/globals.css +24 -0
- package/tests/cypress/e2e/_utils/selectors/block-editor.bdd.md +491 -0
- package/tests/cypress/e2e/_utils/selectors/block-editor.cy.ts +475 -0
- package/tests/cypress/e2e/_utils/selectors/dashboard-container.cy.ts +52 -0
- package/tests/cypress/e2e/_utils/selectors/dashboard-mobile.cy.ts +14 -14
- package/tests/cypress/e2e/_utils/selectors/dashboard-navigation.cy.ts +3 -3
- package/tests/cypress/e2e/_utils/selectors/dashboard-sidebar.bdd.md +38 -73
- package/tests/cypress/e2e/_utils/selectors/dashboard-sidebar.cy.ts +21 -42
- package/tests/cypress/e2e/_utils/selectors/dashboard-topnav.bdd.md +117 -38
- package/tests/cypress/e2e/_utils/selectors/dashboard-topnav.cy.ts +35 -12
- package/tests/cypress/e2e/_utils/selectors/settings-layout.bdd.md +50 -59
- package/tests/cypress/e2e/_utils/selectors/settings-layout.cy.ts +15 -23
- package/tests/cypress/e2e/_utils/selectors/tasks.bdd.md +395 -155
- package/tests/cypress/e2e/_utils/selectors/tasks.cy.ts +795 -174
- package/tests/cypress/e2e/api/_core/teams/teams-security.cy.ts +415 -0
- package/tests/cypress/e2e/uat/_core/teams/inline-edit.cy.ts +278 -0
- package/tests/cypress/src/core/BlockEditorBasePOM.ts +269 -99
- package/tests/cypress/src/core/DashboardEntityPOM.ts +1 -1
- package/tests/cypress/src/features/DashboardPOM.ts +49 -28
- package/tests/cypress/src/features/PageBuilderPOM.ts +20 -0
- package/tests/cypress/src/features/SettingsPOM.ts +511 -166
- package/tests/cypress/src/features/SuperadminPOM.ts +679 -159
- package/tests/cypress/src/features/index.ts +10 -10
- package/tests/cypress/e2e/_utils/selectors/pages-editor.bdd.md +0 -207
- package/tests/cypress/e2e/_utils/selectors/pages-editor.cy.ts +0 -211
- package/tests/cypress/e2e/_utils/selectors/posts-editor.bdd.md +0 -184
- 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 {
|
|
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={
|
|
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={
|
|
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={
|
|
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={
|
|
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={
|
|
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=
|
|
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 {
|
|
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={
|
|
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 {
|
|
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={
|
|
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={
|
|
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 {
|
|
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={
|
|
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={
|
|
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 {
|
|
1
|
+
import { sel } from '@nextsparkjs/core/selectors'
|
|
2
2
|
|
|
3
3
|
export function TypingIndicator() {
|
|
4
4
|
return (
|
|
5
|
-
<div data-cy={
|
|
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 |
|