@back23/promptly-sdk 2.12.0 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +206 -908
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @back23/promptly-sdk
|
|
2
2
|
|
|
3
|
-
Promptly AI CMS
|
|
3
|
+
Official SDK for Promptly AI CMS - A headless CMS with built-in AI capabilities.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -14,666 +14,234 @@ npm install @back23/promptly-sdk
|
|
|
14
14
|
import { Promptly } from '@back23/promptly-sdk';
|
|
15
15
|
|
|
16
16
|
const client = new Promptly({
|
|
17
|
-
tenantId: '
|
|
18
|
-
apiKey: '
|
|
17
|
+
tenantId: 'your-tenant-id',
|
|
18
|
+
apiKey: 'pky_xxxxxxxxxxxxxxxx', // Required - Get from Dashboard > Settings > API Tokens
|
|
19
19
|
baseUrl: 'https://promptly.webbyon.com', // Optional
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
-
//
|
|
23
|
-
const { data: posts } = await client.blog.list();
|
|
24
|
-
const products = await client.shop.listProducts();
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## v2.11.0 Changes
|
|
28
|
-
|
|
29
|
-
### Blog API Enhancement
|
|
30
|
-
|
|
31
|
-
Added missing fields to blog post responses and new endpoints for categories/tags.
|
|
32
|
-
|
|
33
|
-
**New API Endpoints:**
|
|
34
|
-
- `GET /api/{tenant}/blog/categories` - List all blog categories
|
|
35
|
-
- `GET /api/{tenant}/blog/tags` - List all blog tags
|
|
36
|
-
|
|
37
|
-
**BlogPost Type Updated:**
|
|
38
|
-
```typescript
|
|
39
|
-
interface BlogPost {
|
|
40
|
-
id: number;
|
|
41
|
-
title: string;
|
|
42
|
-
slug: string;
|
|
43
|
-
excerpt: string;
|
|
44
|
-
featured_image: string | null;
|
|
45
|
-
category: string | null; // NEW
|
|
46
|
-
tags: string[]; // NEW
|
|
47
|
-
author: string;
|
|
48
|
-
views: number; // NEW
|
|
49
|
-
status: string;
|
|
50
|
-
published_at: string | null; // NEW
|
|
51
|
-
created_at: string;
|
|
52
|
-
content?: string; // Detail only
|
|
53
|
-
updated_at?: string; // Detail only
|
|
54
|
-
}
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
**SDK Usage:**
|
|
58
|
-
```typescript
|
|
59
|
-
// Get all categories
|
|
60
|
-
const categories = await client.blog.categories();
|
|
61
|
-
// Returns: string[]
|
|
62
|
-
|
|
63
|
-
// Get all tags
|
|
64
|
-
const tags = await client.blog.tags();
|
|
65
|
-
// Returns: string[]
|
|
66
|
-
|
|
67
|
-
// Filter by category
|
|
68
|
-
const { data: posts } = await client.blog.byCategory('tech');
|
|
69
|
-
|
|
70
|
-
// Filter by tag
|
|
71
|
-
const { data: posts } = await client.blog.byTag('laravel');
|
|
72
|
-
|
|
73
|
-
// Access new fields
|
|
74
|
-
const { data: posts } = await client.blog.list();
|
|
75
|
-
posts.forEach(post => {
|
|
76
|
-
console.log(post.category); // 'tech' | null
|
|
77
|
-
console.log(post.tags); // ['laravel', 'php']
|
|
78
|
-
console.log(post.views); // 1234
|
|
79
|
-
console.log(post.published_at); // '2026-01-06T12:00:00+00:00'
|
|
80
|
-
});
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
---
|
|
84
|
-
|
|
85
|
-
## v2.5.0 Changes
|
|
86
|
-
|
|
87
|
-
### BoardPost Type Extension
|
|
22
|
+
// Fetch blog posts
|
|
23
|
+
const { data: posts, meta } = await client.blog.list();
|
|
88
24
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
```typescript
|
|
92
|
-
interface BoardPost {
|
|
93
|
-
// ... existing fields
|
|
94
|
-
is_notice: boolean; // Whether it's a notice
|
|
95
|
-
is_secret: boolean; // Whether it's a secret post
|
|
96
|
-
is_mine: boolean; // Whether current logged-in user is the author
|
|
97
|
-
}
|
|
25
|
+
// Fetch products
|
|
26
|
+
const { data: products } = await client.shop.listProducts();
|
|
98
27
|
```
|
|
99
28
|
|
|
100
|
-
|
|
29
|
+
## Authentication
|
|
101
30
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
### Dual Authentication Support Fixed
|
|
105
|
-
|
|
106
|
-
Fixed issue where API Key and Bearer Token are now sent simultaneously. In previous versions, when API Key was set, the Authorization header was ignored, causing member-only API calls to fail after login.
|
|
31
|
+
All API requests require an API key. Get yours from **Dashboard > Settings > API Tokens**.
|
|
107
32
|
|
|
108
33
|
```typescript
|
|
109
|
-
// API Key + Bearer Token now work together
|
|
110
34
|
const client = new Promptly({
|
|
111
35
|
tenantId: 'demo',
|
|
112
|
-
apiKey: '
|
|
36
|
+
apiKey: 'pky_xxxxxxxxxxxxxxxx', // Required
|
|
113
37
|
});
|
|
114
|
-
|
|
115
|
-
// Set token after login
|
|
116
|
-
await client.auth.login({ email, password });
|
|
117
|
-
|
|
118
|
-
// Member-only APIs work correctly (Authorization: Bearer xxx header included)
|
|
119
|
-
const profile = await client.members.getProfile();
|
|
120
|
-
const orders = await client.orders.list();
|
|
121
38
|
```
|
|
122
39
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
## v2.3.0 Changes
|
|
126
|
-
|
|
127
|
-
### Polymorphic Comments API
|
|
128
|
-
|
|
129
|
-
The comment system has been redesigned to support various use cases including boards, blogs, and guestbooks (standalone pages).
|
|
40
|
+
For member-specific operations (orders, profile, etc.), authenticate first:
|
|
130
41
|
|
|
131
42
|
```typescript
|
|
132
|
-
//
|
|
133
|
-
|
|
134
|
-
await client.comments.createBoardPost(postId, {
|
|
135
|
-
author_name: 'John Doe',
|
|
136
|
-
content: 'Comment content',
|
|
137
|
-
password: '1234', // For guest comments
|
|
138
|
-
});
|
|
43
|
+
// Login
|
|
44
|
+
await client.auth.login({ email: 'user@example.com', password: 'password' });
|
|
139
45
|
|
|
140
|
-
//
|
|
141
|
-
const
|
|
142
|
-
await client.
|
|
143
|
-
author_name: 'Jane',
|
|
144
|
-
content: 'Great post!',
|
|
145
|
-
});
|
|
46
|
+
// Now member-only APIs work
|
|
47
|
+
const profile = await client.members.getProfile();
|
|
48
|
+
const orders = await client.orders.list();
|
|
146
49
|
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
await client.comments.createStandalone('guestbook', {
|
|
150
|
-
author_name: 'Visitor',
|
|
151
|
-
content: 'Hello!',
|
|
152
|
-
});
|
|
50
|
+
// Check auth status
|
|
51
|
+
client.isAuthenticated(); // true
|
|
153
52
|
|
|
154
|
-
//
|
|
155
|
-
await client.
|
|
156
|
-
await client.comments.delete(commentId, { password: '1234' });
|
|
157
|
-
await client.comments.like(commentId);
|
|
53
|
+
// Logout
|
|
54
|
+
await client.auth.logout();
|
|
158
55
|
```
|
|
159
56
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
## v2.2.0 Changes
|
|
163
|
-
|
|
164
|
-
### Entity Definition CRUD
|
|
57
|
+
## API Reference
|
|
165
58
|
|
|
166
|
-
|
|
59
|
+
### Blog
|
|
167
60
|
|
|
168
61
|
```typescript
|
|
169
|
-
//
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
{ name: 'company', label: 'Company Name', type: 'text', required: true },
|
|
177
|
-
{ name: 'email', label: 'Email', type: 'email', required: true },
|
|
178
|
-
{ name: 'status', label: 'Status', type: 'select', options: [
|
|
179
|
-
{ value: 'active', label: 'Active' },
|
|
180
|
-
{ value: 'inactive', label: 'Inactive' }
|
|
181
|
-
]}
|
|
182
|
-
]
|
|
183
|
-
},
|
|
184
|
-
icon: 'users'
|
|
62
|
+
// List posts
|
|
63
|
+
const { data, meta } = await client.blog.list({
|
|
64
|
+
page: 1,
|
|
65
|
+
per_page: 10,
|
|
66
|
+
category: 'tech', // Optional filter
|
|
67
|
+
tag: 'laravel', // Optional filter
|
|
68
|
+
search: 'keyword', // Optional search
|
|
185
69
|
});
|
|
186
70
|
|
|
187
|
-
// Get
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
// Update entity definition
|
|
191
|
-
await client.entities.update('customers', { name: 'Clients' });
|
|
192
|
-
|
|
193
|
-
// Delete entity definition (requires force if records exist)
|
|
194
|
-
await client.entities.delete('customers', true);
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
### Record API Path Change
|
|
198
|
-
|
|
199
|
-
The record API path has changed:
|
|
200
|
-
|
|
201
|
-
```typescript
|
|
202
|
-
// Before v2.1.0
|
|
203
|
-
await client.entities.createRecord('customers', { data: { company: 'ACME' } });
|
|
204
|
-
|
|
205
|
-
// v2.2.0 and later - no data wrapper needed
|
|
206
|
-
await client.entities.createRecord('customers', { company: 'ACME' });
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
---
|
|
210
|
-
|
|
211
|
-
## v2.0.0 Breaking Changes
|
|
71
|
+
// Get single post
|
|
72
|
+
const post = await client.blog.get('post-slug');
|
|
212
73
|
|
|
213
|
-
|
|
74
|
+
// Get featured posts
|
|
75
|
+
const featured = await client.blog.featured(5);
|
|
214
76
|
|
|
215
|
-
|
|
77
|
+
// Filter by category/tag
|
|
78
|
+
const { data } = await client.blog.byCategory('news');
|
|
79
|
+
const { data } = await client.blog.byTag('featured');
|
|
216
80
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const
|
|
220
|
-
tenantId: 'demo',
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
// ✅ v2.0 - apiKey is REQUIRED
|
|
224
|
-
const client = new Promptly({
|
|
225
|
-
tenantId: 'demo',
|
|
226
|
-
apiKey: 'pky_xxxxxxxxxxxxxxxxxxxxxxxx',
|
|
227
|
-
});
|
|
81
|
+
// Get all categories and tags
|
|
82
|
+
const categories = await client.blog.categories(); // string[]
|
|
83
|
+
const tags = await client.blog.tags(); // string[]
|
|
228
84
|
```
|
|
229
85
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
The SDK automatically includes the `X-API-Key` header in all requests.
|
|
233
|
-
|
|
234
|
-
---
|
|
235
|
-
|
|
236
|
-
## v1.3.0 Changes
|
|
237
|
-
|
|
238
|
-
### Unified Response Structure
|
|
239
|
-
|
|
240
|
-
All list APIs now return a consistent `ListResponse<T>` format:
|
|
241
|
-
|
|
86
|
+
**BlogPost Type:**
|
|
242
87
|
```typescript
|
|
243
|
-
interface
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
88
|
+
interface BlogPost {
|
|
89
|
+
id: number;
|
|
90
|
+
title: string;
|
|
91
|
+
slug: string;
|
|
92
|
+
excerpt: string;
|
|
93
|
+
featured_image: string | null;
|
|
94
|
+
category: string | null;
|
|
95
|
+
tags: string[];
|
|
96
|
+
author: string;
|
|
97
|
+
views: number;
|
|
98
|
+
status: string;
|
|
99
|
+
published_at: string | null;
|
|
100
|
+
created_at: string;
|
|
101
|
+
content?: string; // Detail view only
|
|
102
|
+
updated_at?: string; // Detail view only
|
|
253
103
|
}
|
|
254
104
|
```
|
|
255
105
|
|
|
256
|
-
**No more defensive code needed:**
|
|
257
|
-
|
|
258
|
-
```typescript
|
|
259
|
-
// Before (v1.1.0)
|
|
260
|
-
const posts = await client.blog.list();
|
|
261
|
-
const items = posts?.data ?? []; // Defensive check needed
|
|
262
|
-
|
|
263
|
-
// After (v1.3.0)
|
|
264
|
-
const { data, meta } = await client.blog.list();
|
|
265
|
-
data.map(post => ...); // data is always an array
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
## API Overview
|
|
269
|
-
|
|
270
|
-
> **Note:** All APIs require API key (v2.0+). "Auth Required" means additional member login token is needed.
|
|
271
|
-
|
|
272
|
-
| Resource | Read Operations | Write Operations (Auth Required) |
|
|
273
|
-
|----------|-----------------|----------------------------------|
|
|
274
|
-
| **Boards** | list, get, listPosts, getPost | createPost, updatePost, deletePost |
|
|
275
|
-
| **Blog** | list, get, featured, byCategory, byTag | - |
|
|
276
|
-
| **Comments** | boardPost, blogPost, standalone | createBoardPost, createBlogPost, createStandalone, update, delete, like |
|
|
277
|
-
| **Shop** | listProducts, getProduct, listCategories | getCart, addToCart, listOrders, createOrder |
|
|
278
|
-
| **Forms** | list, get, submit | mySubmissions |
|
|
279
|
-
| **Auth** | login, register | logout, me, updateProfile |
|
|
280
|
-
| **Media** | - | upload, list, delete |
|
|
281
|
-
| **Entities** | list, get, listRecords, getRecord | create, update, delete, createRecord, updateRecord, deleteRecord |
|
|
282
|
-
| **Reservation** | getSettings, listServices, listStaff, getAvailableDates, getAvailableSlots | create, list, get, cancel |
|
|
283
|
-
|
|
284
|
-
## API Reference
|
|
285
|
-
|
|
286
106
|
### Boards
|
|
287
107
|
|
|
288
108
|
```typescript
|
|
289
109
|
// List boards
|
|
290
|
-
const { data: boards
|
|
291
|
-
// Returns: ListResponse<Board>
|
|
110
|
+
const { data: boards } = await client.boards.list();
|
|
292
111
|
|
|
293
|
-
// Get board
|
|
294
|
-
const board = await client.boards.get('
|
|
295
|
-
// Returns: Board
|
|
112
|
+
// Get board
|
|
113
|
+
const board = await client.boards.get('board-slug');
|
|
296
114
|
|
|
297
|
-
// List
|
|
298
|
-
const { data: posts
|
|
115
|
+
// List posts
|
|
116
|
+
const { data: posts } = await client.boards.listPosts('board-slug', {
|
|
299
117
|
page: 1,
|
|
300
118
|
per_page: 10,
|
|
301
|
-
search: 'keyword',
|
|
119
|
+
search: 'keyword',
|
|
302
120
|
});
|
|
303
|
-
// Returns: ListResponse<BoardPost>
|
|
304
|
-
|
|
305
|
-
// Get post details
|
|
306
|
-
const post = await client.boards.getPost(1);
|
|
307
|
-
// Returns: BoardPost
|
|
308
121
|
|
|
309
|
-
//
|
|
310
|
-
const
|
|
311
|
-
// Returns: BoardComment[] (always an array)
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
### Posts & Comments - Protected (Login Required)
|
|
122
|
+
// Get post
|
|
123
|
+
const post = await client.boards.getPost(postId);
|
|
315
124
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
await client.auth.login({ email: 'user@example.com', password: 'password' });
|
|
319
|
-
|
|
320
|
-
// Create post
|
|
321
|
-
const newPost = await client.boards.createPost({
|
|
125
|
+
// Create post (auth required)
|
|
126
|
+
await client.boards.createPost({
|
|
322
127
|
board_id: 1,
|
|
323
128
|
title: 'Title',
|
|
324
129
|
content: 'Content',
|
|
325
|
-
is_notice: false,
|
|
326
130
|
});
|
|
327
131
|
|
|
328
|
-
// Update post
|
|
329
|
-
await client.boards.updatePost(postId, {
|
|
330
|
-
title: 'Updated title',
|
|
331
|
-
content: 'Updated content',
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
// Delete post
|
|
132
|
+
// Update/Delete post (auth required)
|
|
133
|
+
await client.boards.updatePost(postId, { title: 'New Title' });
|
|
335
134
|
await client.boards.deletePost(postId);
|
|
336
|
-
|
|
337
|
-
// Create comment
|
|
338
|
-
await client.boards.createComment(postId, {
|
|
339
|
-
content: 'Comment content',
|
|
340
|
-
parent_id: null, // Parent comment ID for replies
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
// Update comment
|
|
344
|
-
await client.boards.updateComment(commentId, {
|
|
345
|
-
content: 'Updated comment',
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
// Delete comment
|
|
349
|
-
await client.boards.deleteComment(commentId);
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
### Blog
|
|
353
|
-
|
|
354
|
-
```typescript
|
|
355
|
-
// List blog posts
|
|
356
|
-
const { data: posts, meta } = await client.blog.list({
|
|
357
|
-
page: 1,
|
|
358
|
-
per_page: 10,
|
|
359
|
-
category: 'news', // optional
|
|
360
|
-
tag: 'featured', // optional
|
|
361
|
-
search: 'keyword', // optional
|
|
362
|
-
});
|
|
363
|
-
// Returns: ListResponse<BlogPost>
|
|
364
|
-
|
|
365
|
-
// Get blog post details
|
|
366
|
-
const post = await client.blog.get('post-slug');
|
|
367
|
-
// Returns: BlogPost
|
|
368
|
-
|
|
369
|
-
// Featured posts
|
|
370
|
-
const featured = await client.blog.featured(5);
|
|
371
|
-
// Returns: BlogPost[] (always an array)
|
|
372
|
-
|
|
373
|
-
// Get posts by category
|
|
374
|
-
const { data: newsPosts } = await client.blog.byCategory('news');
|
|
375
|
-
|
|
376
|
-
// Get posts by tag
|
|
377
|
-
const { data: taggedPosts } = await client.blog.byTag('featured');
|
|
378
|
-
|
|
379
|
-
// List categories
|
|
380
|
-
const categories = await client.blog.categories();
|
|
381
|
-
// Returns: string[] (always an array)
|
|
382
|
-
|
|
383
|
-
// List tags
|
|
384
|
-
const tags = await client.blog.tags();
|
|
385
|
-
// Returns: string[] (always an array)
|
|
386
135
|
```
|
|
387
136
|
|
|
388
137
|
### Comments
|
|
389
138
|
|
|
390
|
-
|
|
391
|
-
- **Board comments** (`board_post`)
|
|
392
|
-
- **Blog comments** (`blog_post`)
|
|
393
|
-
- **Guestbook/Standalone comments** (`page`)
|
|
139
|
+
Supports three comment types: board posts, blog posts, and standalone pages (guestbook).
|
|
394
140
|
|
|
395
141
|
```typescript
|
|
396
|
-
//
|
|
397
|
-
const { data
|
|
398
|
-
page: 1,
|
|
399
|
-
per_page: 20,
|
|
400
|
-
});
|
|
401
|
-
// Returns: ListResponse<Comment>
|
|
402
|
-
|
|
403
|
-
// Create board post comment
|
|
142
|
+
// Board post comments
|
|
143
|
+
const { data } = await client.comments.boardPost(postId);
|
|
404
144
|
await client.comments.createBoardPost(postId, {
|
|
405
|
-
author_name: 'John
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
password: '1234', // For guest comments
|
|
409
|
-
parent_id: null, // Parent comment ID for replies
|
|
410
|
-
is_secret: false, // Secret comment flag
|
|
145
|
+
author_name: 'John',
|
|
146
|
+
content: 'Great post!',
|
|
147
|
+
password: '1234', // For guest comments
|
|
411
148
|
});
|
|
412
149
|
|
|
413
|
-
//
|
|
414
|
-
const { data
|
|
415
|
-
// Returns: ListResponse<Comment>
|
|
416
|
-
|
|
417
|
-
// Create blog post comment
|
|
150
|
+
// Blog post comments
|
|
151
|
+
const { data } = await client.comments.blogPost('post-slug');
|
|
418
152
|
await client.comments.createBlogPost('post-slug', {
|
|
419
153
|
author_name: 'Jane',
|
|
420
|
-
content: '
|
|
154
|
+
content: 'Nice article!',
|
|
421
155
|
});
|
|
422
156
|
|
|
423
|
-
//
|
|
424
|
-
const { data
|
|
425
|
-
// Returns: ListResponse<Comment>
|
|
426
|
-
|
|
427
|
-
// Create guestbook comment
|
|
157
|
+
// Standalone comments (guestbook)
|
|
158
|
+
const { data } = await client.comments.standalone('guestbook');
|
|
428
159
|
await client.comments.createStandalone('guestbook', {
|
|
429
160
|
author_name: 'Visitor',
|
|
430
161
|
content: 'Hello!',
|
|
431
162
|
});
|
|
432
163
|
|
|
433
|
-
//
|
|
434
|
-
await client.comments.update(commentId, {
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
});
|
|
438
|
-
|
|
439
|
-
// Delete comment
|
|
440
|
-
await client.comments.delete(commentId, {
|
|
441
|
-
password: '1234', // Required for guest comments
|
|
442
|
-
});
|
|
443
|
-
|
|
444
|
-
// Like comment
|
|
445
|
-
const result = await client.comments.like(commentId);
|
|
446
|
-
// Returns: { data: { likes: number } }
|
|
164
|
+
// Common operations
|
|
165
|
+
await client.comments.update(commentId, { content: 'Updated' });
|
|
166
|
+
await client.comments.delete(commentId, { password: '1234' });
|
|
167
|
+
await client.comments.like(commentId);
|
|
447
168
|
```
|
|
448
169
|
|
|
449
170
|
### Shop
|
|
450
171
|
|
|
451
|
-
#### Public (No Login Required)
|
|
452
|
-
|
|
453
172
|
```typescript
|
|
454
|
-
//
|
|
455
|
-
const { data: products
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
category: 'electronics', // optional
|
|
459
|
-
is_featured: true, // optional
|
|
460
|
-
search: 'keyword', // optional
|
|
173
|
+
// Products
|
|
174
|
+
const { data: products } = await client.shop.listProducts({
|
|
175
|
+
category: 'electronics',
|
|
176
|
+
is_featured: true,
|
|
461
177
|
});
|
|
462
|
-
// Returns: ListResponse<Product>
|
|
463
|
-
|
|
464
|
-
// Get product details
|
|
465
178
|
const product = await client.shop.getProduct('product-slug');
|
|
466
|
-
// Returns: Product
|
|
467
|
-
|
|
468
|
-
// Featured products
|
|
469
179
|
const featured = await client.shop.featuredProducts(8);
|
|
470
|
-
// Returns: Product[] (always an array)
|
|
471
|
-
|
|
472
|
-
// List categories
|
|
473
180
|
const categories = await client.shop.listCategories();
|
|
474
|
-
// Returns: ProductCategory[] (always an array)
|
|
475
|
-
```
|
|
476
|
-
|
|
477
|
-
#### Protected (Login Required)
|
|
478
181
|
|
|
479
|
-
|
|
480
|
-
// Get cart
|
|
182
|
+
// Cart (auth required)
|
|
481
183
|
const cart = await client.shop.getCart();
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
// Add to cart
|
|
485
|
-
await client.shop.addToCart({
|
|
486
|
-
product_id: 1,
|
|
487
|
-
quantity: 2,
|
|
488
|
-
variant_id: 10, // optional - for variant products
|
|
489
|
-
});
|
|
490
|
-
|
|
491
|
-
// Update cart item quantity
|
|
184
|
+
await client.shop.addToCart({ product_id: 1, quantity: 2 });
|
|
492
185
|
await client.shop.updateCartItem(itemId, { quantity: 3 });
|
|
493
|
-
|
|
494
|
-
// Remove from cart
|
|
495
186
|
await client.shop.removeFromCart(itemId);
|
|
496
|
-
|
|
497
|
-
// Clear cart
|
|
498
187
|
await client.shop.clearCart();
|
|
499
188
|
|
|
500
|
-
//
|
|
189
|
+
// Orders (auth required)
|
|
501
190
|
const order = await client.shop.createOrder({
|
|
502
191
|
orderer_name: 'John Doe',
|
|
503
192
|
orderer_email: 'john@example.com',
|
|
504
|
-
orderer_phone: '555-
|
|
193
|
+
orderer_phone: '555-1234',
|
|
505
194
|
shipping_name: 'John Doe',
|
|
506
|
-
shipping_phone: '555-
|
|
195
|
+
shipping_phone: '555-1234',
|
|
507
196
|
shipping_zipcode: '12345',
|
|
508
197
|
shipping_address: '123 Main St',
|
|
509
198
|
shipping_address_detail: 'Apt 101',
|
|
510
|
-
shipping_memo: 'Leave at door',
|
|
511
|
-
coupon_code: 'SAVE10', // optional
|
|
512
199
|
});
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
const { data: orders, meta } = await client.shop.listOrders();
|
|
516
|
-
// Returns: ListResponse<Order>
|
|
517
|
-
|
|
518
|
-
// Get order details
|
|
519
|
-
const order = await client.shop.getOrder(orderId);
|
|
520
|
-
// Returns: Order
|
|
521
|
-
|
|
522
|
-
// Cancel order
|
|
200
|
+
const { data: orders } = await client.shop.listOrders();
|
|
201
|
+
const orderDetail = await client.shop.getOrder(orderId);
|
|
523
202
|
await client.shop.cancelOrder(orderId);
|
|
524
203
|
|
|
525
|
-
//
|
|
204
|
+
// Coupons
|
|
526
205
|
const validation = await client.shop.validateCoupon('SAVE10', 50000);
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
// My coupons
|
|
530
|
-
const coupons = await client.shop.myCoupons();
|
|
531
|
-
// Returns: Coupon[] (always an array)
|
|
206
|
+
const myCoupons = await client.shop.myCoupons();
|
|
532
207
|
```
|
|
533
208
|
|
|
534
|
-
### Reservation
|
|
535
|
-
|
|
536
|
-
#### Public (No Login Required)
|
|
209
|
+
### Reservation
|
|
537
210
|
|
|
538
211
|
```typescript
|
|
539
|
-
//
|
|
212
|
+
// Public APIs
|
|
540
213
|
const settings = await client.reservation.getSettings();
|
|
541
|
-
// Returns: ReservationSettings
|
|
542
|
-
|
|
543
|
-
// List services
|
|
544
214
|
const services = await client.reservation.listServices();
|
|
545
|
-
|
|
215
|
+
const staff = await client.reservation.listStaff(serviceId);
|
|
546
216
|
|
|
547
|
-
//
|
|
548
|
-
const staffs = await client.reservation.listStaff();
|
|
549
|
-
// Returns: ReservationStaff[] (always an array)
|
|
550
|
-
|
|
551
|
-
// Get staff for specific service
|
|
552
|
-
const serviceStaffs = await client.reservation.listStaff(serviceId);
|
|
553
|
-
|
|
554
|
-
// Get available dates
|
|
217
|
+
// Get available dates/slots
|
|
555
218
|
const dates = await client.reservation.getAvailableDates({
|
|
556
219
|
service_id: 1,
|
|
557
|
-
staff_id: 2,
|
|
558
|
-
start_date: '2026-01-01',
|
|
559
|
-
end_date: '2026-01-31',
|
|
220
|
+
staff_id: 2,
|
|
221
|
+
start_date: '2026-01-01',
|
|
222
|
+
end_date: '2026-01-31',
|
|
560
223
|
});
|
|
561
|
-
// Returns: string[] (YYYY-MM-DD format)
|
|
562
|
-
|
|
563
|
-
// Get available time slots
|
|
564
224
|
const slots = await client.reservation.getAvailableSlots({
|
|
565
225
|
service_id: 1,
|
|
566
226
|
date: '2026-01-15',
|
|
567
|
-
staff_id: 2,
|
|
227
|
+
staff_id: 2,
|
|
568
228
|
});
|
|
569
|
-
// Returns: ReservationSlot[]
|
|
570
|
-
```
|
|
571
|
-
|
|
572
|
-
#### Protected (Login Required)
|
|
573
229
|
|
|
574
|
-
|
|
575
|
-
// Create reservation
|
|
230
|
+
// Create reservation (auth required)
|
|
576
231
|
const result = await client.reservation.create({
|
|
577
232
|
service_id: 1,
|
|
578
|
-
staff_id: 2,
|
|
233
|
+
staff_id: 2,
|
|
579
234
|
reservation_date: '2026-01-15',
|
|
580
235
|
start_time: '14:00',
|
|
581
236
|
customer_name: 'John Doe',
|
|
582
|
-
customer_phone: '555-
|
|
583
|
-
customer_email: 'john@example.com', // optional
|
|
584
|
-
customer_memo: 'Special requests', // optional
|
|
237
|
+
customer_phone: '555-1234',
|
|
585
238
|
});
|
|
586
|
-
// Returns: { reservation: Reservation, requires_payment: boolean, deposit: number }
|
|
587
239
|
|
|
588
|
-
//
|
|
589
|
-
const { data
|
|
590
|
-
status: 'confirmed', // optional
|
|
591
|
-
upcoming: true, // optional
|
|
592
|
-
past: false, // optional
|
|
593
|
-
});
|
|
594
|
-
// Returns: ListResponse<Reservation>
|
|
595
|
-
|
|
596
|
-
// Upcoming reservations
|
|
240
|
+
// My reservations (auth required)
|
|
241
|
+
const { data } = await client.reservation.list({ status: 'confirmed' });
|
|
597
242
|
const upcoming = await client.reservation.upcoming(5);
|
|
598
|
-
// Returns: Reservation[] (always an array)
|
|
599
|
-
|
|
600
|
-
// Past reservations
|
|
601
243
|
const past = await client.reservation.past(10);
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
// Get reservation details
|
|
605
|
-
const reservation = await client.reservation.get('RES-20260115-001');
|
|
606
|
-
// Returns: Reservation
|
|
607
|
-
|
|
608
|
-
// Cancel reservation
|
|
609
|
-
const cancelled = await client.reservation.cancel('RES-20260115-001', 'Schedule change');
|
|
610
|
-
// Returns: Reservation
|
|
611
|
-
```
|
|
612
|
-
|
|
613
|
-
### Auth (Authentication)
|
|
614
|
-
|
|
615
|
-
```typescript
|
|
616
|
-
// Login
|
|
617
|
-
const response = await client.auth.login({
|
|
618
|
-
email: 'user@example.com',
|
|
619
|
-
password: 'password',
|
|
620
|
-
});
|
|
621
|
-
// Returns: { member: Member, token: string }
|
|
622
|
-
// Token is automatically stored
|
|
623
|
-
|
|
624
|
-
// Register
|
|
625
|
-
await client.auth.register({
|
|
626
|
-
name: 'John Doe',
|
|
627
|
-
email: 'user@example.com',
|
|
628
|
-
password: 'password',
|
|
629
|
-
password_confirmation: 'password',
|
|
630
|
-
phone: '555-123-4567', // optional
|
|
631
|
-
});
|
|
632
|
-
|
|
633
|
-
// Logout
|
|
634
|
-
await client.auth.logout();
|
|
635
|
-
|
|
636
|
-
// Get current user
|
|
637
|
-
const me = await client.auth.me();
|
|
638
|
-
// Returns: Member
|
|
639
|
-
|
|
640
|
-
// Update profile
|
|
641
|
-
await client.auth.updateProfile({
|
|
642
|
-
name: 'New Name',
|
|
643
|
-
phone: '555-999-8888',
|
|
644
|
-
});
|
|
645
|
-
|
|
646
|
-
// Change password
|
|
647
|
-
await client.auth.updateProfile({
|
|
648
|
-
current_password: 'current_password',
|
|
649
|
-
password: 'new_password',
|
|
650
|
-
password_confirmation: 'new_password',
|
|
651
|
-
});
|
|
652
|
-
|
|
653
|
-
// Check authentication status
|
|
654
|
-
client.isAuthenticated(); // true or false
|
|
655
|
-
|
|
656
|
-
// Set token directly (e.g., from localStorage)
|
|
657
|
-
client.setToken('saved-token');
|
|
658
|
-
|
|
659
|
-
// Get token
|
|
660
|
-
const token = client.getToken();
|
|
661
|
-
```
|
|
662
|
-
|
|
663
|
-
#### Social Login
|
|
664
|
-
|
|
665
|
-
```typescript
|
|
666
|
-
// Get social login providers
|
|
667
|
-
const providers = await client.auth.getSocialProviders();
|
|
668
|
-
// Returns: SocialProvider[]
|
|
669
|
-
|
|
670
|
-
// Get social auth URL
|
|
671
|
-
const { url } = await client.auth.getSocialAuthUrl('google');
|
|
672
|
-
// Redirect to this URL
|
|
673
|
-
|
|
674
|
-
// Handle callback (after redirect)
|
|
675
|
-
const response = await client.auth.socialCallback('google', code);
|
|
676
|
-
// Returns: { member: Member, token: string }
|
|
244
|
+
await client.reservation.cancel('RES-20260115-001', 'Schedule change');
|
|
677
245
|
```
|
|
678
246
|
|
|
679
247
|
### Forms
|
|
@@ -681,350 +249,133 @@ const response = await client.auth.socialCallback('google', code);
|
|
|
681
249
|
```typescript
|
|
682
250
|
// List forms
|
|
683
251
|
const { data: forms } = await client.forms.list();
|
|
684
|
-
// Returns: ListResponse<Form>
|
|
685
252
|
|
|
686
|
-
// Get form
|
|
253
|
+
// Get form with fields
|
|
687
254
|
const form = await client.forms.get('contact');
|
|
688
|
-
// Returns: Form (includes field definitions)
|
|
689
255
|
|
|
690
|
-
// Submit form
|
|
256
|
+
// Submit form
|
|
691
257
|
await client.forms.submit('contact', {
|
|
692
258
|
name: 'John Doe',
|
|
693
|
-
email: '
|
|
694
|
-
message: '
|
|
259
|
+
email: 'john@example.com',
|
|
260
|
+
message: 'Hello!',
|
|
695
261
|
});
|
|
696
262
|
|
|
697
|
-
// My submissions (
|
|
263
|
+
// My submissions (auth required)
|
|
698
264
|
const { data: submissions } = await client.forms.mySubmissions();
|
|
699
|
-
// Returns: ListResponse<FormSubmission>
|
|
700
265
|
```
|
|
701
266
|
|
|
702
|
-
### Media
|
|
267
|
+
### Media (Auth Required)
|
|
703
268
|
|
|
704
269
|
```typescript
|
|
705
|
-
// Upload
|
|
706
|
-
const media = await client.media.upload(file);
|
|
707
|
-
// Returns: Media
|
|
708
|
-
|
|
709
|
-
// Upload multiple files
|
|
270
|
+
// Upload
|
|
271
|
+
const media = await client.media.upload(file);
|
|
710
272
|
const mediaList = await client.media.uploadMultiple([file1, file2]);
|
|
711
|
-
// Returns: Media[]
|
|
712
273
|
|
|
713
|
-
// List
|
|
714
|
-
const { data:
|
|
715
|
-
page: 1,
|
|
716
|
-
per_page: 20,
|
|
717
|
-
type: 'image/jpeg', // optional
|
|
718
|
-
});
|
|
719
|
-
// Returns: ListResponse<Media>
|
|
720
|
-
|
|
721
|
-
// Delete media
|
|
274
|
+
// List & Delete
|
|
275
|
+
const { data: myMedia } = await client.media.list({ type: 'image/jpeg' });
|
|
722
276
|
await client.media.delete(mediaId);
|
|
723
277
|
```
|
|
724
278
|
|
|
725
|
-
###
|
|
279
|
+
### Custom Entities
|
|
726
280
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
#### Entity Definition CRUD
|
|
281
|
+
Dynamic data structures for custom use cases.
|
|
730
282
|
|
|
731
283
|
```typescript
|
|
732
|
-
//
|
|
284
|
+
// Entity definitions
|
|
733
285
|
const entities = await client.entities.list();
|
|
734
|
-
|
|
286
|
+
const entity = await client.entities.get('customers');
|
|
735
287
|
|
|
736
|
-
|
|
737
|
-
const entity = await client.entities.create({
|
|
288
|
+
await client.entities.create({
|
|
738
289
|
name: 'Customer',
|
|
739
290
|
slug: 'customers',
|
|
740
|
-
description: 'Customer management',
|
|
741
291
|
schema: {
|
|
742
292
|
fields: [
|
|
743
|
-
{ name: 'company', label: 'Company
|
|
293
|
+
{ name: 'company', label: 'Company', type: 'text', required: true },
|
|
744
294
|
{ name: 'email', label: 'Email', type: 'email', required: true },
|
|
745
295
|
{ name: 'status', label: 'Status', type: 'select', options: [
|
|
746
296
|
{ value: 'active', label: 'Active' },
|
|
747
|
-
{ value: 'inactive', label: 'Inactive' }
|
|
748
|
-
]}
|
|
749
|
-
]
|
|
297
|
+
{ value: 'inactive', label: 'Inactive' },
|
|
298
|
+
]},
|
|
299
|
+
],
|
|
750
300
|
},
|
|
751
|
-
icon: 'users'
|
|
752
301
|
});
|
|
753
302
|
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
// Returns: CustomEntity (includes schema)
|
|
757
|
-
|
|
758
|
-
// Update entity definition
|
|
759
|
-
await client.entities.update('customers', {
|
|
760
|
-
name: 'Clients',
|
|
761
|
-
description: 'Client management'
|
|
762
|
-
});
|
|
763
|
-
|
|
764
|
-
// Delete entity definition (requires force if records exist)
|
|
765
|
-
await client.entities.delete('customers'); // When no records exist
|
|
766
|
-
await client.entities.delete('customers', true); // Force delete with records
|
|
767
|
-
```
|
|
768
|
-
|
|
769
|
-
#### Record CRUD
|
|
303
|
+
await client.entities.update('customers', { name: 'Clients' });
|
|
304
|
+
await client.entities.delete('customers', true); // force delete
|
|
770
305
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
per_page: 20,
|
|
776
|
-
search: 'ACME', // Search
|
|
777
|
-
sort: 'company', // Sort field
|
|
778
|
-
dir: 'asc', // Sort direction
|
|
779
|
-
filters: JSON.stringify({ status: 'active' }) // JSON filter
|
|
306
|
+
// Records
|
|
307
|
+
const { data: records } = await client.entities.listRecords('customers', {
|
|
308
|
+
search: 'ACME',
|
|
309
|
+
filters: JSON.stringify({ status: 'active' }),
|
|
780
310
|
});
|
|
781
|
-
|
|
311
|
+
const record = await client.entities.getRecord('customers', 1);
|
|
782
312
|
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
console.log(customer.data.company); // 'ABC Corp'
|
|
787
|
-
|
|
788
|
-
// Create record (pass schema fields directly)
|
|
789
|
-
const newCustomer = await client.entities.createRecord('customers', {
|
|
790
|
-
company: 'ABC Corp',
|
|
791
|
-
email: 'contact@abc.com',
|
|
313
|
+
await client.entities.createRecord('customers', {
|
|
314
|
+
company: 'ACME Corp',
|
|
315
|
+
email: 'contact@acme.com',
|
|
792
316
|
status: 'active',
|
|
793
317
|
});
|
|
794
|
-
|
|
795
|
-
// Update record (partial update - merges with existing data)
|
|
796
|
-
await client.entities.updateRecord('customers', 1, {
|
|
797
|
-
status: 'inactive',
|
|
798
|
-
email: 'new@abc.com'
|
|
799
|
-
});
|
|
800
|
-
|
|
801
|
-
// Delete record
|
|
318
|
+
await client.entities.updateRecord('customers', 1, { status: 'inactive' });
|
|
802
319
|
await client.entities.deleteRecord('customers', 1);
|
|
803
|
-
```
|
|
804
|
-
|
|
805
|
-
#### TypeScript Type Support
|
|
806
320
|
|
|
807
|
-
|
|
808
|
-
// Typed entity accessor
|
|
321
|
+
// TypeScript support
|
|
809
322
|
interface Customer {
|
|
810
323
|
company: string;
|
|
811
324
|
email: string;
|
|
812
|
-
|
|
325
|
+
status: 'active' | 'inactive';
|
|
813
326
|
}
|
|
814
|
-
|
|
815
|
-
const
|
|
816
|
-
|
|
817
|
-
// Type inference
|
|
818
|
-
const list = await customers.list();
|
|
819
|
-
list.data[0].data.company; // string
|
|
820
|
-
|
|
821
|
-
const record = await customers.get(1);
|
|
822
|
-
record.data.tier; // 'standard' | 'vip'
|
|
823
|
-
|
|
824
|
-
// Type checking on create/update
|
|
825
|
-
await customers.create({
|
|
826
|
-
company: 'New Corp',
|
|
827
|
-
email: 'new@corp.com',
|
|
828
|
-
tier: 'standard',
|
|
829
|
-
});
|
|
327
|
+
const customers = client.entities.typed<Customer>('customers');
|
|
328
|
+
const { data } = await customers.list();
|
|
830
329
|
```
|
|
831
330
|
|
|
832
|
-
###
|
|
331
|
+
### Auth
|
|
833
332
|
|
|
834
333
|
```typescript
|
|
835
|
-
//
|
|
836
|
-
|
|
837
|
-
|
|
334
|
+
// Register
|
|
335
|
+
await client.auth.register({
|
|
336
|
+
name: 'John Doe',
|
|
337
|
+
email: 'john@example.com',
|
|
338
|
+
password: 'password',
|
|
339
|
+
password_confirmation: 'password',
|
|
340
|
+
});
|
|
838
341
|
|
|
839
|
-
//
|
|
840
|
-
const
|
|
841
|
-
|
|
842
|
-
|
|
342
|
+
// Login/Logout
|
|
343
|
+
const { member, token } = await client.auth.login({
|
|
344
|
+
email: 'john@example.com',
|
|
345
|
+
password: 'password',
|
|
346
|
+
});
|
|
347
|
+
await client.auth.logout();
|
|
843
348
|
|
|
844
|
-
|
|
349
|
+
// Profile
|
|
350
|
+
const me = await client.auth.me();
|
|
351
|
+
await client.auth.updateProfile({ name: 'New Name' });
|
|
845
352
|
|
|
846
|
-
|
|
353
|
+
// Token management
|
|
354
|
+
client.setToken('saved-token');
|
|
355
|
+
const token = client.getToken();
|
|
356
|
+
client.isAuthenticated();
|
|
847
357
|
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
/** API key (required) - Get from Dashboard > Settings > API Tokens */
|
|
853
|
-
apiKey: string;
|
|
854
|
-
/** Base URL of Promptly API (optional) */
|
|
855
|
-
baseUrl?: string;
|
|
856
|
-
/** Request timeout in milliseconds (optional) */
|
|
857
|
-
timeout?: number;
|
|
858
|
-
}
|
|
358
|
+
// Social login
|
|
359
|
+
const providers = await client.auth.getSocialProviders();
|
|
360
|
+
const { url } = await client.auth.getSocialAuthUrl('google');
|
|
361
|
+
const response = await client.auth.socialCallback('google', code);
|
|
859
362
|
```
|
|
860
363
|
|
|
861
|
-
|
|
364
|
+
## Response Types
|
|
862
365
|
|
|
863
|
-
|
|
864
|
-
// Unified list response type
|
|
865
|
-
interface ListResponse<T> {
|
|
866
|
-
data: T[];
|
|
867
|
-
meta: PaginationMeta;
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
interface PaginationMeta {
|
|
871
|
-
current_page: number;
|
|
872
|
-
last_page: number;
|
|
873
|
-
per_page: number;
|
|
874
|
-
total: number;
|
|
875
|
-
from: number | null;
|
|
876
|
-
to: number | null;
|
|
877
|
-
}
|
|
878
|
-
```
|
|
879
|
-
|
|
880
|
-
### Resource Types
|
|
366
|
+
All list APIs return a consistent format:
|
|
881
367
|
|
|
882
368
|
```typescript
|
|
883
|
-
interface
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
id: number;
|
|
894
|
-
board_id: number;
|
|
895
|
-
title: string;
|
|
896
|
-
content: string;
|
|
897
|
-
excerpt?: string;
|
|
898
|
-
author: string;
|
|
899
|
-
views: number;
|
|
900
|
-
is_notice: boolean;
|
|
901
|
-
is_secret: boolean;
|
|
902
|
-
is_mine: boolean;
|
|
903
|
-
is_private: boolean;
|
|
904
|
-
comment_count: number;
|
|
905
|
-
attachments?: Media[];
|
|
906
|
-
created_at: string;
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
interface BlogPost {
|
|
910
|
-
id: number;
|
|
911
|
-
slug: string;
|
|
912
|
-
title: string;
|
|
913
|
-
content: string;
|
|
914
|
-
excerpt?: string;
|
|
915
|
-
featured_image?: string;
|
|
916
|
-
category?: string;
|
|
917
|
-
tags?: string[];
|
|
918
|
-
author_name?: string;
|
|
919
|
-
is_published: boolean;
|
|
920
|
-
published_at?: string;
|
|
921
|
-
view_count: number;
|
|
922
|
-
created_at: string;
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
interface Comment {
|
|
926
|
-
id: number;
|
|
927
|
-
type: 'board_post' | 'blog_post' | 'page';
|
|
928
|
-
author_name: string;
|
|
929
|
-
author_avatar: string | null;
|
|
930
|
-
content: string;
|
|
931
|
-
is_approved: boolean;
|
|
932
|
-
is_pinned: boolean;
|
|
933
|
-
is_secret: boolean;
|
|
934
|
-
likes: number;
|
|
935
|
-
depth: number;
|
|
936
|
-
created_at: string;
|
|
937
|
-
replies: Comment[];
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
interface Product {
|
|
941
|
-
id: number;
|
|
942
|
-
slug: string;
|
|
943
|
-
name: string;
|
|
944
|
-
description?: string;
|
|
945
|
-
price: number;
|
|
946
|
-
compare_price?: number;
|
|
947
|
-
thumbnail?: string;
|
|
948
|
-
images?: string[];
|
|
949
|
-
status: 'draft' | 'active' | 'inactive';
|
|
950
|
-
is_featured: boolean;
|
|
951
|
-
in_stock?: boolean;
|
|
952
|
-
discount_percent?: number;
|
|
953
|
-
created_at: string;
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
interface Reservation {
|
|
957
|
-
id: number;
|
|
958
|
-
reservation_number: string;
|
|
959
|
-
status: 'pending' | 'confirmed' | 'completed' | 'cancelled' | 'no_show';
|
|
960
|
-
status_label: string;
|
|
961
|
-
reservation_date: string;
|
|
962
|
-
start_time: string;
|
|
963
|
-
end_time: string;
|
|
964
|
-
time_range: string;
|
|
965
|
-
customer_name: string;
|
|
966
|
-
customer_phone: string | null;
|
|
967
|
-
customer_email: string | null;
|
|
968
|
-
price: number;
|
|
969
|
-
deposit: number;
|
|
970
|
-
payment_status: 'pending' | 'paid' | 'refunded' | 'partial';
|
|
971
|
-
can_cancel: boolean;
|
|
972
|
-
service: { id: number; name: string; duration: number; } | null;
|
|
973
|
-
staff: { id: number; name: string; avatar: string | null; } | null;
|
|
974
|
-
created_at: string;
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
interface ReservationService {
|
|
978
|
-
id: number;
|
|
979
|
-
name: string;
|
|
980
|
-
slug: string;
|
|
981
|
-
description: string | null;
|
|
982
|
-
thumbnail: string | null;
|
|
983
|
-
duration: number;
|
|
984
|
-
price: number;
|
|
985
|
-
requires_staff: boolean;
|
|
986
|
-
requires_payment: boolean;
|
|
987
|
-
deposit: number;
|
|
988
|
-
staffs: Array<{ id: number; name: string; avatar: string | null; }>;
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
interface ReservationSettings {
|
|
992
|
-
timezone: string;
|
|
993
|
-
slot_interval: number;
|
|
994
|
-
min_notice_hours: number;
|
|
995
|
-
max_advance_days: number;
|
|
996
|
-
cancellation_hours: number;
|
|
997
|
-
allow_online_payment: boolean;
|
|
998
|
-
bookable_date_range: { start: string; end: string; };
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
interface Member {
|
|
1002
|
-
id: number;
|
|
1003
|
-
name: string;
|
|
1004
|
-
email: string;
|
|
1005
|
-
phone?: string;
|
|
1006
|
-
avatar?: string;
|
|
1007
|
-
is_active: boolean;
|
|
1008
|
-
created_at: string;
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
|
-
interface CustomEntity {
|
|
1012
|
-
id: number;
|
|
1013
|
-
name: string;
|
|
1014
|
-
slug: string;
|
|
1015
|
-
description?: string;
|
|
1016
|
-
schema: EntitySchema;
|
|
1017
|
-
is_active: boolean;
|
|
1018
|
-
created_at: string;
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
interface EntityRecord {
|
|
1022
|
-
id: number;
|
|
1023
|
-
entity_id: number;
|
|
1024
|
-
data: Record<string, any>;
|
|
1025
|
-
status: 'active' | 'archived' | 'draft';
|
|
1026
|
-
created_at: string;
|
|
1027
|
-
updated_at: string;
|
|
369
|
+
interface ListResponse<T> {
|
|
370
|
+
data: T[]; // Always an array, never null
|
|
371
|
+
meta: {
|
|
372
|
+
current_page: number;
|
|
373
|
+
last_page: number;
|
|
374
|
+
per_page: number;
|
|
375
|
+
total: number;
|
|
376
|
+
from: number | null;
|
|
377
|
+
to: number | null;
|
|
378
|
+
};
|
|
1028
379
|
}
|
|
1029
380
|
```
|
|
1030
381
|
|
|
@@ -1052,23 +403,20 @@ import { Promptly } from '@back23/promptly-sdk';
|
|
|
1052
403
|
|
|
1053
404
|
const client = new Promptly({
|
|
1054
405
|
tenantId: 'demo',
|
|
1055
|
-
apiKey: process.env.NEXT_PUBLIC_PROMPTLY_API_KEY!,
|
|
1056
|
-
baseUrl: 'https://promptly.webbyon.com',
|
|
406
|
+
apiKey: process.env.NEXT_PUBLIC_PROMPTLY_API_KEY!,
|
|
1057
407
|
});
|
|
1058
408
|
|
|
1059
|
-
// Blog post list (with pagination)
|
|
1060
409
|
function BlogList() {
|
|
1061
410
|
const [posts, setPosts] = useState([]);
|
|
1062
|
-
const [
|
|
1063
|
-
const [page, setPage] = useState(1);
|
|
411
|
+
const [loading, setLoading] = useState(true);
|
|
1064
412
|
|
|
1065
413
|
useEffect(() => {
|
|
1066
|
-
client.blog.list({
|
|
1067
|
-
.then(({ data
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
414
|
+
client.blog.list({ per_page: 10 })
|
|
415
|
+
.then(({ data }) => setPosts(data))
|
|
416
|
+
.finally(() => setLoading(false));
|
|
417
|
+
}, []);
|
|
418
|
+
|
|
419
|
+
if (loading) return <div>Loading...</div>;
|
|
1072
420
|
|
|
1073
421
|
return (
|
|
1074
422
|
<div>
|
|
@@ -1076,83 +424,33 @@ function BlogList() {
|
|
|
1076
424
|
<article key={post.id}>
|
|
1077
425
|
<h2>{post.title}</h2>
|
|
1078
426
|
<p>{post.excerpt}</p>
|
|
427
|
+
<span>{post.category}</span>
|
|
428
|
+
{post.tags.map(tag => <span key={tag}>{tag}</span>)}
|
|
1079
429
|
</article>
|
|
1080
430
|
))}
|
|
1081
|
-
|
|
1082
|
-
{meta && (
|
|
1083
|
-
<div>
|
|
1084
|
-
Page {meta.current_page} of {meta.last_page}
|
|
1085
|
-
<button
|
|
1086
|
-
onClick={() => setPage(p => p - 1)}
|
|
1087
|
-
disabled={page <= 1}
|
|
1088
|
-
>
|
|
1089
|
-
Previous
|
|
1090
|
-
</button>
|
|
1091
|
-
<button
|
|
1092
|
-
onClick={() => setPage(p => p + 1)}
|
|
1093
|
-
disabled={page >= meta.last_page}
|
|
1094
|
-
>
|
|
1095
|
-
Next
|
|
1096
|
-
</button>
|
|
1097
|
-
</div>
|
|
1098
|
-
)}
|
|
1099
431
|
</div>
|
|
1100
432
|
);
|
|
1101
433
|
}
|
|
434
|
+
```
|
|
1102
435
|
|
|
1103
|
-
|
|
1104
|
-
function ReservationForm() {
|
|
1105
|
-
const [services, setServices] = useState([]);
|
|
1106
|
-
const [selectedService, setSelectedService] = useState(null);
|
|
1107
|
-
const [dates, setDates] = useState([]);
|
|
1108
|
-
const [slots, setSlots] = useState([]);
|
|
436
|
+
## Changelog
|
|
1109
437
|
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
438
|
+
### v2.12.0
|
|
439
|
+
- Added `category`, `tags`, `views`, `published_at` fields to BlogPost response
|
|
440
|
+
- Added `GET /blog/categories` and `GET /blog/tags` endpoints
|
|
1113
441
|
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
client.reservation.getAvailableDates({
|
|
1117
|
-
service_id: selectedService,
|
|
1118
|
-
}).then(setDates);
|
|
1119
|
-
}
|
|
1120
|
-
}, [selectedService]);
|
|
1121
|
-
|
|
1122
|
-
const handleDateSelect = async (date) => {
|
|
1123
|
-
const availableSlots = await client.reservation.getAvailableSlots({
|
|
1124
|
-
service_id: selectedService,
|
|
1125
|
-
date,
|
|
1126
|
-
});
|
|
1127
|
-
setSlots(availableSlots);
|
|
1128
|
-
};
|
|
442
|
+
### v2.5.0
|
|
443
|
+
- Added `is_secret` and `is_mine` fields to BoardPost
|
|
1129
444
|
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
<div>
|
|
1140
|
-
{dates.map(date => (
|
|
1141
|
-
<button key={date} onClick={() => handleDateSelect(date)}>
|
|
1142
|
-
{date}
|
|
1143
|
-
</button>
|
|
1144
|
-
))}
|
|
1145
|
-
</div>
|
|
1146
|
-
|
|
1147
|
-
<div>
|
|
1148
|
-
{slots.filter(s => s.available).map(slot => (
|
|
1149
|
-
<button key={slot.time}>{slot.time}</button>
|
|
1150
|
-
))}
|
|
1151
|
-
</div>
|
|
1152
|
-
</div>
|
|
1153
|
-
);
|
|
1154
|
-
}
|
|
1155
|
-
```
|
|
445
|
+
### v2.3.0
|
|
446
|
+
- Polymorphic comments API (board, blog, standalone)
|
|
447
|
+
|
|
448
|
+
### v2.0.0
|
|
449
|
+
- **Breaking:** API key required for all requests
|
|
450
|
+
|
|
451
|
+
### v1.3.0
|
|
452
|
+
- Unified `ListResponse<T>` format for all list APIs
|
|
453
|
+
- Reservation system support
|
|
1156
454
|
|
|
1157
455
|
## License
|
|
1158
456
|
|