@verbatims/sdk 1.5.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/resources/index.d.ts +1 -0
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/resources/index.js +1 -0
- package/dist/resources/index.js.map +1 -1
- package/dist/resources/themes.d.ts +469 -0
- package/dist/resources/themes.d.ts.map +1 -0
- package/dist/resources/themes.js +170 -0
- package/dist/resources/themes.js.map +1 -0
- package/dist/types.d.ts +181 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +1 -0
- package/src/index.ts +24 -0
- package/src/resources/__tests__/resources.test.ts +200 -0
- package/src/resources/index.ts +1 -0
- package/src/resources/themes.ts +214 -0
- package/src/types.ts +183 -0
|
@@ -6,6 +6,7 @@ import { ReferencesResource } from '../references'
|
|
|
6
6
|
import { TagsResource } from '../tags'
|
|
7
7
|
import { CollectionsResource } from '../collections'
|
|
8
8
|
import { SearchResource } from '../search'
|
|
9
|
+
import { ThemesResource } from '../themes'
|
|
9
10
|
|
|
10
11
|
function mockResponse(data: unknown, status = 200) {
|
|
11
12
|
return new Response(JSON.stringify(data), {
|
|
@@ -338,6 +339,205 @@ describe('CollectionsResource', () => {
|
|
|
338
339
|
})
|
|
339
340
|
})
|
|
340
341
|
|
|
342
|
+
describe('ThemesResource', () => {
|
|
343
|
+
let fetchFn: ReturnType<typeof vi.fn>
|
|
344
|
+
let themes: ThemesResource
|
|
345
|
+
|
|
346
|
+
beforeEach(() => {
|
|
347
|
+
const mock = createMockClient()
|
|
348
|
+
fetchFn = mock.fetchFn
|
|
349
|
+
themes = new ThemesResource(mock.client)
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
it('getActive calls GET /themes/active', async () => {
|
|
353
|
+
fetchFn.mockResolvedValue(mockResponse({
|
|
354
|
+
success: true,
|
|
355
|
+
data: { id: 1, slug: 'summer', name: 'Summer', description: null, language: 'en', isActive: true, isDefault: false, scheduledStart: null, scheduledEnd: null, priority: 0, config: null, createdAt: '2024-01-01', updatedAt: '2024-01-01', filters_count: 0 },
|
|
356
|
+
}))
|
|
357
|
+
|
|
358
|
+
const result = await themes.getActive({ language: 'en' })
|
|
359
|
+
const [url] = fetchFn.mock.calls[0]
|
|
360
|
+
expect(url).toContain('/themes/active')
|
|
361
|
+
const parsed = new URL(url, 'http://localhost')
|
|
362
|
+
expect(parsed.searchParams.get('language')).toBe('en')
|
|
363
|
+
expect(result.data?.slug).toBe('summer')
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
it('getFeed calls GET /themes/:slug/feed', async () => {
|
|
367
|
+
fetchFn.mockResolvedValue(mockResponse({
|
|
368
|
+
success: true,
|
|
369
|
+
data: { theme: { id: 1, slug: 'summer', name: 'Summer', description: null, language: 'en', isActive: true, isDefault: false, scheduledStart: null, scheduledEnd: null, priority: 0, config: null, createdAt: '2024-01-01', updatedAt: '2024-01-01', filters_count: 0 }, quotes: [], authors: [], references: [], total: 0 },
|
|
370
|
+
}))
|
|
371
|
+
|
|
372
|
+
await themes.getFeed('summer', { language: 'fr' })
|
|
373
|
+
const [url] = fetchFn.mock.calls[0]
|
|
374
|
+
expect(url).toContain('/themes/summer/feed')
|
|
375
|
+
const parsed = new URL(url, 'http://localhost')
|
|
376
|
+
expect(parsed.searchParams.get('language')).toBe('fr')
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
it('list calls GET /themes with params', async () => {
|
|
380
|
+
fetchFn.mockResolvedValue(mockResponse({
|
|
381
|
+
success: true,
|
|
382
|
+
data: [{ id: 1, slug: 'summer', name: 'Summer', description: null, language: 'en', isActive: true, isDefault: false, scheduledStart: null, scheduledEnd: null, priority: 0, config: null, createdAt: '2024-01-01', updatedAt: '2024-01-01', filters_count: 0 }],
|
|
383
|
+
pagination: { page: 1, limit: 10, total: 1, totalPages: 1, hasMore: false },
|
|
384
|
+
}))
|
|
385
|
+
|
|
386
|
+
const result = await themes.list({ search: 'summer' })
|
|
387
|
+
const [url] = fetchFn.mock.calls[0]
|
|
388
|
+
expect(url).toContain('/themes')
|
|
389
|
+
const parsed = new URL(url, 'http://localhost')
|
|
390
|
+
expect(parsed.searchParams.get('search')).toBe('summer')
|
|
391
|
+
expect(result.data).toHaveLength(1)
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
it('paginate yields themes across pages', async () => {
|
|
395
|
+
fetchFn
|
|
396
|
+
.mockResolvedValueOnce(mockResponse({
|
|
397
|
+
success: true,
|
|
398
|
+
data: [{ id: 1, slug: 'a', name: 'A', description: null, language: null, isActive: false, isDefault: false, scheduledStart: null, scheduledEnd: null, priority: 0, config: null, createdAt: '2024-01-01', updatedAt: '2024-01-01', filters_count: 0 }],
|
|
399
|
+
pagination: { page: 1, limit: 1, total: 2, totalPages: 2, hasMore: true },
|
|
400
|
+
}))
|
|
401
|
+
.mockResolvedValueOnce(mockResponse({
|
|
402
|
+
success: true,
|
|
403
|
+
data: [{ id: 2, slug: 'b', name: 'B', description: null, language: null, isActive: false, isDefault: false, scheduledStart: null, scheduledEnd: null, priority: 0, config: null, createdAt: '2024-01-01', updatedAt: '2024-01-01', filters_count: 0 }],
|
|
404
|
+
pagination: { page: 2, limit: 1, total: 2, totalPages: 2, hasMore: false },
|
|
405
|
+
}))
|
|
406
|
+
|
|
407
|
+
const results = []
|
|
408
|
+
for await (const item of themes.paginate()) {
|
|
409
|
+
results.push(item)
|
|
410
|
+
}
|
|
411
|
+
expect(results).toHaveLength(2)
|
|
412
|
+
})
|
|
413
|
+
|
|
414
|
+
it('get calls GET /themes/:id', async () => {
|
|
415
|
+
fetchFn.mockResolvedValue(mockResponse({
|
|
416
|
+
success: true,
|
|
417
|
+
data: { id: 5, slug: 'test', name: 'Test', description: null, language: null, isActive: false, isDefault: false, scheduledStart: null, scheduledEnd: null, priority: 0, config: null, createdAt: '2024-01-01', updatedAt: '2024-01-01', filters_count: 0, filters: [], translations: [] },
|
|
418
|
+
}))
|
|
419
|
+
|
|
420
|
+
await themes.get(5)
|
|
421
|
+
const [url] = fetchFn.mock.calls[0]
|
|
422
|
+
expect(url).toContain('/themes/5')
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
it('create calls POST /themes', async () => {
|
|
426
|
+
fetchFn.mockResolvedValue(mockResponse({
|
|
427
|
+
success: true,
|
|
428
|
+
data: { id: 1, slug: 'new-theme', name: 'New Theme', description: null, language: 'en', isActive: false, isDefault: false, scheduledStart: null, scheduledEnd: null, priority: 0, config: null, createdAt: '2024-01-01', updatedAt: '2024-01-01', filters_count: 0 },
|
|
429
|
+
}))
|
|
430
|
+
|
|
431
|
+
await themes.create({ slug: 'new-theme', name: 'New Theme', language: 'en' })
|
|
432
|
+
const [, opts] = fetchFn.mock.calls[0]
|
|
433
|
+
expect(opts.method).toBe('POST')
|
|
434
|
+
expect(JSON.parse(opts.body).slug).toBe('new-theme')
|
|
435
|
+
})
|
|
436
|
+
|
|
437
|
+
it('update calls PUT /themes/:id', async () => {
|
|
438
|
+
fetchFn.mockResolvedValue(mockResponse({
|
|
439
|
+
success: true,
|
|
440
|
+
data: { id: 1, slug: 'updated', name: 'Updated', description: null, language: 'en', isActive: false, isDefault: false, scheduledStart: null, scheduledEnd: null, priority: 5, config: null, createdAt: '2024-01-01', updatedAt: '2024-01-01', filters_count: 0 },
|
|
441
|
+
}))
|
|
442
|
+
|
|
443
|
+
await themes.update(1, { priority: 5 })
|
|
444
|
+
const [, opts] = fetchFn.mock.calls[0]
|
|
445
|
+
expect(opts.method).toBe('PUT')
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
it('delete calls DELETE /themes/:id', async () => {
|
|
449
|
+
fetchFn.mockResolvedValue(mockResponse({ success: true }))
|
|
450
|
+
await themes.delete(1)
|
|
451
|
+
const [url, opts] = fetchFn.mock.calls[0]
|
|
452
|
+
expect(url).toContain('/themes/1')
|
|
453
|
+
expect(opts.method).toBe('DELETE')
|
|
454
|
+
})
|
|
455
|
+
|
|
456
|
+
it('activate calls PUT /themes/:id/activate', async () => {
|
|
457
|
+
fetchFn.mockResolvedValue(mockResponse({
|
|
458
|
+
success: true,
|
|
459
|
+
data: { id: 1, slug: 'test', name: 'Test', description: null, language: null, isActive: true, isDefault: false, scheduledStart: null, scheduledEnd: null, priority: 0, config: null, createdAt: '2024-01-01', updatedAt: '2024-01-01', filters_count: 0 },
|
|
460
|
+
}))
|
|
461
|
+
|
|
462
|
+
await themes.activate(1, true)
|
|
463
|
+
const [, opts] = fetchFn.mock.calls[0]
|
|
464
|
+
expect(opts.method).toBe('PUT')
|
|
465
|
+
expect(JSON.parse(opts.body)).toEqual({ is_active: true })
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
it('addFilter calls POST /themes/:id/filters', async () => {
|
|
469
|
+
fetchFn.mockResolvedValue(mockResponse({
|
|
470
|
+
success: true,
|
|
471
|
+
data: { id: 10, themeId: 1, type: 'keyword', value: 'test', matchMode: 'any' },
|
|
472
|
+
}))
|
|
473
|
+
|
|
474
|
+
await themes.addFilter(1, { type: 'keyword', value: 'test' })
|
|
475
|
+
const [, opts] = fetchFn.mock.calls[0]
|
|
476
|
+
expect(opts.method).toBe('POST')
|
|
477
|
+
expect(JSON.parse(opts.body)).toEqual({ type: 'keyword', value: 'test' })
|
|
478
|
+
})
|
|
479
|
+
|
|
480
|
+
it('removeFilter calls DELETE /themes/:id/filters/:fid', async () => {
|
|
481
|
+
fetchFn.mockResolvedValue(mockResponse({ success: true }))
|
|
482
|
+
await themes.removeFilter(1, 5)
|
|
483
|
+
const [url, opts] = fetchFn.mock.calls[0]
|
|
484
|
+
expect(url).toContain('/themes/1/filters/5')
|
|
485
|
+
expect(opts.method).toBe('DELETE')
|
|
486
|
+
})
|
|
487
|
+
|
|
488
|
+
it('filterSuggestions calls GET /themes/filter-suggestions', async () => {
|
|
489
|
+
fetchFn.mockResolvedValue(mockResponse({
|
|
490
|
+
success: true,
|
|
491
|
+
data: [{ label: 'Test Tag', value: 'test' }],
|
|
492
|
+
}))
|
|
493
|
+
|
|
494
|
+
await themes.filterSuggestions({ q: 'test', type: 'tag_name' })
|
|
495
|
+
const [url] = fetchFn.mock.calls[0]
|
|
496
|
+
expect(url).toContain('/themes/filter-suggestions')
|
|
497
|
+
const parsed = new URL(url, 'http://localhost')
|
|
498
|
+
expect(parsed.searchParams.get('q')).toBe('test')
|
|
499
|
+
expect(parsed.searchParams.get('type')).toBe('tag_name')
|
|
500
|
+
})
|
|
501
|
+
|
|
502
|
+
it('filterRecommendations calls POST /themes/filter-recommendations', async () => {
|
|
503
|
+
fetchFn.mockResolvedValue(mockResponse({
|
|
504
|
+
success: true,
|
|
505
|
+
data: [{ type: 'tag', value: 'wisdom', label: 'Wisdom' }],
|
|
506
|
+
}))
|
|
507
|
+
|
|
508
|
+
await themes.filterRecommendations({ name: 'Summer', filters: [{ type: 'keyword', value: 'sun' }] })
|
|
509
|
+
const [, opts] = fetchFn.mock.calls[0]
|
|
510
|
+
expect(opts.method).toBe('POST')
|
|
511
|
+
expect(JSON.parse(opts.body).name).toBe('Summer')
|
|
512
|
+
})
|
|
513
|
+
|
|
514
|
+
it('suggestName calls POST /themes/suggest-name', async () => {
|
|
515
|
+
fetchFn.mockResolvedValue(mockResponse({
|
|
516
|
+
success: true,
|
|
517
|
+
data: { name: 'Summer Vibes', slug: 'summer-vibes', description: 'A summer theme' },
|
|
518
|
+
}))
|
|
519
|
+
|
|
520
|
+
await themes.suggestName('summer')
|
|
521
|
+
const [, opts] = fetchFn.mock.calls[0]
|
|
522
|
+
expect(opts.method).toBe('POST')
|
|
523
|
+
expect(JSON.parse(opts.body).name).toBe('summer')
|
|
524
|
+
})
|
|
525
|
+
|
|
526
|
+
it('suggestions calls GET /themes/suggestions', async () => {
|
|
527
|
+
fetchFn.mockResolvedValue(mockResponse({
|
|
528
|
+
success: true,
|
|
529
|
+
data: [{ type: 'tag', name: 'Summer', slug: 'summer', description: 'Summer theme', color_primary: '#FF0000', color_secondary: '#00FF00', filters: [{ type: 'keyword', value: 'summer', match_mode: 'any' }] }],
|
|
530
|
+
}))
|
|
531
|
+
|
|
532
|
+
await themes.suggestions({ ai: true, tags: 'summer,beach' })
|
|
533
|
+
const [url] = fetchFn.mock.calls[0]
|
|
534
|
+
expect(url).toContain('/themes/suggestions')
|
|
535
|
+
const parsed = new URL(url, 'http://localhost')
|
|
536
|
+
expect(parsed.searchParams.get('ai')).toBe('true')
|
|
537
|
+
expect(parsed.searchParams.get('tags')).toBe('summer,beach')
|
|
538
|
+
})
|
|
539
|
+
})
|
|
540
|
+
|
|
341
541
|
describe('SearchResource', () => {
|
|
342
542
|
let fetchFn: ReturnType<typeof vi.fn>
|
|
343
543
|
let search: SearchResource
|
package/src/resources/index.ts
CHANGED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { z } from 'zod/v4'
|
|
2
|
+
import type { VerbatimsClient } from '../client'
|
|
3
|
+
import { apiResponseSchema } from '../types'
|
|
4
|
+
import { paginate } from '../pagination'
|
|
5
|
+
import type {
|
|
6
|
+
Theme,
|
|
7
|
+
ThemeFilter,
|
|
8
|
+
ThemeSuggestion,
|
|
9
|
+
ThemeFeed,
|
|
10
|
+
ThemeWithDetails,
|
|
11
|
+
ListThemesParams,
|
|
12
|
+
CreateThemeData,
|
|
13
|
+
UpdateThemeData,
|
|
14
|
+
AddFilterData,
|
|
15
|
+
FilterSuggestion,
|
|
16
|
+
FilterRecommendationsData,
|
|
17
|
+
FilterRecommendation,
|
|
18
|
+
ThemeNameSuggestion,
|
|
19
|
+
ThemeSuggestionItem,
|
|
20
|
+
ActiveThemeParams,
|
|
21
|
+
FeedParams,
|
|
22
|
+
ThemeSuggestionsQuery,
|
|
23
|
+
} from '../types'
|
|
24
|
+
|
|
25
|
+
const themeSchema = z.object({
|
|
26
|
+
id: z.number(),
|
|
27
|
+
slug: z.string(),
|
|
28
|
+
name: z.string(),
|
|
29
|
+
description: z.string().nullable(),
|
|
30
|
+
language: z.string().nullable(),
|
|
31
|
+
isActive: z.boolean(),
|
|
32
|
+
isDefault: z.boolean(),
|
|
33
|
+
scheduledStart: z.string().nullable(),
|
|
34
|
+
scheduledEnd: z.string().nullable(),
|
|
35
|
+
priority: z.number(),
|
|
36
|
+
config: z.record(z.string(), z.unknown()).nullable(),
|
|
37
|
+
createdAt: z.string(),
|
|
38
|
+
updatedAt: z.string(),
|
|
39
|
+
filters_count: z.number(),
|
|
40
|
+
pending_suggestions_count: z.number().optional(),
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const themeListResponseSchema = apiResponseSchema(z.array(themeSchema))
|
|
44
|
+
const themeSingleResponseSchema = apiResponseSchema(themeSchema)
|
|
45
|
+
|
|
46
|
+
const themeFilterSchema = z.object({
|
|
47
|
+
id: z.number(),
|
|
48
|
+
themeId: z.number(),
|
|
49
|
+
type: z.string(),
|
|
50
|
+
value: z.string(),
|
|
51
|
+
matchMode: z.enum(['any', 'all']),
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
const themeSuggestionSchema = z.object({
|
|
55
|
+
id: z.number(),
|
|
56
|
+
themeId: z.number(),
|
|
57
|
+
enrichmentJobId: z.number().nullable(),
|
|
58
|
+
type: z.enum(['tag', 'author', 'reference']),
|
|
59
|
+
suggestedValue: z.string(),
|
|
60
|
+
context: z.string().nullable(),
|
|
61
|
+
status: z.enum(['pending', 'accepted', 'rejected']),
|
|
62
|
+
createdBy: z.number().nullable(),
|
|
63
|
+
reviewedBy: z.number().nullable(),
|
|
64
|
+
reviewedAt: z.string().nullable(),
|
|
65
|
+
createdAt: z.string(),
|
|
66
|
+
updatedAt: z.string(),
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
const themeFeedSchema = z.object({
|
|
70
|
+
theme: themeSchema,
|
|
71
|
+
quotes: z.array(z.object({
|
|
72
|
+
id: z.number(),
|
|
73
|
+
name: z.string(),
|
|
74
|
+
language: z.string(),
|
|
75
|
+
created_at: z.string(),
|
|
76
|
+
views_count: z.number(),
|
|
77
|
+
likes_count: z.number(),
|
|
78
|
+
updated_at: z.string(),
|
|
79
|
+
author: z.object({ id: z.number(), name: z.string() }).optional(),
|
|
80
|
+
reference: z.object({ id: z.number(), name: z.string() }).optional(),
|
|
81
|
+
})),
|
|
82
|
+
authors: z.array(z.object({
|
|
83
|
+
id: z.number(),
|
|
84
|
+
name: z.string(),
|
|
85
|
+
job: z.string(),
|
|
86
|
+
description: z.string(),
|
|
87
|
+
image_url: z.string(),
|
|
88
|
+
likes_count: z.number(),
|
|
89
|
+
})),
|
|
90
|
+
references: z.array(z.object({
|
|
91
|
+
id: z.number(),
|
|
92
|
+
name: z.string(),
|
|
93
|
+
description: z.string(),
|
|
94
|
+
primary_type: z.string(),
|
|
95
|
+
image_url: z.string(),
|
|
96
|
+
likes_count: z.number(),
|
|
97
|
+
})),
|
|
98
|
+
total: z.number(),
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
type ThemeItem = z.infer<typeof themeSchema>
|
|
102
|
+
|
|
103
|
+
export class ThemesResource {
|
|
104
|
+
constructor(private client: VerbatimsClient) {}
|
|
105
|
+
|
|
106
|
+
async getActive(params?: ActiveThemeParams) {
|
|
107
|
+
return this.client.get('/themes/active', { params: params as Record<string, unknown> }, apiResponseSchema(themeSchema.nullable()))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async getFeed(slug: string, params?: FeedParams) {
|
|
111
|
+
return this.client.get(`/themes/${slug}/feed`, { params: params as Record<string, unknown> }, apiResponseSchema(themeFeedSchema.nullable()))
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async list(params?: ListThemesParams) {
|
|
115
|
+
return this.client.get('/themes', { params: params as Record<string, unknown> }, themeListResponseSchema)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
paginate(params?: ListThemesParams): AsyncGenerator<ThemeItem> {
|
|
119
|
+
return paginate<ThemeItem>((page) =>
|
|
120
|
+
this.list({ ...params, page }).then(r => ({
|
|
121
|
+
data: r.data,
|
|
122
|
+
pagination: r.pagination,
|
|
123
|
+
}))
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async get(id: number) {
|
|
128
|
+
return this.client.get(`/themes/${id}`, {}, apiResponseSchema(themeSchema.and(z.object({
|
|
129
|
+
filters: z.array(themeFilterSchema),
|
|
130
|
+
translations: z.array(z.object({
|
|
131
|
+
id: z.number(),
|
|
132
|
+
themeId: z.number(),
|
|
133
|
+
language: z.string(),
|
|
134
|
+
name: z.string(),
|
|
135
|
+
description: z.string().nullable(),
|
|
136
|
+
})),
|
|
137
|
+
}))))
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async create(data: CreateThemeData) {
|
|
141
|
+
return this.client.post('/themes', data, {}, themeSingleResponseSchema)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async update(id: number, data: UpdateThemeData) {
|
|
145
|
+
return this.client.put(`/themes/${id}`, data, {}, themeSingleResponseSchema)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async delete(id: number) {
|
|
149
|
+
return this.client.delete(`/themes/${id}`, {}, apiResponseSchema(z.undefined()))
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async activate(id: number, isActive: boolean) {
|
|
153
|
+
return this.client.put(`/themes/${id}/activate`, { is_active: isActive }, {}, themeSingleResponseSchema)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async setDefault(id: number, isDefault: boolean) {
|
|
157
|
+
return this.client.put(`/themes/${id}/default`, { is_default: isDefault }, {}, themeSingleResponseSchema)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async addFilter(id: number, data: AddFilterData) {
|
|
161
|
+
return this.client.post(`/themes/${id}/filters`, data, {}, apiResponseSchema(themeFilterSchema))
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async removeFilter(id: number, filterId: number) {
|
|
165
|
+
return this.client.delete(`/themes/${id}/filters/${filterId}`, {}, apiResponseSchema(z.undefined()))
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async listSuggestions(id: number) {
|
|
169
|
+
return this.client.get(`/themes/${id}/suggestions`, {}, apiResponseSchema(z.array(themeSuggestionSchema)))
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async reviewSuggestion(id: number, suggestionId: number, action: 'accepted' | 'rejected') {
|
|
173
|
+
return this.client.put(`/themes/${id}/suggestions/${suggestionId}`, { action }, {}, apiResponseSchema(themeSuggestionSchema))
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async filterSuggestions(params: { q: string; type: string }) {
|
|
177
|
+
return this.client.get('/themes/filter-suggestions', { params: params as Record<string, unknown> }, apiResponseSchema(z.array(z.object({
|
|
178
|
+
label: z.string(),
|
|
179
|
+
value: z.string(),
|
|
180
|
+
}))))
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async filterRecommendations(data: FilterRecommendationsData) {
|
|
184
|
+
return this.client.post('/themes/filter-recommendations', data, {}, apiResponseSchema(z.array(z.object({
|
|
185
|
+
type: z.string(),
|
|
186
|
+
value: z.string(),
|
|
187
|
+
label: z.string(),
|
|
188
|
+
}))))
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async suggestName(name: string) {
|
|
192
|
+
return this.client.post('/themes/suggest-name', { name }, {}, apiResponseSchema(z.object({
|
|
193
|
+
name: z.string(),
|
|
194
|
+
slug: z.string(),
|
|
195
|
+
description: z.string(),
|
|
196
|
+
})))
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async suggestions(params?: ThemeSuggestionsQuery) {
|
|
200
|
+
return this.client.get('/themes/suggestions', { params: params as Record<string, unknown> }, apiResponseSchema(z.array(z.object({
|
|
201
|
+
type: z.enum(['tag', 'author', 'reference']),
|
|
202
|
+
name: z.string(),
|
|
203
|
+
slug: z.string(),
|
|
204
|
+
description: z.string(),
|
|
205
|
+
color_primary: z.string(),
|
|
206
|
+
color_secondary: z.string(),
|
|
207
|
+
filters: z.array(z.object({
|
|
208
|
+
type: z.string(),
|
|
209
|
+
value: z.string(),
|
|
210
|
+
match_mode: z.literal('any'),
|
|
211
|
+
})),
|
|
212
|
+
}))))
|
|
213
|
+
}
|
|
214
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -235,3 +235,186 @@ export interface CreateCollectionData {
|
|
|
235
235
|
description?: string | null
|
|
236
236
|
is_public?: boolean
|
|
237
237
|
}
|
|
238
|
+
|
|
239
|
+
// --- Theme types ---
|
|
240
|
+
|
|
241
|
+
export interface Theme {
|
|
242
|
+
id: number
|
|
243
|
+
slug: string
|
|
244
|
+
name: string
|
|
245
|
+
description: string | null
|
|
246
|
+
language: string | null
|
|
247
|
+
isActive: boolean
|
|
248
|
+
isDefault: boolean
|
|
249
|
+
scheduledStart: string | null
|
|
250
|
+
scheduledEnd: string | null
|
|
251
|
+
priority: number
|
|
252
|
+
config: Record<string, unknown> | null
|
|
253
|
+
createdAt: string
|
|
254
|
+
updatedAt: string
|
|
255
|
+
filters_count: number
|
|
256
|
+
pending_suggestions_count?: number
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export interface ThemeFilter {
|
|
260
|
+
id: number
|
|
261
|
+
themeId: number
|
|
262
|
+
type: string
|
|
263
|
+
value: string
|
|
264
|
+
matchMode: 'any' | 'all'
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export interface ThemeTranslation {
|
|
268
|
+
id: number
|
|
269
|
+
themeId: number
|
|
270
|
+
language: string
|
|
271
|
+
name: string
|
|
272
|
+
description: string | null
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export interface ThemeSuggestion {
|
|
276
|
+
id: number
|
|
277
|
+
themeId: number
|
|
278
|
+
enrichmentJobId: number | null
|
|
279
|
+
type: 'tag' | 'author' | 'reference'
|
|
280
|
+
suggestedValue: string
|
|
281
|
+
context: string | null
|
|
282
|
+
status: 'pending' | 'accepted' | 'rejected'
|
|
283
|
+
createdBy: number | null
|
|
284
|
+
reviewedBy: number | null
|
|
285
|
+
reviewedAt: string | null
|
|
286
|
+
createdAt: string
|
|
287
|
+
updatedAt: string
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export interface ThemeFeedQuote {
|
|
291
|
+
id: number
|
|
292
|
+
name: string
|
|
293
|
+
language: string
|
|
294
|
+
created_at: string
|
|
295
|
+
views_count: number
|
|
296
|
+
likes_count: number
|
|
297
|
+
updated_at: string
|
|
298
|
+
author?: { id: number; name: string }
|
|
299
|
+
reference?: { id: number; name: string }
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export interface ThemeFeedAuthor {
|
|
303
|
+
id: number
|
|
304
|
+
name: string
|
|
305
|
+
job: string
|
|
306
|
+
description: string
|
|
307
|
+
image_url: string
|
|
308
|
+
likes_count: number
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export interface ThemeFeedReference {
|
|
312
|
+
id: number
|
|
313
|
+
name: string
|
|
314
|
+
description: string
|
|
315
|
+
primary_type: string
|
|
316
|
+
image_url: string
|
|
317
|
+
likes_count: number
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export interface ThemeFeed {
|
|
321
|
+
theme: Theme
|
|
322
|
+
quotes: ThemeFeedQuote[]
|
|
323
|
+
authors: ThemeFeedAuthor[]
|
|
324
|
+
references: ThemeFeedReference[]
|
|
325
|
+
total: number
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export interface ThemeWithDetails extends Theme {
|
|
329
|
+
filters: ThemeFilter[]
|
|
330
|
+
translations: ThemeTranslation[]
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export interface ListThemesParams {
|
|
334
|
+
page?: number
|
|
335
|
+
limit?: number
|
|
336
|
+
search?: string
|
|
337
|
+
sort_by?: 'priority' | 'name' | 'slug'
|
|
338
|
+
sort_order?: 'asc' | 'desc'
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export interface CreateThemeData {
|
|
342
|
+
slug: string
|
|
343
|
+
name: string
|
|
344
|
+
description?: string | null
|
|
345
|
+
language?: string | null
|
|
346
|
+
translations?: Array<{ language: string; name: string; description?: string | null }>
|
|
347
|
+
is_active?: boolean
|
|
348
|
+
is_default?: boolean
|
|
349
|
+
priority?: number
|
|
350
|
+
scheduled_start?: string | null
|
|
351
|
+
scheduled_end?: string | null
|
|
352
|
+
config?: Record<string, unknown> | null
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export interface UpdateThemeData {
|
|
356
|
+
slug?: string
|
|
357
|
+
name?: string
|
|
358
|
+
description?: string | null
|
|
359
|
+
language?: string | null
|
|
360
|
+
is_active?: boolean
|
|
361
|
+
is_default?: boolean
|
|
362
|
+
priority?: number
|
|
363
|
+
scheduled_start?: string | null
|
|
364
|
+
scheduled_end?: string | null
|
|
365
|
+
config?: Record<string, unknown> | string | null
|
|
366
|
+
translations?: Array<{ language: string; name: string; description?: string | null }>
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
export interface AddFilterData {
|
|
370
|
+
type: string
|
|
371
|
+
value: string
|
|
372
|
+
match_mode?: 'any' | 'all'
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export interface FilterSuggestion {
|
|
376
|
+
label: string
|
|
377
|
+
value: string
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export interface FilterRecommendationsData {
|
|
381
|
+
name?: string
|
|
382
|
+
filters?: Array<{ type: string; value: string }>
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export interface FilterRecommendation {
|
|
386
|
+
type: string
|
|
387
|
+
value: string
|
|
388
|
+
label: string
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
export interface ThemeNameSuggestion {
|
|
392
|
+
name: string
|
|
393
|
+
slug: string
|
|
394
|
+
description: string
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export interface ThemeSuggestionItem {
|
|
398
|
+
type: 'tag' | 'author' | 'reference'
|
|
399
|
+
name: string
|
|
400
|
+
slug: string
|
|
401
|
+
description: string
|
|
402
|
+
color_primary: string
|
|
403
|
+
color_secondary: string
|
|
404
|
+
filters: Array<{ type: string; value: string; match_mode: 'any' }>
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
export interface ActiveThemeParams {
|
|
408
|
+
language?: string
|
|
409
|
+
theme?: string
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
export interface FeedParams {
|
|
413
|
+
language?: string
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
export interface ThemeSuggestionsQuery {
|
|
417
|
+
ai?: boolean
|
|
418
|
+
tags?: string
|
|
419
|
+
language?: string
|
|
420
|
+
}
|