@currentjs/gen 0.3.2 → 0.5.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/CHANGELOG.md +18 -609
- package/README.md +623 -427
- package/dist/cli.js +2 -1
- package/dist/commands/commit.js +25 -42
- package/dist/commands/createApp.js +1 -0
- package/dist/commands/createModule.js +151 -45
- package/dist/commands/diff.js +27 -40
- package/dist/commands/generateAll.js +141 -291
- package/dist/commands/migrateCommit.js +6 -18
- package/dist/generators/controllerGenerator.d.ts +50 -19
- package/dist/generators/controllerGenerator.js +588 -331
- package/dist/generators/domainLayerGenerator.d.ts +21 -0
- package/dist/generators/domainLayerGenerator.js +286 -0
- package/dist/generators/dtoGenerator.d.ts +21 -0
- package/dist/generators/dtoGenerator.js +523 -0
- package/dist/generators/serviceGenerator.d.ts +22 -51
- package/dist/generators/serviceGenerator.js +345 -568
- package/dist/generators/storeGenerator.d.ts +39 -32
- package/dist/generators/storeGenerator.js +396 -236
- package/dist/generators/templateGenerator.d.ts +21 -21
- package/dist/generators/templateGenerator.js +393 -268
- package/dist/generators/templates/appTemplates.d.ts +3 -1
- package/dist/generators/templates/appTemplates.js +16 -11
- package/dist/generators/templates/data/appYamlTemplate +5 -2
- package/dist/generators/templates/data/cursorRulesTemplate +315 -221
- package/dist/generators/templates/data/frontendScriptTemplate +56 -15
- package/dist/generators/templates/data/mainViewTemplate +2 -1
- package/dist/generators/templates/data/systemTsTemplate +5 -0
- package/dist/generators/templates/index.d.ts +0 -3
- package/dist/generators/templates/index.js +0 -3
- package/dist/generators/templates/storeTemplates.d.ts +1 -5
- package/dist/generators/templates/storeTemplates.js +84 -224
- package/dist/generators/useCaseGenerator.d.ts +13 -0
- package/dist/generators/useCaseGenerator.js +191 -0
- package/dist/types/configTypes.d.ts +149 -0
- package/dist/types/configTypes.js +10 -0
- package/dist/utils/childEntityUtils.d.ts +18 -0
- package/dist/utils/childEntityUtils.js +78 -0
- package/dist/utils/commandUtils.d.ts +43 -0
- package/dist/utils/commandUtils.js +124 -0
- package/dist/utils/commitUtils.d.ts +4 -1
- package/dist/utils/constants.d.ts +10 -0
- package/dist/utils/constants.js +13 -1
- package/dist/utils/diResolver.d.ts +32 -0
- package/dist/utils/diResolver.js +204 -0
- package/dist/utils/typeUtils.d.ts +23 -0
- package/dist/utils/typeUtils.js +77 -0
- package/package.json +7 -3
- package/dist/generators/domainModelGenerator.d.ts +0 -41
- package/dist/generators/domainModelGenerator.js +0 -242
- package/dist/generators/templates/controllerTemplates.d.ts +0 -43
- package/dist/generators/templates/controllerTemplates.js +0 -82
- package/dist/generators/templates/serviceTemplates.d.ts +0 -16
- package/dist/generators/templates/serviceTemplates.js +0 -59
- package/dist/generators/templates/validationTemplates.d.ts +0 -25
- package/dist/generators/templates/validationTemplates.js +0 -66
- package/dist/generators/templates/viewTemplates.d.ts +0 -25
- package/dist/generators/templates/viewTemplates.js +0 -491
- package/dist/generators/validationGenerator.d.ts +0 -29
- package/dist/generators/validationGenerator.js +0 -250
|
@@ -1,491 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.toFileNameFromTemplateName = toFileNameFromTemplateName;
|
|
4
|
-
exports.setAvailableModels = setAvailableModels;
|
|
5
|
-
exports.setRelationshipContext = setRelationshipContext;
|
|
6
|
-
exports.isRelationshipField = isRelationshipField;
|
|
7
|
-
exports.getForeignKeyFieldName = getForeignKeyFieldName;
|
|
8
|
-
exports.renderListTemplate = renderListTemplate;
|
|
9
|
-
exports.renderDetailTemplate = renderDetailTemplate;
|
|
10
|
-
exports.renderCreateTemplate = renderCreateTemplate;
|
|
11
|
-
exports.renderUpdateTemplate = renderUpdateTemplate;
|
|
12
|
-
exports.renderDeleteTemplate = renderDeleteTemplate;
|
|
13
|
-
exports.renderLayoutTemplate = renderLayoutTemplate;
|
|
14
|
-
// Helper to check if a field type is a known model (relationship)
|
|
15
|
-
let availableModels = new Set();
|
|
16
|
-
let relationshipContext = {
|
|
17
|
-
routePaths: new Map(),
|
|
18
|
-
apiPaths: new Map()
|
|
19
|
-
};
|
|
20
|
-
function setAvailableModels(models) {
|
|
21
|
-
availableModels = new Set(models);
|
|
22
|
-
}
|
|
23
|
-
function setRelationshipContext(context) {
|
|
24
|
-
relationshipContext = context;
|
|
25
|
-
}
|
|
26
|
-
function isRelationshipField(field) {
|
|
27
|
-
return availableModels.has(field.type);
|
|
28
|
-
}
|
|
29
|
-
function getForeignKeyFieldName(field) {
|
|
30
|
-
// Convention: fieldName + 'Id' (e.g., owner -> ownerId)
|
|
31
|
-
return field.name + 'Id';
|
|
32
|
-
}
|
|
33
|
-
function toFileNameFromTemplateName(name) {
|
|
34
|
-
const last = name.split('/').pop() || 'template';
|
|
35
|
-
let base = last
|
|
36
|
-
.replace(/\.tpl\.html$/i, '')
|
|
37
|
-
.replace(/\.(html|htm|tpl)$/i, '');
|
|
38
|
-
base = base.toLowerCase().replace(/[^a-z0-9._-]+/g, '-');
|
|
39
|
-
return `${base}.html`;
|
|
40
|
-
}
|
|
41
|
-
function generateFormInput(field) {
|
|
42
|
-
const requiredAttr = field.required ? ' required' : '';
|
|
43
|
-
const fieldId = field.name;
|
|
44
|
-
const fieldName = field.name.charAt(0).toUpperCase() + field.name.slice(1).replace(/_/g, ' ');
|
|
45
|
-
// Skip auto fields (like auto-incrementing IDs)
|
|
46
|
-
if (field.auto) {
|
|
47
|
-
return '';
|
|
48
|
-
}
|
|
49
|
-
// Handle relationship fields with a select dropdown
|
|
50
|
-
if (isRelationshipField(field)) {
|
|
51
|
-
const foreignKeyName = getForeignKeyFieldName(field);
|
|
52
|
-
const relatedModel = field.type;
|
|
53
|
-
const relatedModelLower = relatedModel.toLowerCase();
|
|
54
|
-
const displayField = (field.displayFields && field.displayFields.length > 0) ? field.displayFields[0] : 'name';
|
|
55
|
-
// Get actual paths from context, or fallback to defaults
|
|
56
|
-
const routePath = relationshipContext.routePaths.get(relatedModel) || `/${relatedModelLower}/create`;
|
|
57
|
-
const apiPath = relationshipContext.apiPaths.get(relatedModel) || `/api/${relatedModelLower}`;
|
|
58
|
-
return ` <div class="mb-3">
|
|
59
|
-
<label for="${foreignKeyName}" class="form-label">${fieldName}</label>
|
|
60
|
-
<div class="input-group">
|
|
61
|
-
<select id="${foreignKeyName}" name="${foreignKeyName}" class="form-select"${requiredAttr} data-relationship="${relatedModel}">
|
|
62
|
-
<option value="">-- Select ${fieldName} --</option>
|
|
63
|
-
<!-- Options will be populated via JavaScript from ${apiPath} -->
|
|
64
|
-
</select>
|
|
65
|
-
<button type="button" class="btn btn-outline-secondary" onclick="window.open('${routePath}', '${relatedModel}Create', 'width=600,height=400')">
|
|
66
|
-
<i class="bi bi-plus-circle"></i> New
|
|
67
|
-
</button>
|
|
68
|
-
</div>
|
|
69
|
-
<small class="form-text text-muted">Select an existing ${fieldName} or create a new one</small>
|
|
70
|
-
</div>
|
|
71
|
-
<script>
|
|
72
|
-
// Load ${relatedModel} options
|
|
73
|
-
(async () => {
|
|
74
|
-
try {
|
|
75
|
-
const response = await fetch('${apiPath}', {
|
|
76
|
-
headers: App.auth.buildAuthHeaders()
|
|
77
|
-
});
|
|
78
|
-
const data = await response.json();
|
|
79
|
-
const select = document.getElementById('${foreignKeyName}');
|
|
80
|
-
if (Array.isArray(data)) {
|
|
81
|
-
data.forEach(item => {
|
|
82
|
-
const option = document.createElement('option');
|
|
83
|
-
option.value = item.id;
|
|
84
|
-
option.textContent = item.${displayField} || item.id;
|
|
85
|
-
select.appendChild(option);
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
} catch (error) {
|
|
89
|
-
console.error('Failed to load ${relatedModel} options:', error);
|
|
90
|
-
}
|
|
91
|
-
})();
|
|
92
|
-
</script>`;
|
|
93
|
-
}
|
|
94
|
-
switch (field.type.toLowerCase()) {
|
|
95
|
-
case 'number':
|
|
96
|
-
case 'int':
|
|
97
|
-
case 'integer':
|
|
98
|
-
case 'float':
|
|
99
|
-
case 'decimal':
|
|
100
|
-
return ` <div class="mb-3">
|
|
101
|
-
<label for="${fieldId}" class="form-label">${fieldName}</label>
|
|
102
|
-
<input id="${fieldId}" name="${field.name}" type="number" class="form-control"${requiredAttr} />
|
|
103
|
-
</div>`;
|
|
104
|
-
case 'boolean':
|
|
105
|
-
case 'bool':
|
|
106
|
-
return ` <div class="mb-3">
|
|
107
|
-
<label class="form-label">${fieldName}</label>
|
|
108
|
-
<div class="form-check">
|
|
109
|
-
<input id="${fieldId}_true" name="${field.name}" type="radio" value="true" class="form-check-input"${requiredAttr} />
|
|
110
|
-
<label for="${fieldId}_true" class="form-check-label">Yes</label>
|
|
111
|
-
</div>
|
|
112
|
-
<div class="form-check">
|
|
113
|
-
<input id="${fieldId}_false" name="${field.name}" type="radio" value="false" class="form-check-input" />
|
|
114
|
-
<label for="${fieldId}_false" class="form-check-label">No</label>
|
|
115
|
-
</div>
|
|
116
|
-
</div>`;
|
|
117
|
-
case 'enum':
|
|
118
|
-
if (field.enum && field.enum.length > 0) {
|
|
119
|
-
const options = field.enum.map(opt => ` <option value="${opt}">${opt.charAt(0).toUpperCase() + opt.slice(1)}</option>`).join('\n');
|
|
120
|
-
return ` <div class="mb-3">
|
|
121
|
-
<label for="${fieldId}" class="form-label">${fieldName}</label>
|
|
122
|
-
<select id="${fieldId}" name="${field.name}" class="form-select"${requiredAttr}>
|
|
123
|
-
<option value="">-- Select ${fieldName} --</option>
|
|
124
|
-
${options}
|
|
125
|
-
</select>
|
|
126
|
-
</div>`;
|
|
127
|
-
}
|
|
128
|
-
// Fallback to text if no enum values
|
|
129
|
-
return ` <div class="mb-3">
|
|
130
|
-
<label for="${fieldId}" class="form-label">${fieldName}</label>
|
|
131
|
-
<input id="${fieldId}" name="${field.name}" type="text" class="form-control"${requiredAttr} />
|
|
132
|
-
</div>`;
|
|
133
|
-
default:
|
|
134
|
-
// Text fields (string, text, etc.)
|
|
135
|
-
return ` <div class="mb-3">
|
|
136
|
-
<label for="${fieldId}" class="form-label">${fieldName}</label>
|
|
137
|
-
<input id="${fieldId}" name="${field.name}" type="text" class="form-control"${requiredAttr} />
|
|
138
|
-
</div>`;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
function generateUpdateFormInput(field) {
|
|
142
|
-
const requiredAttr = field.required ? ' required' : '';
|
|
143
|
-
const fieldId = field.name;
|
|
144
|
-
const fieldName = field.name.charAt(0).toUpperCase() + field.name.slice(1).replace(/_/g, ' ');
|
|
145
|
-
const fieldValue = `{{ $root.${field.name} }}`;
|
|
146
|
-
// Skip auto fields (like auto-incrementing IDs)
|
|
147
|
-
if (field.auto) {
|
|
148
|
-
return '';
|
|
149
|
-
}
|
|
150
|
-
// Handle relationship fields with a select dropdown
|
|
151
|
-
if (isRelationshipField(field)) {
|
|
152
|
-
const foreignKeyName = getForeignKeyFieldName(field);
|
|
153
|
-
const relatedModel = field.type;
|
|
154
|
-
const relatedModelLower = relatedModel.toLowerCase();
|
|
155
|
-
const displayField = (field.displayFields && field.displayFields.length > 0) ? field.displayFields[0] : 'name';
|
|
156
|
-
// Get actual paths from context, or fallback to defaults
|
|
157
|
-
const routePath = relationshipContext.routePaths.get(relatedModel) || `/${relatedModelLower}/create`;
|
|
158
|
-
const apiPath = relationshipContext.apiPaths.get(relatedModel) || `/api/${relatedModelLower}`;
|
|
159
|
-
return ` <div class="mb-3">
|
|
160
|
-
<label for="${foreignKeyName}" class="form-label">${fieldName}</label>
|
|
161
|
-
<div class="input-group">
|
|
162
|
-
<select id="${foreignKeyName}" name="${foreignKeyName}" class="form-select"${requiredAttr} data-relationship="${relatedModel}" data-current-value="{{ $root.${foreignKeyName} }}">
|
|
163
|
-
<option value="">-- Select ${fieldName} --</option>
|
|
164
|
-
<!-- Options will be populated via JavaScript from ${apiPath} -->
|
|
165
|
-
</select>
|
|
166
|
-
<button type="button" class="btn btn-outline-secondary" onclick="window.open('${routePath}', '${relatedModel}Create', 'width=600,height=400')">
|
|
167
|
-
<i class="bi bi-plus-circle"></i> New
|
|
168
|
-
</button>
|
|
169
|
-
</div>
|
|
170
|
-
<small class="form-text text-muted">Select an existing ${fieldName} or create a new one</small>
|
|
171
|
-
</div>
|
|
172
|
-
<script>
|
|
173
|
-
// Load ${relatedModel} options
|
|
174
|
-
(async () => {
|
|
175
|
-
try {
|
|
176
|
-
const response = await fetch('${apiPath}', {
|
|
177
|
-
headers: App.auth.buildAuthHeaders()
|
|
178
|
-
});
|
|
179
|
-
const data = await response.json();
|
|
180
|
-
const select = document.getElementById('${foreignKeyName}');
|
|
181
|
-
const currentValue = select.getAttribute('data-current-value');
|
|
182
|
-
if (Array.isArray(data)) {
|
|
183
|
-
data.forEach(item => {
|
|
184
|
-
const option = document.createElement('option');
|
|
185
|
-
option.value = item.id;
|
|
186
|
-
option.textContent = item.${displayField} || item.id;
|
|
187
|
-
if (currentValue && item.id == currentValue) {
|
|
188
|
-
option.selected = true;
|
|
189
|
-
}
|
|
190
|
-
select.appendChild(option);
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
} catch (error) {
|
|
194
|
-
console.error('Failed to load ${relatedModel} options:', error);
|
|
195
|
-
}
|
|
196
|
-
})();
|
|
197
|
-
</script>`;
|
|
198
|
-
}
|
|
199
|
-
switch (field.type.toLowerCase()) {
|
|
200
|
-
case 'number':
|
|
201
|
-
case 'int':
|
|
202
|
-
case 'integer':
|
|
203
|
-
case 'float':
|
|
204
|
-
case 'decimal':
|
|
205
|
-
return ` <div class="mb-3">
|
|
206
|
-
<label for="${fieldId}" class="form-label">${fieldName}</label>
|
|
207
|
-
<input id="${fieldId}" name="${field.name}" type="number" class="form-control" value="${fieldValue}"${requiredAttr} />
|
|
208
|
-
</div>`;
|
|
209
|
-
case 'boolean':
|
|
210
|
-
case 'bool':
|
|
211
|
-
return ` <div class="mb-3">
|
|
212
|
-
<label class="form-label">${fieldName}</label>
|
|
213
|
-
<div class="form-check">
|
|
214
|
-
<input id="${fieldId}_true" name="${field.name}" type="radio" value="true" class="form-check-input" {{ $root.${field.name} ? 'checked' : '' }}${requiredAttr} />
|
|
215
|
-
<label for="${fieldId}_true" class="form-check-label">Yes</label>
|
|
216
|
-
</div>
|
|
217
|
-
<div class="form-check">
|
|
218
|
-
<input id="${fieldId}_false" name="${field.name}" type="radio" value="false" class="form-check-input" {{ $root.${field.name} ? '' : 'checked' }} />
|
|
219
|
-
<label for="${fieldId}_false" class="form-check-label">No</label>
|
|
220
|
-
</div>
|
|
221
|
-
</div>`;
|
|
222
|
-
case 'enum':
|
|
223
|
-
if (field.enum && field.enum.length > 0) {
|
|
224
|
-
const options = field.enum.map(opt => ` <option value="${opt}" {{ $root.${field.name} === '${opt}' ? 'selected' : '' }}>${opt.charAt(0).toUpperCase() + opt.slice(1)}</option>`).join('\n');
|
|
225
|
-
return ` <div class="mb-3">
|
|
226
|
-
<label for="${fieldId}" class="form-label">${fieldName}</label>
|
|
227
|
-
<select id="${fieldId}" name="${field.name}" class="form-select"${requiredAttr}>
|
|
228
|
-
<option value="">-- Select ${fieldName} --</option>
|
|
229
|
-
${options}
|
|
230
|
-
</select>
|
|
231
|
-
</div>`;
|
|
232
|
-
}
|
|
233
|
-
// Fallback to text if no enum values
|
|
234
|
-
return ` <div class="mb-3">
|
|
235
|
-
<label for="${fieldId}" class="form-label">${fieldName}</label>
|
|
236
|
-
<input id="${fieldId}" name="${field.name}" type="text" class="form-control" value="${fieldValue}"${requiredAttr} />
|
|
237
|
-
</div>`;
|
|
238
|
-
default:
|
|
239
|
-
// Text fields (string, text, etc.)
|
|
240
|
-
return ` <div class="mb-3">
|
|
241
|
-
<label for="${fieldId}" class="form-label">${fieldName}</label>
|
|
242
|
-
<input id="${fieldId}" name="${field.name}" type="text" class="form-control" value="${fieldValue}"${requiredAttr} />
|
|
243
|
-
</div>`;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
function renderListTemplate(entityName, templateName, basePath, fields, apiBase) {
|
|
247
|
-
const safeFields = fields.length > 0 ? fields : [{ name: 'id', type: 'number' }, { name: 'name', type: 'string' }];
|
|
248
|
-
const headers = ['ID', ...safeFields.filter(f => f.name !== 'id').map(f => f.name.charAt(0).toUpperCase() + f.name.slice(1).replace(/_/g, ' '))].map(f => `<th scope="col">${f}</th>`).join('');
|
|
249
|
-
// Generate cells with relationship field handling
|
|
250
|
-
const cells = [
|
|
251
|
-
'<td>{{ row.id }}</td>',
|
|
252
|
-
...safeFields.filter(f => f.name !== 'id').map(f => {
|
|
253
|
-
// For relationship fields, show a specific field from the related object
|
|
254
|
-
if (isRelationshipField(f)) {
|
|
255
|
-
const displayField = (f.displayFields && f.displayFields.length > 0) ? f.displayFields[0] : 'name';
|
|
256
|
-
return `<td>{{ row.${f.name}.${displayField} }}</td>`;
|
|
257
|
-
}
|
|
258
|
-
return `<td>{{ row.${f.name} }}</td>`;
|
|
259
|
-
})
|
|
260
|
-
].join('\n ');
|
|
261
|
-
const deleteAction = apiBase ? `${apiBase}/{{ row.id }}` : `${basePath}/api/{{ row.id }}`;
|
|
262
|
-
return `<!-- @template name="${templateName}" -->
|
|
263
|
-
<div class="container-fluid py-4">
|
|
264
|
-
<div class="row">
|
|
265
|
-
<div class="col">
|
|
266
|
-
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
267
|
-
<h1 class="h2">${entityName} List</h1>
|
|
268
|
-
<a href="${basePath}/create" class="btn btn-primary">
|
|
269
|
-
<i class="bi bi-plus-circle me-1"></i>Create New ${entityName}
|
|
270
|
-
</a>
|
|
271
|
-
</div>
|
|
272
|
-
|
|
273
|
-
<div class="card">
|
|
274
|
-
<div class="card-body">
|
|
275
|
-
<div class="table-responsive">
|
|
276
|
-
<table class="table table-hover">
|
|
277
|
-
<thead class="table-light">
|
|
278
|
-
<tr>
|
|
279
|
-
${headers}
|
|
280
|
-
<th scope="col" class="text-end">Actions</th>
|
|
281
|
-
</tr>
|
|
282
|
-
</thead>
|
|
283
|
-
<tbody x-for="$root" x-row="row">
|
|
284
|
-
<tr id="row-{{ row.id }}">
|
|
285
|
-
${cells}
|
|
286
|
-
<td class="text-end">
|
|
287
|
-
<div class="btn-group" role="group">
|
|
288
|
-
<a href="${basePath}/{{ row.id }}" class="btn btn-sm btn-outline-primary">View</a>
|
|
289
|
-
<a href="${basePath}/{{ row.id }}/edit" class="btn btn-sm btn-outline-secondary">Edit</a>
|
|
290
|
-
<form style="display: inline;"
|
|
291
|
-
data-action="${deleteAction}"
|
|
292
|
-
data-method="DELETE"
|
|
293
|
-
data-strategy='["toast", "remove"]'
|
|
294
|
-
data-entity-name="${entityName}"
|
|
295
|
-
data-confirm-message="Are you sure you want to delete this ${entityName.toLowerCase()}? This action cannot be undone."
|
|
296
|
-
data-target-selector="#row-{{ row.id }}">
|
|
297
|
-
<button type="submit" class="btn btn-sm btn-outline-danger">
|
|
298
|
-
<i class="bi bi-trash me-1"></i>Delete
|
|
299
|
-
</button>
|
|
300
|
-
</form>
|
|
301
|
-
</div>
|
|
302
|
-
</td>
|
|
303
|
-
</tr>
|
|
304
|
-
</tbody>
|
|
305
|
-
</table>
|
|
306
|
-
</div>
|
|
307
|
-
</div>
|
|
308
|
-
</div>
|
|
309
|
-
</div>
|
|
310
|
-
</div>
|
|
311
|
-
</div>
|
|
312
|
-
`;
|
|
313
|
-
}
|
|
314
|
-
function renderDetailTemplate(entityName, templateName, fields) {
|
|
315
|
-
const safeFields = fields.length > 0 ? fields : [{ name: 'id', type: 'number' }, { name: 'name', type: 'string' }];
|
|
316
|
-
const rows = safeFields.map(f => {
|
|
317
|
-
const fieldName = f.name.charAt(0).toUpperCase() + f.name.slice(1).replace(/_/g, ' ');
|
|
318
|
-
// For relationship fields, show specific fields from the related object
|
|
319
|
-
if (isRelationshipField(f)) {
|
|
320
|
-
const displayFields = (f.displayFields && f.displayFields.length > 0) ? f.displayFields : ['name'];
|
|
321
|
-
// Show all displayFields separated by comma
|
|
322
|
-
const fieldAccess = displayFields.map(df => `{{ $root.${f.name}.${df} }}`).join(', ');
|
|
323
|
-
return ` <tr><th scope="row" class="w-25">${fieldName}</th><td>${fieldAccess}</td></tr>`;
|
|
324
|
-
}
|
|
325
|
-
return ` <tr><th scope="row" class="w-25">${fieldName}</th><td>{{ $root.${f.name} }}</td></tr>`;
|
|
326
|
-
}).join('\n');
|
|
327
|
-
return `<!-- @template name="${templateName}" -->
|
|
328
|
-
<div class="container-fluid py-4">
|
|
329
|
-
<div class="row justify-content-center">
|
|
330
|
-
<div class="col-lg-8">
|
|
331
|
-
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
332
|
-
<h1 class="h2">${entityName} Details</h1>
|
|
333
|
-
<div class="btn-group" role="group">
|
|
334
|
-
<a href="{{ basePath }}/{{ $root.id }}/edit" class="btn btn-outline-primary">Edit</a>
|
|
335
|
-
<button onclick="window.history.back()" class="btn btn-outline-secondary">Back</button>
|
|
336
|
-
</div>
|
|
337
|
-
</div>
|
|
338
|
-
|
|
339
|
-
<div class="card">
|
|
340
|
-
<div class="card-body">
|
|
341
|
-
<div class="table-responsive">
|
|
342
|
-
<table class="table table-borderless">
|
|
343
|
-
<tbody>
|
|
344
|
-
${rows}
|
|
345
|
-
</tbody>
|
|
346
|
-
</table>
|
|
347
|
-
</div>
|
|
348
|
-
</div>
|
|
349
|
-
</div>
|
|
350
|
-
</div>
|
|
351
|
-
</div>
|
|
352
|
-
</div>
|
|
353
|
-
`;
|
|
354
|
-
}
|
|
355
|
-
function renderCreateTemplate(entityName, templateName, apiBase, fields, strategy = ['back', 'toast'], basePath) {
|
|
356
|
-
const safeFields = fields.filter(f => f.name !== 'id' && !f.auto);
|
|
357
|
-
const inputs = safeFields.map(f => generateFormInput(f)).filter(input => input.trim() !== '').join('\n');
|
|
358
|
-
const tplId = templateName.replace(/[^a-z0-9._-]+/gi, '-').toLowerCase();
|
|
359
|
-
const targetId = `${tplId}-result`;
|
|
360
|
-
const messageId = `${tplId}-message`;
|
|
361
|
-
const modalId = `${tplId}-modal`;
|
|
362
|
-
const errorsId = `${tplId}-errors`;
|
|
363
|
-
const fieldTypes = JSON.stringify(safeFields.reduce((acc, f) => {
|
|
364
|
-
acc[f.name] = f.type;
|
|
365
|
-
return acc;
|
|
366
|
-
}, {}));
|
|
367
|
-
const inlineErrorsBlock = strategy.includes('inline') ? `\n <div id="${errorsId}" class="alert alert-danger d-none"></div>` : '';
|
|
368
|
-
const messageBlock = strategy.includes('message') ? `\n <div id="${messageId}" class="alert d-none" role="status"></div>` : '';
|
|
369
|
-
const modalBlock = strategy.includes('modal')
|
|
370
|
-
? `\n<div class="modal fade" id="${modalId}" tabindex="-1"><div class="modal-dialog"><div class="modal-content"><div class="modal-body"></div></div></div></div>`
|
|
371
|
-
: '';
|
|
372
|
-
return `<!-- @template name="${templateName}" -->
|
|
373
|
-
<div class="container-fluid py-4">
|
|
374
|
-
<div class="row justify-content-center">
|
|
375
|
-
<div class="col-lg-6">
|
|
376
|
-
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
377
|
-
<h1 class="h2">Create ${entityName}</h1>
|
|
378
|
-
<button onclick="window.history.back()" class="btn btn-outline-secondary">Cancel</button>
|
|
379
|
-
</div>
|
|
380
|
-
|
|
381
|
-
<div class="card">
|
|
382
|
-
<div class="card-body">
|
|
383
|
-
<form data-template="${tplId}" data-action="${apiBase}" data-method="POST" data-strategy='${JSON.stringify(strategy)}' data-base-path="${basePath || ''}" data-entity-name="${entityName}" data-message-id="${messageId}" data-modal-id="${modalId}" data-field-types='${fieldTypes}'>
|
|
384
|
-
${inputs}
|
|
385
|
-
<div class="d-flex gap-2 justify-content-end">
|
|
386
|
-
<button type="button" onclick="window.history.back()" class="btn btn-outline-secondary">Cancel</button>
|
|
387
|
-
<button type="submit" class="btn btn-primary">Create ${entityName}</button>
|
|
388
|
-
</div>
|
|
389
|
-
</form>
|
|
390
|
-
<div id="${targetId}"></div>${inlineErrorsBlock}${messageBlock}
|
|
391
|
-
</div>
|
|
392
|
-
</div>
|
|
393
|
-
</div>
|
|
394
|
-
</div>
|
|
395
|
-
</div>${modalBlock}
|
|
396
|
-
`;
|
|
397
|
-
}
|
|
398
|
-
function renderUpdateTemplate(entityName, templateName, apiBase, fields, strategy = ['back', 'toast'], basePath) {
|
|
399
|
-
const safeFields = fields.filter(f => f.name !== 'id' && !f.auto);
|
|
400
|
-
const inputs = safeFields.map(f => generateUpdateFormInput(f)).filter(input => input.trim() !== '').join('\n');
|
|
401
|
-
const tplId = templateName.replace(/[^a-z0-9._-]+/gi, '-').toLowerCase();
|
|
402
|
-
const targetId = `${tplId}-result`;
|
|
403
|
-
const messageId = `${tplId}-message`;
|
|
404
|
-
const modalId = `${tplId}-modal`;
|
|
405
|
-
const errorsId = `${tplId}-errors`;
|
|
406
|
-
const fieldTypes = JSON.stringify(safeFields.reduce((acc, f) => {
|
|
407
|
-
acc[f.name] = f.type;
|
|
408
|
-
return acc;
|
|
409
|
-
}, {}));
|
|
410
|
-
const inlineErrorsBlock = strategy.includes('inline') ? `\n <div id="${errorsId}" class="alert alert-danger d-none"></div>` : '';
|
|
411
|
-
const messageBlock = strategy.includes('message') ? `\n <div id="${messageId}" class="alert d-none" role="status"></div>` : '';
|
|
412
|
-
const modalBlock = strategy.includes('modal')
|
|
413
|
-
? `\n<div class="modal fade" id="${modalId}" tabindex="-1"><div class="modal-dialog"><div class="modal-content"><div class="modal-body"></div></div></div></div>`
|
|
414
|
-
: '';
|
|
415
|
-
return `<!-- @template name="${templateName}" -->
|
|
416
|
-
<div class="container-fluid py-4">
|
|
417
|
-
<div class="row justify-content-center">
|
|
418
|
-
<div class="col-lg-6">
|
|
419
|
-
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
420
|
-
<h1 class="h2">Edit ${entityName}</h1>
|
|
421
|
-
<button onclick="window.history.back()" class="btn btn-outline-secondary">Cancel</button>
|
|
422
|
-
</div>
|
|
423
|
-
|
|
424
|
-
<div class="card">
|
|
425
|
-
<div class="card-body">
|
|
426
|
-
<form data-template="${tplId}" data-action="${apiBase}/{{ $root.id }}" data-method="PUT" data-strategy='${JSON.stringify(strategy)}' data-base-path="${basePath || ''}" data-entity-name="${entityName}" data-message-id="${messageId}" data-modal-id="${modalId}" data-field-types='${fieldTypes}'>
|
|
427
|
-
${inputs}
|
|
428
|
-
<div class="d-flex gap-2 justify-content-end">
|
|
429
|
-
<button type="button" onclick="window.history.back()" class="btn btn-outline-secondary">Cancel</button>
|
|
430
|
-
<button type="submit" class="btn btn-primary">Save Changes</button>
|
|
431
|
-
</div>
|
|
432
|
-
</form>
|
|
433
|
-
<div id="${targetId}"></div>${inlineErrorsBlock}${messageBlock}
|
|
434
|
-
</div>
|
|
435
|
-
</div>
|
|
436
|
-
</div>
|
|
437
|
-
</div>
|
|
438
|
-
</div>${modalBlock}
|
|
439
|
-
`;
|
|
440
|
-
}
|
|
441
|
-
function renderDeleteTemplate(entityName, templateName, apiBase, strategy = ['back', 'toast'], basePath) {
|
|
442
|
-
const tplId = templateName.replace(/[^a-z0-9._-]+/gi, '-').toLowerCase();
|
|
443
|
-
const messageId = `${tplId}-message`;
|
|
444
|
-
const modalId = `${tplId}-modal`;
|
|
445
|
-
const messageBlock = strategy.includes('message') ? `\n <div id="${messageId}" class="alert d-none" role="status"></div>` : '';
|
|
446
|
-
const modalBlock = strategy.includes('modal')
|
|
447
|
-
? `\n<div class="modal fade" id="${modalId}" tabindex="-1"><div class="modal-dialog"><div class="modal-content"><div class="modal-body"></div></div></div></div>`
|
|
448
|
-
: '';
|
|
449
|
-
return `<!-- @template name="${templateName}" -->
|
|
450
|
-
<div class="container-fluid py-4">
|
|
451
|
-
<div class="row justify-content-center">
|
|
452
|
-
<div class="col-lg-6">
|
|
453
|
-
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
454
|
-
<h1 class="h2">Delete ${entityName}</h1>
|
|
455
|
-
<button onclick="window.history.back()" class="btn btn-outline-secondary">Cancel</button>
|
|
456
|
-
</div>
|
|
457
|
-
|
|
458
|
-
<div class="card border-danger">
|
|
459
|
-
<div class="card-body">
|
|
460
|
-
<div class="alert alert-warning" role="alert">
|
|
461
|
-
<h5 class="alert-heading">⚠️ Confirm Deletion</h5>
|
|
462
|
-
<p class="mb-0">Are you sure you want to delete this ${entityName.toLowerCase()}? This action cannot be undone.</p>
|
|
463
|
-
</div>
|
|
464
|
-
|
|
465
|
-
<form data-template="${tplId}" data-action="${apiBase}/{{ $root.id }}" data-method="DELETE" data-strategy='${JSON.stringify(strategy)}' data-base-path="${basePath || ''}" data-entity-name="${entityName}" data-message-id="${messageId}" data-modal-id="${modalId}">
|
|
466
|
-
<div class="d-flex gap-2 justify-content-end">
|
|
467
|
-
<button type="button" onclick="window.history.back()" class="btn btn-outline-secondary">Cancel</button>
|
|
468
|
-
<button type="submit" class="btn btn-danger">Delete ${entityName}</button>
|
|
469
|
-
</div>
|
|
470
|
-
</form>${messageBlock}
|
|
471
|
-
</div>
|
|
472
|
-
</div>
|
|
473
|
-
</div>
|
|
474
|
-
</div>
|
|
475
|
-
</div>${modalBlock}
|
|
476
|
-
`;
|
|
477
|
-
}
|
|
478
|
-
function renderLayoutTemplate(layoutName) {
|
|
479
|
-
return `<!-- @template name="${layoutName}" -->
|
|
480
|
-
<!doctype html>
|
|
481
|
-
<html>
|
|
482
|
-
<head>
|
|
483
|
-
<meta charset="utf-8" />
|
|
484
|
-
<title>${layoutName}</title>
|
|
485
|
-
</head>
|
|
486
|
-
<body>
|
|
487
|
-
{{ content }}
|
|
488
|
-
</body>
|
|
489
|
-
</html>
|
|
490
|
-
`;
|
|
491
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
interface FieldConfig {
|
|
2
|
-
name: string;
|
|
3
|
-
type: string;
|
|
4
|
-
required?: boolean;
|
|
5
|
-
auto?: boolean;
|
|
6
|
-
unique?: boolean;
|
|
7
|
-
displayFields?: string[];
|
|
8
|
-
}
|
|
9
|
-
export declare class ValidationGenerator {
|
|
10
|
-
private availableModels;
|
|
11
|
-
private setAvailableModels;
|
|
12
|
-
private isRelationshipField;
|
|
13
|
-
private getForeignKeyFieldName;
|
|
14
|
-
private replaceTemplateVars;
|
|
15
|
-
private getTypeScriptType;
|
|
16
|
-
private generateInterfaceField;
|
|
17
|
-
private generateValidationLogic;
|
|
18
|
-
private generateDtoField;
|
|
19
|
-
private generateDtoInterface;
|
|
20
|
-
private generateInputInterface;
|
|
21
|
-
private generateValidationFunction;
|
|
22
|
-
generateValidation(entityName: string, fields: FieldConfig[]): string;
|
|
23
|
-
generateFromYamlFile(yamlFilePath: string): Record<string, string>;
|
|
24
|
-
generateAndSaveFiles(yamlFilePath?: string, outputDir?: string, opts?: {
|
|
25
|
-
force?: boolean;
|
|
26
|
-
skipOnConflict?: boolean;
|
|
27
|
-
}): Promise<void>;
|
|
28
|
-
}
|
|
29
|
-
export {};
|