@kaushalparajuli/react-crud-ui 1.1.14 → 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 +120 -59
- package/dist/index.cjs +78 -78
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +11702 -11405
- package/dist/index.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
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
|
|
16
|
-
import path from
|
|
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 ===
|
|
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 ===
|
|
47
|
+
if (command === "--version" || command === "-v") {
|
|
48
48
|
try {
|
|
49
|
-
const packageJson = JSON.parse(
|
|
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(
|
|
54
|
+
console.log("Unknown version");
|
|
53
55
|
}
|
|
54
56
|
process.exit(0);
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
// Handle generate command
|
|
58
|
-
if (command ===
|
|
60
|
+
if (command === "generate" || command === "g") {
|
|
59
61
|
if (!moduleName) {
|
|
60
|
-
console.error(
|
|
61
|
-
console.log(
|
|
62
|
-
|
|
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(
|
|
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) =>
|
|
77
|
-
|
|
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,
|
|
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(
|
|
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) =>
|
|
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
|
|
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
|
|
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
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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,
|
|
268
|
-
const routesDir = path.join(srcPath,
|
|
269
|
-
const storesDir = path.join(srcPath,
|
|
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
|
-
{
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
{
|
|
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`);
|