@verbatims/cli 0.1.6 → 0.1.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verbatims/cli",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "CLI for the Verbatims quotes API",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,5 +1,5 @@
1
1
  import type { Command } from 'commander'
2
- import { text, isCancel, cancel, confirm } from '@clack/prompts'
2
+ import { text, isCancel, cancel, confirm, select } from '@clack/prompts'
3
3
  import chalk from 'chalk'
4
4
  import { getClient } from '../utils/client.js'
5
5
  import { output, type Format } from '../utils/format.js'
@@ -21,6 +21,7 @@ export function registerQuotesCommand(program: Command) {
21
21
  .option('--author <id>', 'Filter by author ID')
22
22
  .option('--tag <name>', 'Filter by tag')
23
23
  .option('--search <q>', 'Search in quote text')
24
+ .option('--status <status>', 'Filter by status (draft|pending|approved|rejected)')
24
25
  .option('--sort-by <field>', 'Sort field')
25
26
  .option('--sort-order <order>', 'Sort order (asc|desc)')
26
27
  .action(async (options: Record<string, string>) => {
@@ -34,6 +35,8 @@ export function registerQuotesCommand(program: Command) {
34
35
  author_id: options.author ? Number(options.author) : undefined,
35
36
  tag: options.tag,
36
37
  search: options.search,
38
+ q: options.search,
39
+ status: options.status as any,
37
40
  sort_by: options.sortBy,
38
41
  sort_order: options.sortOrder as 'asc' | 'desc',
39
42
  }),
@@ -121,6 +124,49 @@ export function registerQuotesCommand(program: Command) {
121
124
  console.log(chalk.green(` Quote #${id} deleted`))
122
125
  })
123
126
 
127
+ quotes
128
+ .command('submit <id>')
129
+ .description('Submit a draft quote for review (draft → pending)')
130
+ .action(async (id: string) => {
131
+ const client = await getClient()
132
+ const format = getFormat(program)
133
+ const { data } = await withSpinner('Submitting quote', () => client.quotes.submit(Number(id)), format)
134
+ console.log(chalk.green(' Quote submitted for review'))
135
+ output(data, format)
136
+ })
137
+
138
+ quotes
139
+ .command('moderate <id>')
140
+ .description('Moderate a pending quote (pending → approved|rejected)')
141
+ .option('--action <action>', 'Action to take: approve or reject')
142
+ .option('--reason <reason>', 'Rejection reason (required when rejecting)')
143
+ .action(async (id: string, options: Record<string, string>) => {
144
+ const client = await getClient()
145
+ const format = getFormat(program)
146
+
147
+ const action = options.action ?? await select({
148
+ message: 'Action',
149
+ options: [
150
+ { value: 'approve', label: 'Approve' },
151
+ { value: 'reject', label: 'Reject' },
152
+ ],
153
+ })
154
+ if (isCancel(action)) cancel('Cancelled')
155
+
156
+ let rejection_reason: string | undefined | null
157
+ if (action === 'reject') {
158
+ rejection_reason = options.reason ?? await text({ message: 'Rejection reason' }) as string
159
+ if (isCancel(rejection_reason)) cancel('Cancelled')
160
+ }
161
+
162
+ const { data } = await withSpinner('Moderating quote', () =>
163
+ client.quotes.moderate(Number(id), { action: action as 'approve' | 'reject', rejection_reason }),
164
+ format,
165
+ )
166
+ console.log(chalk.green(` Quote ${action === 'approve' ? 'approved' : 'rejected'}`))
167
+ output(data, format)
168
+ })
169
+
124
170
  quotes
125
171
  .command('browse')
126
172
  .description('Browse quotes interactively')
@@ -128,6 +174,7 @@ export function registerQuotesCommand(program: Command) {
128
174
  .option('--author <id>', 'Filter by author ID')
129
175
  .option('--tag <name>', 'Filter by tag')
130
176
  .option('--search <q>', 'Search in quote text')
177
+ .option('--status <status>', 'Filter by status (draft|pending|approved|rejected)')
131
178
  .option('--sort-by <field>', 'Sort field')
132
179
  .option('--sort-order <order>', 'Sort order (asc|desc)')
133
180
  .action(async (options: Record<string, string>) => {
@@ -140,6 +187,8 @@ export function registerQuotesCommand(program: Command) {
140
187
  author_id: options.author ? Number(options.author) : undefined,
141
188
  tag: options.tag,
142
189
  search: options.search,
190
+ q: options.search,
191
+ status: options.status as any,
143
192
  sort_by: options.sortBy,
144
193
  sort_order: options.sortOrder as 'asc' | 'desc',
145
194
  }),
@@ -57,7 +57,11 @@ export function registerThemesCommand(program: Command) {
57
57
  .option('--active', 'Set as active')
58
58
  .option('--default', 'Set as default')
59
59
  .option('--priority <n>', 'Priority', '0')
60
- .action(async (options: Record<string, string>) => {
60
+ .option('--scheduled-start <date>', 'Scheduled start date (ISO 8601)')
61
+ .option('--scheduled-end <date>', 'Scheduled end date (ISO 8601)')
62
+ .option('--config <json>', 'Theme configuration as JSON')
63
+ .option('--translation <lang:name:desc>', 'Add a translation (can be repeated)', (val: string, prev: string[]) => { prev.push(val); return prev }, [])
64
+ .action(async (options: Record<string, string | string[]>) => {
61
65
  const client = await getClient()
62
66
  const format = getFormat(program)
63
67
 
@@ -70,15 +74,33 @@ export function registerThemesCommand(program: Command) {
70
74
  const description = options.description ?? await text({ message: 'Description (optional)', initialValue: '' })
71
75
  if (isCancel(description)) cancel('Cancelled')
72
76
 
77
+ const translations = Array.isArray(options.translation) ? options.translation.map((t: string) => {
78
+ const [lang, tName, tDesc] = t.split(':')
79
+ return { language: lang!, name: tName!, description: tDesc || null }
80
+ }) : undefined
81
+
82
+ let config: Record<string, unknown> | string | null | undefined
83
+ if (options.config) {
84
+ try {
85
+ config = JSON.parse(options.config as string)
86
+ } catch {
87
+ config = options.config as string
88
+ }
89
+ }
90
+
73
91
  const { data } = await withSpinner('Creating theme', () =>
74
92
  client.themes.create({
75
93
  slug: slug as string,
76
94
  name: name as string,
77
95
  description: (description as string) || null,
78
- language: options.language || null,
96
+ language: (options.language as string) || null,
79
97
  is_active: options.active !== undefined ? true : undefined,
80
98
  is_default: options.default !== undefined ? true : undefined,
81
99
  priority: options.priority ? Number(options.priority) : undefined,
100
+ scheduled_start: (options.scheduledStart as string) || null,
101
+ scheduled_end: (options.scheduledEnd as string) || null,
102
+ config,
103
+ translations,
82
104
  }),
83
105
  format,
84
106
  )
@@ -94,7 +116,11 @@ export function registerThemesCommand(program: Command) {
94
116
  .option('--description <text>', 'Description')
95
117
  .option('--language <lang>', 'Language')
96
118
  .option('--priority <n>', 'Priority')
97
- .action(async (id: string, options: Record<string, string>) => {
119
+ .option('--scheduled-start <date>', 'Scheduled start date (ISO 8601)')
120
+ .option('--scheduled-end <date>', 'Scheduled end date (ISO 8601)')
121
+ .option('--config <json>', 'Theme configuration as JSON')
122
+ .option('--translation <lang:name:desc>', 'Add a translation (can be repeated)', (val: string, prev: string[]) => { prev.push(val); return prev }, [])
123
+ .action(async (id: string, options: Record<string, string | string[]>) => {
98
124
  const client = await getClient()
99
125
  const format = getFormat(program)
100
126
 
@@ -102,8 +128,23 @@ export function registerThemesCommand(program: Command) {
102
128
  if (options.slug) data_.slug = options.slug
103
129
  if (options.name) data_.name = options.name
104
130
  if (options.description !== undefined) data_.description = options.description || null
105
- if (options.language !== undefined) data_.language = options.language || null
131
+ if (options.language !== undefined) data_.language = (options.language as string) || null
106
132
  if (options.priority !== undefined) data_.priority = Number(options.priority)
133
+ if (options.scheduledStart !== undefined) data_.scheduled_start = (options.scheduledStart as string) || null
134
+ if (options.scheduledEnd !== undefined) data_.scheduled_end = (options.scheduledEnd as string) || null
135
+ if (options.config !== undefined) {
136
+ try {
137
+ data_.config = JSON.parse(options.config as string)
138
+ } catch {
139
+ data_.config = options.config as string
140
+ }
141
+ }
142
+ if (Array.isArray(options.translation) && options.translation.length > 0) {
143
+ data_.translations = options.translation.map((t: string) => {
144
+ const [lang, tName, tDesc] = t.split(':')
145
+ return { language: lang!, name: tName!, description: tDesc || null }
146
+ })
147
+ }
107
148
 
108
149
  const { data } = await withSpinner('Updating theme', () => client.themes.update(Number(id), data_ as any), format)
109
150
  console.log(chalk.green(' Theme updated'))