@kaushalparajuli/react-crud-ui 1.1.13 → 1.2.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/bin/cli.js CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * CRUD Module Generator CLI
4
- *
4
+ *
5
5
  * Usage: npx @kaushalparajuli/react-crud-ui generate <module-name>
6
6
  * Example: npx @kaushalparajuli/react-crud-ui generate blog
7
- *
7
+ *
8
8
  * This will create:
9
9
  * - src/pages/<module>/ModuleListPage.jsx
10
10
  * - src/pages/<module>/ModuleFormPage.jsx
@@ -12,15 +12,15 @@
12
12
  * - src/stores/use<Module>Store.js
13
13
  */
14
14
 
15
- import fs from 'fs';
16
- import path from 'path';
15
+ import fs from "fs";
16
+ import path from "path";
17
17
 
18
18
  const args = process.argv.slice(2);
19
19
  const command = args[0];
20
20
  const moduleName = args[1];
21
21
 
22
22
  // Show help if no command
23
- if (!command || command === '--help' || command === '-h') {
23
+ if (!command || command === "--help" || command === "-h") {
24
24
  console.log(`
25
25
  @kaushalparajuli/react-crud-ui CLI
26
26
 
@@ -44,37 +44,42 @@ Examples:
44
44
  }
45
45
 
46
46
  // Show version
47
- if (command === '--version' || command === '-v') {
47
+ if (command === "--version" || command === "-v") {
48
48
  try {
49
- const packageJson = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf-8'));
49
+ const packageJson = JSON.parse(
50
+ fs.readFileSync(new URL("../package.json", import.meta.url), "utf-8"),
51
+ );
50
52
  console.log(packageJson.version);
51
53
  } catch {
52
- console.log('Unknown version');
54
+ console.log("Unknown version");
53
55
  }
54
56
  process.exit(0);
55
57
  }
56
58
 
57
59
  // Handle generate command
58
- if (command === 'generate' || command === 'g') {
60
+ if (command === "generate" || command === "g") {
59
61
  if (!moduleName) {
60
- console.error('❌ Please provide a module name');
61
- console.log('Usage: npx @kaushalparajuli/react-crud-ui generate <module-name>');
62
- console.log('Example: npx @kaushalparajuli/react-crud-ui generate blog');
62
+ console.error("❌ Please provide a module name");
63
+ console.log(
64
+ "Usage: npx @kaushalparajuli/react-crud-ui generate <module-name>",
65
+ );
66
+ console.log("Example: npx @kaushalparajuli/react-crud-ui generate blog");
63
67
  process.exit(1);
64
68
  }
65
69
 
66
70
  generateModule(moduleName);
67
71
  } else {
68
72
  console.error(`❌ Unknown command: ${command}`);
69
- console.log('Run with --help to see available commands');
73
+ console.log("Run with --help to see available commands");
70
74
  process.exit(1);
71
75
  }
72
76
 
73
77
  function generateModule(name) {
74
78
  // Helper functions
75
79
  const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
76
- const kebabCase = (str) => str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
77
- const pascalCase = (str) => str.split(/[-_]/).map(capitalize).join('');
80
+ const kebabCase = (str) =>
81
+ str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
82
+ const pascalCase = (str) => str.split(/[-_]/).map(capitalize).join("");
78
83
  const camelCase = (str) => {
79
84
  const pascal = pascalCase(str);
80
85
  return pascal.charAt(0).toLowerCase() + pascal.slice(1);
@@ -86,18 +91,19 @@ function generateModule(name) {
86
91
  const moduleNameKebab = kebabCase(name);
87
92
 
88
93
  const cwd = process.cwd();
89
- const srcPath = path.join(cwd, 'src');
94
+ const srcPath = path.join(cwd, "src");
90
95
 
91
96
  // Check if src folder exists
92
97
  if (!fs.existsSync(srcPath)) {
93
- console.error('❌ Could not find src folder. Make sure you are in the root of your React project.');
98
+ console.error(
99
+ "❌ Could not find src folder. Make sure you are in the root of your React project.",
100
+ );
94
101
  process.exit(1);
95
102
  }
96
103
 
97
104
  // Templates
98
- const listPageTemplate = `import { Badge, CrudList } from '@kaushalparajuli/react-crud-ui';
105
+ const listPageTemplate = `import { Badge, CrudList,TimeStampFormat } from '@kaushalparajuli/react-crud-ui';
99
106
  import use${moduleNamePascal}Store from '@/stores/use${moduleNamePascal}Store';
100
- import { formatDate } from '@kaushalparajuli/react-crud-ui';
101
107
 
102
108
  // Column definitions (actions column is auto-added by DataTable)
103
109
  const columns = [
@@ -109,7 +115,7 @@ const columns = [
109
115
  {
110
116
  key: 'created_at',
111
117
  label: 'Created',
112
- render: (value) => formatDate(value),
118
+ render: (value) => TimeStampFormat(value),
113
119
  },
114
120
  {
115
121
  key: 'is_active',
@@ -155,6 +161,7 @@ export default function ${moduleNamePascal}FormPage() {
155
161
  listPath="/${moduleNameLower}s"
156
162
  useStore={use${moduleNamePascal}Store}
157
163
  >
164
+ <div className="flex flex-col gap-4">
158
165
  <QInput
159
166
  name="name"
160
167
  label="Name"
@@ -180,15 +187,16 @@ export default function ${moduleNamePascal}FormPage() {
180
187
  checked={form.values.is_active ?? true}
181
188
  onChange={(val) => setFormField('is_active', val)}
182
189
  />
190
+ </div>
183
191
  </CrudForm>
184
192
  );
185
193
  }
186
194
  `;
187
195
 
188
- const detailPageTemplate = `import { Box } from 'lucide-react';
189
- import { CrudDetail, Badge } from '@kaushalparajuli/react-crud-ui';
196
+ const detailPageTemplate = `import { CheckCircle2, FileText, XCircle, Activity, Box, ListOrdered } from 'lucide-react';
197
+ import { CrudDetail } from '@kaushalparajuli/react-crud-ui';
190
198
  import use${moduleNamePascal}Store from '@/stores/use${moduleNamePascal}Store';
191
- import { formatDate } from '@kaushalparajuli/react-crud-ui';
199
+ import DetailView from '@/components/features/DetailView';
192
200
 
193
201
  export default function ${moduleNamePascal}DetailPage() {
194
202
  return (
@@ -200,35 +208,73 @@ export default function ${moduleNamePascal}DetailPage() {
200
208
  editPath="/${moduleNameLower}s"
201
209
  useStore={use${moduleNamePascal}Store}
202
210
  getItemName={(item) => item?.name}
211
+ getBreadcrumbName={(item) => item?.name}
203
212
  >
204
- {(data) => (
205
- <div className="grid gap-4 md:grid-cols-2">
206
- <div className="space-y-2">
207
- <p className="text-sm font-medium text-muted-foreground">Name</p>
208
- <p className="text-sm">{data.name}</p>
209
- </div>
210
- <div className="space-y-2">
211
- <p className="text-sm font-medium text-muted-foreground">Status</p>
212
- <Badge variant={data.is_active ? 'success' : 'secondary'}>
213
- {data.is_active ? 'Active' : 'Inactive'}
214
- </Badge>
215
- </div>
216
- {data.description && (
217
- <div className="space-y-2 md:col-span-2">
218
- <p className="text-sm font-medium text-muted-foreground">Description</p>
219
- <p className="text-sm">{data.description}</p>
220
- </div>
221
- )}
222
- <div className="space-y-2">
223
- <p className="text-sm font-medium text-muted-foreground">Created</p>
224
- <p className="text-sm">{formatDate(data.created_at)}</p>
225
- </div>
226
- <div className="space-y-2">
227
- <p className="text-sm font-medium text-muted-foreground">Updated</p>
228
- <p className="text-sm">{formatDate(data.updated_at)}</p>
229
- </div>
230
- </div>
231
- )}
213
+ {(data) => {
214
+ if (!data) return null;
215
+
216
+ const mainSection = {
217
+ title: '${moduleNamePascal}',
218
+ description: '${moduleNamePascal} details',
219
+ icon: FileText,
220
+ columns: 2,
221
+ fields: [
222
+ {
223
+ label: 'Name',
224
+ value: data.name,
225
+ icon: FileText,
226
+ },
227
+ {
228
+ label: 'Description',
229
+ value: data.description || '-',
230
+ icon: Box,
231
+ },
232
+ {
233
+ label: 'Status',
234
+ value: data.is_active ? 'Active' : 'Inactive',
235
+ type: 'badge',
236
+ variant: data.is_active ? 'success' : 'destructive',
237
+ icon: data.is_active ? CheckCircle2 : XCircle,
238
+ },
239
+ ],
240
+ };
241
+
242
+ const timelineConfig = {
243
+ show: true,
244
+ data: data,
245
+ title: 'Timeline',
246
+ description: 'Creation and modification history',
247
+ showUserInfo: true,
248
+ };
249
+
250
+ const sidebarConfig = {
251
+ statsTitle: 'Quick Info',
252
+ statsIcon: Activity,
253
+ stats: [
254
+ {
255
+ label: 'Status',
256
+ value: data.is_active ? 'Active' : 'Inactive',
257
+ type: 'badge',
258
+ variant: data.is_active ? 'success' : 'secondary',
259
+ icon: data.is_active ? CheckCircle2 : XCircle,
260
+ },
261
+ {
262
+ label: 'ID',
263
+ value: data.id ?? '-',
264
+ icon: ListOrdered,
265
+ },
266
+ ],
267
+ };
268
+
269
+ return (
270
+ <DetailView
271
+ data={data}
272
+ sections={[mainSection]}
273
+ timeline={timelineConfig}
274
+ sidebar={sidebarConfig}
275
+ />
276
+ );
277
+ }}
232
278
  </CrudDetail>
233
279
  );
234
280
  }
@@ -264,9 +310,9 @@ export default use${moduleNamePascal}Store;
264
310
  `;
265
311
 
266
312
  // Create directories
267
- const pagesDir = path.join(srcPath, 'pages', `${moduleNameLower}s`);
268
- const routesDir = path.join(srcPath, 'routes', 'modules');
269
- const storesDir = path.join(srcPath, 'stores');
313
+ const pagesDir = path.join(srcPath, "pages", `${moduleNameLower}s`);
314
+ const routesDir = path.join(srcPath, "routes", "modules");
315
+ const storesDir = path.join(srcPath, "stores");
270
316
 
271
317
  if (!fs.existsSync(pagesDir)) {
272
318
  fs.mkdirSync(pagesDir, { recursive: true });
@@ -278,11 +324,26 @@ export default use${moduleNamePascal}Store;
278
324
 
279
325
  // Write files
280
326
  const files = [
281
- { path: path.join(pagesDir, `${moduleNamePascal}ListPage.jsx`), content: listPageTemplate },
282
- { path: path.join(pagesDir, `${moduleNamePascal}FormPage.jsx`), content: formPageTemplate },
283
- { path: path.join(pagesDir, `${moduleNamePascal}DetailPage.jsx`), content: detailPageTemplate },
284
- { path: path.join(routesDir, `${moduleNameCamel}Routes.jsx`), content: routesTemplate },
285
- { path: path.join(storesDir, `use${moduleNamePascal}Store.jsx`), content: storeTemplate },
327
+ {
328
+ path: path.join(pagesDir, `${moduleNamePascal}ListPage.jsx`),
329
+ content: listPageTemplate,
330
+ },
331
+ {
332
+ path: path.join(pagesDir, `${moduleNamePascal}FormPage.jsx`),
333
+ content: formPageTemplate,
334
+ },
335
+ {
336
+ path: path.join(pagesDir, `${moduleNamePascal}DetailPage.jsx`),
337
+ content: detailPageTemplate,
338
+ },
339
+ {
340
+ path: path.join(routesDir, `${moduleNameCamel}Routes.jsx`),
341
+ content: routesTemplate,
342
+ },
343
+ {
344
+ path: path.join(storesDir, `use${moduleNamePascal}Store.jsx`),
345
+ content: storeTemplate,
346
+ },
286
347
  ];
287
348
 
288
349
  console.log(`\n🚀 Generating ${moduleNamePascal} module...\n`);