directify-cli 1.0.0 → 1.1.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/README.md CHANGED
@@ -226,6 +226,55 @@ directify articles delete 789
226
226
  directify articles exists --slug "best-italian-restaurants"
227
227
  ```
228
228
 
229
+ ### Custom Pages
230
+
231
+ ```bash
232
+ # List
233
+ directify pages list
234
+ directify pages ls --json
235
+
236
+ # Get
237
+ directify pages get 12
238
+
239
+ # Create a page in the navbar
240
+ directify pages create \
241
+ --title "About Us" \
242
+ --markdown "# About Us\n\nWe are a directory of the best restaurants..." \
243
+ --placement navbar \
244
+ --seo-title "About Us" \
245
+ --seo-description "Learn about our restaurant directory"
246
+
247
+ # Create a programmatic SEO page (unlisted by default)
248
+ directify pages create \
249
+ --title "NYC vs Chicago Pizza" \
250
+ --markdown "# NYC vs Chicago Pizza\n\nA detailed comparison..." \
251
+ --seo-title "Best Pizza: NYC vs Chicago Compared" \
252
+ --seo-description "Compare pizza styles between New York and Chicago"
253
+
254
+ # Create a footer link
255
+ directify pages create \
256
+ --title "Terms of Service" \
257
+ --markdown "# Terms of Service\n\n..." \
258
+ --placement footer \
259
+ --order 1
260
+
261
+ # Create an external link in the navbar
262
+ directify pages create \
263
+ --title "Submit a Listing" \
264
+ --external-url "https://forms.google.com/your-form" \
265
+ --placement navbar \
266
+ --new-tab
267
+
268
+ # Update
269
+ directify pages update 12 --title "Updated Title" --placement footer
270
+
271
+ # Toggle published/unpublished
272
+ directify pages toggle 12
273
+
274
+ # Delete
275
+ directify pages delete 12
276
+ ```
277
+
229
278
  ## Global Options
230
279
 
231
280
  All resource commands support these options:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "directify-cli",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "Official CLI tool for Directify - manage your directories, listings, categories, tags, and articles from the command line.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -0,0 +1,187 @@
1
+ import { Command } from 'commander';
2
+ import { api, resolveDirectory } from '../utils/api.js';
3
+ import { printTable, printJson, printSuccess, printError } from '../utils/output.js';
4
+ import ora from 'ora';
5
+
6
+ const pages = new Command('pages').description('Manage custom pages');
7
+
8
+ pages
9
+ .command('list')
10
+ .alias('ls')
11
+ .description('List all custom pages')
12
+ .option('-d, --directory <id>', 'Directory ID')
13
+ .option('--json', 'Output as JSON')
14
+ .action(async (opts) => {
15
+ const spinner = ora('Fetching pages...').start();
16
+ try {
17
+ const dir = resolveDirectory(opts);
18
+ const data = await api.get(`/directories/${dir}/pages`);
19
+ spinner.stop();
20
+ if (opts.json) {
21
+ printJson(data);
22
+ } else {
23
+ const items = data.data || data;
24
+ printTable(items, [
25
+ { key: 'id', label: 'ID' },
26
+ { key: 'title', label: 'Title', maxWidth: 40 },
27
+ { key: 'slug', label: 'Slug', maxWidth: 25 },
28
+ { key: 'placement', label: 'Placement' },
29
+ { key: 'is_published', label: 'Published' },
30
+ { key: 'order', label: 'Order' },
31
+ ]);
32
+ if (data.meta) {
33
+ console.log(`\nPage ${data.meta.current_page} of ${data.meta.last_page} (${data.meta.total} total)`);
34
+ }
35
+ }
36
+ } catch (err) {
37
+ spinner.stop();
38
+ printError(err.message);
39
+ process.exit(1);
40
+ }
41
+ });
42
+
43
+ pages
44
+ .command('get <id>')
45
+ .description('Get a specific page')
46
+ .option('-d, --directory <id>', 'Directory ID')
47
+ .action(async (id, opts) => {
48
+ try {
49
+ const dir = resolveDirectory(opts);
50
+ const data = await api.get(`/directories/${dir}/pages/${id}`);
51
+ printJson(data.data || data);
52
+ } catch (err) {
53
+ printError(err.message);
54
+ process.exit(1);
55
+ }
56
+ });
57
+
58
+ pages
59
+ .command('create')
60
+ .description('Create a new custom page')
61
+ .requiredOption('--title <title>', 'Page title')
62
+ .option('--slug <slug>', 'URL slug (auto-generated from title if not set)')
63
+ .option('--markdown <text>', 'Page content in markdown')
64
+ .option('--placement <type>', 'Where the link appears: navbar, footer, sidebar, unlisted (default: unlisted)')
65
+ .option('--order <n>', 'Sort order for navigation', '0')
66
+ .option('--seo-title <text>', 'SEO title')
67
+ .option('--seo-description <text>', 'SEO description')
68
+ .option('--external-url <url>', 'External URL (makes this a link, not a page)')
69
+ .option('--new-tab', 'Open external links in new tab')
70
+ .option('--unpublished', 'Create as unpublished')
71
+ .option('-d, --directory <id>', 'Directory ID')
72
+ .action(async (opts) => {
73
+ const spinner = ora('Creating page...').start();
74
+ try {
75
+ const dir = resolveDirectory(opts);
76
+ const body = { title: opts.title };
77
+ if (opts.slug) body.slug = opts.slug;
78
+ if (opts.markdown) body.markdown = opts.markdown;
79
+ if (opts.placement) body.placement = opts.placement;
80
+ if (opts.order) body.order = parseInt(opts.order, 10);
81
+ if (opts.externalUrl) {
82
+ body.is_external = true;
83
+ body.external_url = opts.externalUrl;
84
+ }
85
+ if (opts.newTab) body.new_tab = true;
86
+ if (opts.seoTitle || opts.seoDescription) {
87
+ body.seo = {};
88
+ if (opts.seoTitle) body.seo.title = opts.seoTitle;
89
+ if (opts.seoDescription) body.seo.description = opts.seoDescription;
90
+ }
91
+ body.is_published = !opts.unpublished;
92
+
93
+ const data = await api.post(`/directories/${dir}/pages`, body);
94
+ spinner.stop();
95
+ const result = data.data || data;
96
+ printSuccess(`Page created: ${result.title} (ID: ${result.id})`);
97
+ } catch (err) {
98
+ spinner.stop();
99
+ printError(err.message);
100
+ process.exit(1);
101
+ }
102
+ });
103
+
104
+ pages
105
+ .command('update <id>')
106
+ .description('Update a custom page')
107
+ .option('--title <title>', 'Page title')
108
+ .option('--slug <slug>', 'URL slug')
109
+ .option('--markdown <text>', 'Page content in markdown')
110
+ .option('--placement <type>', 'Where the link appears: navbar, footer, sidebar, unlisted')
111
+ .option('--order <n>', 'Sort order for navigation')
112
+ .option('--seo-title <text>', 'SEO title')
113
+ .option('--seo-description <text>', 'SEO description')
114
+ .option('--external-url <url>', 'External URL')
115
+ .option('--new-tab <bool>', 'Open in new tab (true/false)')
116
+ .option('--published <bool>', 'Published status (true/false)')
117
+ .option('-d, --directory <id>', 'Directory ID')
118
+ .action(async (id, opts) => {
119
+ const spinner = ora('Updating page...').start();
120
+ try {
121
+ const dir = resolveDirectory(opts);
122
+ const body = {};
123
+ if (opts.title) body.title = opts.title;
124
+ if (opts.slug) body.slug = opts.slug;
125
+ if (opts.markdown) body.markdown = opts.markdown;
126
+ if (opts.placement) body.placement = opts.placement;
127
+ if (opts.order !== undefined) body.order = parseInt(opts.order, 10);
128
+ if (opts.externalUrl) {
129
+ body.is_external = true;
130
+ body.external_url = opts.externalUrl;
131
+ }
132
+ if (opts.newTab !== undefined) body.new_tab = opts.newTab === 'true';
133
+ if (opts.published !== undefined) body.is_published = opts.published === 'true';
134
+ if (opts.seoTitle || opts.seoDescription) {
135
+ body.seo = {};
136
+ if (opts.seoTitle) body.seo.title = opts.seoTitle;
137
+ if (opts.seoDescription) body.seo.description = opts.seoDescription;
138
+ }
139
+
140
+ const data = await api.put(`/directories/${dir}/pages/${id}`, body);
141
+ spinner.stop();
142
+ printSuccess(`Page updated: ${data.data?.title || data.title}`);
143
+ } catch (err) {
144
+ spinner.stop();
145
+ printError(err.message);
146
+ process.exit(1);
147
+ }
148
+ });
149
+
150
+ pages
151
+ .command('delete <id>')
152
+ .description('Delete a custom page')
153
+ .option('-d, --directory <id>', 'Directory ID')
154
+ .action(async (id, opts) => {
155
+ const spinner = ora('Deleting page...').start();
156
+ try {
157
+ const dir = resolveDirectory(opts);
158
+ await api.delete(`/directories/${dir}/pages/${id}`);
159
+ spinner.stop();
160
+ printSuccess(`Page ${id} deleted.`);
161
+ } catch (err) {
162
+ spinner.stop();
163
+ printError(err.message);
164
+ process.exit(1);
165
+ }
166
+ });
167
+
168
+ pages
169
+ .command('toggle <id>')
170
+ .description('Toggle page published/unpublished status')
171
+ .option('-d, --directory <id>', 'Directory ID')
172
+ .action(async (id, opts) => {
173
+ const spinner = ora('Toggling page...').start();
174
+ try {
175
+ const dir = resolveDirectory(opts);
176
+ const data = await api.patch(`/directories/${dir}/pages/${id}/toggle`);
177
+ spinner.stop();
178
+ const result = data.data || data;
179
+ printSuccess(`Page "${result.title}" is now ${result.is_published ? 'published' : 'unpublished'}.`);
180
+ } catch (err) {
181
+ spinner.stop();
182
+ printError(err.message);
183
+ process.exit(1);
184
+ }
185
+ });
186
+
187
+ export default pages;
package/src/index.js CHANGED
@@ -9,6 +9,7 @@ import tags from './commands/tags.js';
9
9
  import fields from './commands/fields.js';
10
10
  import listings from './commands/listings.js';
11
11
  import articles from './commands/articles.js';
12
+ import pages from './commands/pages.js';
12
13
 
13
14
  const program = new Command();
14
15
 
@@ -25,5 +26,6 @@ program.addCommand(tags);
25
26
  program.addCommand(fields);
26
27
  program.addCommand(listings);
27
28
  program.addCommand(articles);
29
+ program.addCommand(pages);
28
30
 
29
31
  program.parse();