@specverse/engines 4.1.28 → 4.2.0
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/assets/examples/manifests/frontend-only.yaml +3 -6
- package/assets/examples/manifests/fullstack-app.yaml +5 -7
- package/assets/examples/manifests/fullstack-monorepo.yaml +3 -6
- package/dist/inference/comprehensive-engine.d.ts.map +1 -1
- package/dist/inference/comprehensive-engine.js +3 -19
- package/dist/inference/comprehensive-engine.js.map +1 -1
- package/dist/inference/core/rule-engine.d.ts +31 -0
- package/dist/inference/core/rule-engine.d.ts.map +1 -1
- package/dist/inference/core/rule-engine.js +117 -33
- package/dist/inference/core/rule-engine.js.map +1 -1
- package/dist/inference/core/rule-file-types.d.ts +0 -2
- package/dist/inference/core/rule-file-types.d.ts.map +1 -1
- package/dist/inference/core/rule-file-types.js +3 -6
- package/dist/inference/core/rule-file-types.js.map +1 -1
- package/dist/inference/core/rule-loader.d.ts +5 -15
- package/dist/inference/core/rule-loader.d.ts.map +1 -1
- package/dist/inference/core/rule-loader.js +43 -132
- package/dist/inference/core/rule-loader.js.map +1 -1
- package/dist/inference/core/types.d.ts +0 -6
- package/dist/inference/core/types.d.ts.map +1 -1
- package/dist/inference/core/types.js +0 -4
- package/dist/inference/core/types.js.map +1 -1
- package/dist/inference/logical/generators/component-type-resolver.d.ts +0 -26
- package/dist/inference/logical/generators/component-type-resolver.d.ts.map +1 -1
- package/dist/inference/logical/generators/component-type-resolver.js +0 -19
- package/dist/inference/logical/generators/component-type-resolver.js.map +1 -1
- package/dist/inference/logical/generators/specialist-view-expander.d.ts +1 -17
- package/dist/inference/logical/generators/specialist-view-expander.d.ts.map +1 -1
- package/dist/inference/logical/generators/specialist-view-expander.js +0 -15
- package/dist/inference/logical/generators/specialist-view-expander.js.map +1 -1
- package/dist/inference/logical/generators/view-generator.d.ts +4 -14
- package/dist/inference/logical/generators/view-generator.d.ts.map +1 -1
- package/dist/inference/logical/generators/view-generator.js +6 -26
- package/dist/inference/logical/generators/view-generator.js.map +1 -1
- package/dist/inference/logical/index.d.ts +2 -2
- package/dist/inference/logical/index.d.ts.map +1 -1
- package/dist/inference/logical/logical-engine.d.ts.map +1 -1
- package/dist/inference/logical/logical-engine.js +17 -80
- package/dist/inference/logical/logical-engine.js.map +1 -1
- package/dist/inference/quint-transpiler.d.ts +5 -3
- package/dist/inference/quint-transpiler.d.ts.map +1 -1
- package/dist/inference/quint-transpiler.js +11 -6
- package/dist/inference/quint-transpiler.js.map +1 -1
- package/dist/libs/instance-factories/applications/templates/generic/main-generator.js +3 -3
- package/dist/libs/instance-factories/applications/templates/react/api-client-generator.js +16 -6
- package/dist/libs/instance-factories/applications/templates/react-starter/app-tsx-generator.js +110 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/dashboard-body-composer.js +121 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/detail-body-composer.js +78 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/form-body-composer.js +190 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/helpers-emitter.js +45 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/html-to-jsx.js +192 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/list-body-composer.js +46 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/orchestrator.js +30 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/package-json-generator.js +38 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/regen-safety.js +89 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/view-emitter.js +56 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/views-generator.js +66 -0
- package/dist/libs/instance-factories/cli/templates/commander/command-generator.js +14 -11
- package/dist/libs/instance-factories/controllers/templates/fastify/server-generator.js +11 -3
- package/dist/libs/instance-factories/services/templates/prisma/controller-generator.js +27 -17
- package/dist/libs/instance-factories/shared/path-resolver.js +1 -1
- package/dist/realize/index.d.ts.map +1 -1
- package/dist/realize/index.js +15 -22
- package/dist/realize/index.js.map +1 -1
- package/dist/registry/utils/manifest-adapter.d.ts +8 -1
- package/dist/registry/utils/manifest-adapter.d.ts.map +1 -1
- package/dist/registry/utils/manifest-adapter.js +8 -1
- package/dist/registry/utils/manifest-adapter.js.map +1 -1
- package/libs/instance-factories/applications/react-app-starter.yaml +150 -0
- package/libs/instance-factories/applications/templates/generic/main-generator.ts +3 -3
- package/libs/instance-factories/applications/templates/react/api-client-generator.ts +16 -6
- package/libs/instance-factories/applications/templates/react-starter/README.md +211 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/dashboard-body-composer.test.ts +153 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/detail-body-composer.test.ts +145 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/form-body-composer.test.ts +175 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/helpers-emitter.test.ts +55 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/html-to-jsx.test.ts +140 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/list-body-composer.test.ts +146 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/orchestrator.test.ts +163 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/parity-p2-factory-imports.test.ts +116 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/parity-p3-rendered-output.test.ts +183 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/regen-safety.test.ts +144 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/starter-generators.test.ts +114 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/view-emitter.test.ts +107 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/views-generator.test.ts +139 -0
- package/libs/instance-factories/applications/templates/react-starter/app-tsx-generator.ts +141 -0
- package/libs/instance-factories/applications/templates/react-starter/dashboard-body-composer.ts +174 -0
- package/libs/instance-factories/applications/templates/react-starter/detail-body-composer.ts +135 -0
- package/libs/instance-factories/applications/templates/react-starter/form-body-composer.ts +306 -0
- package/libs/instance-factories/applications/templates/react-starter/helpers-emitter.ts +60 -0
- package/libs/instance-factories/applications/templates/react-starter/html-to-jsx.ts +334 -0
- package/libs/instance-factories/applications/templates/react-starter/list-body-composer.ts +120 -0
- package/libs/instance-factories/applications/templates/react-starter/orchestrator.ts +80 -0
- package/libs/instance-factories/applications/templates/react-starter/package-json-generator.ts +57 -0
- package/libs/instance-factories/applications/templates/react-starter/regen-safety.ts +157 -0
- package/libs/instance-factories/applications/templates/react-starter/skeletons/dashboard.tsx.template +47 -0
- package/libs/instance-factories/applications/templates/react-starter/skeletons/detail.tsx.template +94 -0
- package/libs/instance-factories/applications/templates/react-starter/skeletons/form.tsx.template +114 -0
- package/libs/instance-factories/applications/templates/react-starter/skeletons/list.tsx.template +72 -0
- package/libs/instance-factories/applications/templates/react-starter/view-emitter.ts +151 -0
- package/libs/instance-factories/applications/templates/react-starter/views-generator.ts +137 -0
- package/libs/instance-factories/cli/templates/commander/command-generator.ts +14 -11
- package/libs/instance-factories/controllers/templates/fastify/server-generator.ts +11 -3
- package/libs/instance-factories/services/templates/prisma/controller-generator.ts +27 -17
- package/libs/instance-factories/shared/path-resolver.ts +8 -2
- package/package.json +3 -3
- package/dist/libs/instance-factories/applications/templates/react/_view-components-source.js +0 -530
- package/dist/libs/instance-factories/applications/templates/react/app-tsx-generator.js +0 -73
- package/dist/libs/instance-factories/applications/templates/react/field-helpers-generator.js +0 -99
- package/dist/libs/instance-factories/applications/templates/react/package-json-generator.js +0 -49
- package/dist/libs/instance-factories/applications/templates/react/pattern-adapter-generator.js +0 -156
- package/dist/libs/instance-factories/applications/templates/react/react-pattern-adapter.js +0 -935
- package/dist/libs/instance-factories/applications/templates/react/relationship-field-generator.js +0 -143
- package/dist/libs/instance-factories/applications/templates/react/tailwind-adapter-generator.js +0 -646
- package/dist/libs/instance-factories/applications/templates/react/tailwind-adapter-wrapper-generator.js +0 -65
- package/dist/libs/instance-factories/applications/templates/react/view-dashboard-generator.js +0 -143
- package/dist/libs/instance-factories/applications/templates/react/view-detail-generator.js +0 -143
- package/dist/libs/instance-factories/applications/templates/react/view-form-generator.js +0 -355
- package/dist/libs/instance-factories/applications/templates/react/view-list-generator.js +0 -91
- package/dist/libs/instance-factories/applications/templates/react/view-router-generator.js +0 -79
- package/dist/libs/instance-factories/tools/templates/vscode/vscode-extension-generator.js.bak +0 -244
- package/dist/libs/instance-factories/views/index.js +0 -48
- package/dist/libs/instance-factories/views/templates/react/adapters/antd-adapter.js +0 -742
- package/dist/libs/instance-factories/views/templates/react/adapters/mui-adapter.js +0 -824
- package/dist/libs/instance-factories/views/templates/react/adapters/shadcn-adapter.js +0 -719
- package/dist/libs/instance-factories/views/templates/react/app-generator.js +0 -45
- package/dist/libs/instance-factories/views/templates/react/components-generator.js +0 -820
- package/dist/libs/instance-factories/views/templates/react/forms-generator.js +0 -275
- package/dist/libs/instance-factories/views/templates/react/frontend-package-json-generator.js +0 -46
- package/dist/libs/instance-factories/views/templates/react/hooks-generator.js +0 -81
- package/dist/libs/instance-factories/views/templates/react/index-css-generator.js +0 -9
- package/dist/libs/instance-factories/views/templates/react/index-html-generator.js +0 -23
- package/dist/libs/instance-factories/views/templates/react/main-tsx-generator.js +0 -21
- package/dist/libs/instance-factories/views/templates/react/react-component-generator.js +0 -299
- package/dist/libs/instance-factories/views/templates/react/router-generator.js +0 -136
- package/dist/libs/instance-factories/views/templates/react/router-generic-generator.js +0 -107
- package/dist/libs/instance-factories/views/templates/react/shared-utils-generator.js +0 -187
- package/dist/libs/instance-factories/views/templates/react/spec-json-generator.js +0 -7
- package/dist/libs/instance-factories/views/templates/react/types-generator.js +0 -56
- package/dist/libs/instance-factories/views/templates/react/views-metadata-generator.js +0 -27
- package/dist/libs/instance-factories/views/templates/react/vite-config-generator.js +0 -29
- package/dist/libs/instance-factories/views/templates/runtime/runtime-view-renderer.js +0 -261
- package/dist/libs/instance-factories/views/templates/shared/adapter-types.js +0 -34
- package/dist/libs/instance-factories/views/templates/shared/atomic-components-registry.js +0 -800
- package/dist/libs/instance-factories/views/templates/shared/base-generator.js +0 -305
- package/dist/libs/instance-factories/views/templates/shared/component-metadata.js +0 -517
- package/dist/libs/instance-factories/views/templates/shared/composite-pattern-types.js +0 -0
- package/dist/libs/instance-factories/views/templates/shared/composite-patterns.js +0 -445
- package/dist/libs/instance-factories/views/templates/shared/index.js +0 -80
- package/dist/libs/instance-factories/views/templates/shared/pattern-validator.js +0 -210
- package/dist/libs/instance-factories/views/templates/shared/property-mapper.js +0 -492
- package/dist/libs/instance-factories/views/templates/shared/syntax-mapper.js +0 -321
- package/dist/realize/index.js.bak +0 -758
- package/libs/instance-factories/applications/react-app.yaml +0 -186
- package/libs/instance-factories/applications/templates/react/_view-components-source.ts +0 -555
- package/libs/instance-factories/applications/templates/react/app-tsx-generator.ts +0 -94
- package/libs/instance-factories/applications/templates/react/field-helpers-generator.ts +0 -106
- package/libs/instance-factories/applications/templates/react/package-json-generator.ts +0 -57
- package/libs/instance-factories/applications/templates/react/pattern-adapter-generator.ts +0 -179
- package/libs/instance-factories/applications/templates/react/react-pattern-adapter.tsx +0 -1347
- package/libs/instance-factories/applications/templates/react/relationship-field-generator.ts +0 -150
- package/libs/instance-factories/applications/templates/react/tailwind-adapter-generator.ts +0 -704
- package/libs/instance-factories/applications/templates/react/tailwind-adapter-wrapper-generator.ts +0 -84
- package/libs/instance-factories/applications/templates/react/view-dashboard-generator.ts +0 -150
- package/libs/instance-factories/applications/templates/react/view-detail-generator.ts +0 -150
- package/libs/instance-factories/applications/templates/react/view-form-generator.ts +0 -362
- package/libs/instance-factories/applications/templates/react/view-list-generator.ts +0 -98
- package/libs/instance-factories/applications/templates/react/view-router-generator.ts +0 -89
- package/libs/instance-factories/views/README.md +0 -62
- package/libs/instance-factories/views/index.d.ts +0 -13
- package/libs/instance-factories/views/index.d.ts.map +0 -1
- package/libs/instance-factories/views/index.js +0 -18
- package/libs/instance-factories/views/index.js.map +0 -1
- package/libs/instance-factories/views/index.ts +0 -45
- package/libs/instance-factories/views/react-components.yaml +0 -129
- package/libs/instance-factories/views/templates/ARCHITECTURE.md +0 -198
- package/libs/instance-factories/views/templates/react/adapters/antd-adapter.ts +0 -869
- package/libs/instance-factories/views/templates/react/adapters/mui-adapter.ts +0 -953
- package/libs/instance-factories/views/templates/react/adapters/shadcn-adapter.ts +0 -806
- package/libs/instance-factories/views/templates/react/app-generator.ts +0 -55
- package/libs/instance-factories/views/templates/react/components-generator.ts +0 -938
- package/libs/instance-factories/views/templates/react/forms-generator.ts +0 -325
- package/libs/instance-factories/views/templates/react/frontend-package-json-generator.ts +0 -57
- package/libs/instance-factories/views/templates/react/hooks-generator.ts +0 -106
- package/libs/instance-factories/views/templates/react/index-css-generator.ts +0 -14
- package/libs/instance-factories/views/templates/react/index-html-generator.ts +0 -34
- package/libs/instance-factories/views/templates/react/main-tsx-generator.ts +0 -29
- package/libs/instance-factories/views/templates/react/react-component-generator.d.ts +0 -152
- package/libs/instance-factories/views/templates/react/react-component-generator.d.ts.map +0 -1
- package/libs/instance-factories/views/templates/react/react-component-generator.js +0 -398
- package/libs/instance-factories/views/templates/react/react-component-generator.js.map +0 -1
- package/libs/instance-factories/views/templates/react/react-component-generator.ts +0 -533
- package/libs/instance-factories/views/templates/react/router-generator.ts +0 -197
- package/libs/instance-factories/views/templates/react/router-generic-generator.ts +0 -132
- package/libs/instance-factories/views/templates/react/shared-utils-generator.ts +0 -196
- package/libs/instance-factories/views/templates/react/spec-json-generator.ts +0 -17
- package/libs/instance-factories/views/templates/react/types-generator.ts +0 -76
- package/libs/instance-factories/views/templates/react/views-metadata-generator.ts +0 -42
- package/libs/instance-factories/views/templates/react/vite-config-generator.ts +0 -38
- package/libs/instance-factories/views/templates/runtime/runtime-view-renderer.d.ts.map +0 -1
- package/libs/instance-factories/views/templates/runtime/runtime-view-renderer.js.map +0 -1
- package/libs/instance-factories/views/templates/runtime/runtime-view-renderer.ts +0 -474
- package/libs/instance-factories/views/templates/shared/__tests__/composite-patterns.test.ts +0 -242
- package/libs/instance-factories/views/templates/shared/adapter-types.d.ts +0 -77
- package/libs/instance-factories/views/templates/shared/adapter-types.d.ts.map +0 -1
- package/libs/instance-factories/views/templates/shared/adapter-types.js +0 -47
- package/libs/instance-factories/views/templates/shared/adapter-types.js.map +0 -1
- package/libs/instance-factories/views/templates/shared/adapter-types.ts +0 -142
- package/libs/instance-factories/views/templates/shared/atomic-components-registry.d.ts +0 -63
- package/libs/instance-factories/views/templates/shared/atomic-components-registry.d.ts.map +0 -1
- package/libs/instance-factories/views/templates/shared/atomic-components-registry.js +0 -822
- package/libs/instance-factories/views/templates/shared/atomic-components-registry.js.map +0 -1
- package/libs/instance-factories/views/templates/shared/atomic-components-registry.ts +0 -908
- package/libs/instance-factories/views/templates/shared/base-generator.d.ts +0 -247
- package/libs/instance-factories/views/templates/shared/base-generator.d.ts.map +0 -1
- package/libs/instance-factories/views/templates/shared/base-generator.js +0 -363
- package/libs/instance-factories/views/templates/shared/base-generator.js.map +0 -1
- package/libs/instance-factories/views/templates/shared/base-generator.ts +0 -608
- package/libs/instance-factories/views/templates/shared/component-metadata.d.ts +0 -254
- package/libs/instance-factories/views/templates/shared/component-metadata.d.ts.map +0 -1
- package/libs/instance-factories/views/templates/shared/component-metadata.js +0 -602
- package/libs/instance-factories/views/templates/shared/component-metadata.js.map +0 -1
- package/libs/instance-factories/views/templates/shared/component-metadata.ts +0 -803
- package/libs/instance-factories/views/templates/shared/composite-pattern-types.ts +0 -250
- package/libs/instance-factories/views/templates/shared/composite-patterns.ts +0 -535
- package/libs/instance-factories/views/templates/shared/index.ts +0 -68
- package/libs/instance-factories/views/templates/shared/pattern-validator.ts +0 -279
- package/libs/instance-factories/views/templates/shared/property-mapper.d.ts +0 -149
- package/libs/instance-factories/views/templates/shared/property-mapper.d.ts.map +0 -1
- package/libs/instance-factories/views/templates/shared/property-mapper.js +0 -580
- package/libs/instance-factories/views/templates/shared/property-mapper.js.map +0 -1
- package/libs/instance-factories/views/templates/shared/property-mapper.ts +0 -700
- package/libs/instance-factories/views/templates/shared/syntax-mapper.d.ts +0 -143
- package/libs/instance-factories/views/templates/shared/syntax-mapper.d.ts.map +0 -1
- package/libs/instance-factories/views/templates/shared/syntax-mapper.js +0 -420
- package/libs/instance-factories/views/templates/shared/syntax-mapper.js.map +0 -1
- package/libs/instance-factories/views/templates/shared/syntax-mapper.ts +0 -539
package/dist/libs/instance-factories/applications/templates/react/_view-components-source.js
DELETED
|
@@ -1,530 +0,0 @@
|
|
|
1
|
-
async function generate(context) {
|
|
2
|
-
const files = {};
|
|
3
|
-
files["FormView.tsx"] = generateFormView();
|
|
4
|
-
files["ListView.tsx"] = generateListView();
|
|
5
|
-
files["DetailView.tsx"] = generateDetailView();
|
|
6
|
-
files["DashboardView.tsx"] = generateDashboardView();
|
|
7
|
-
return files;
|
|
8
|
-
}
|
|
9
|
-
function generateFormView() {
|
|
10
|
-
return `/**
|
|
11
|
-
* StandardFormView - Controller-Based Form View Component
|
|
12
|
-
*
|
|
13
|
-
* LOCAL VERSION - Uses local hooks with instance-factory-specific API client
|
|
14
|
-
*
|
|
15
|
-
* Supports both Create and Edit modes with:
|
|
16
|
-
* - Controller-driven form generation
|
|
17
|
-
* - Smart input types (text, number, boolean, relationship selectors)
|
|
18
|
-
* - Auto-generated field detection
|
|
19
|
-
* - Validation and error handling
|
|
20
|
-
* - Lifecycle management (Edit/Evolve tabs)
|
|
21
|
-
* - Foreign key relationship fields
|
|
22
|
-
* - Two-panel layout (form + entity list)
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
import { useState, useEffect, useMemo } from 'react';
|
|
26
|
-
import { useAppStore } from '../../stores/appStore';
|
|
27
|
-
import { useEntitiesQuery, useModelSchemaQuery, useExecuteOperationMutation, useAvailableTransitionsQuery, useTransitionStateMutation } from '../../hooks/useApi';
|
|
28
|
-
import { isAutoGeneratedField, isFieldRequired, initializeFormData } from '../../utils/field-helpers';
|
|
29
|
-
import { RelationshipField } from '../../components/RelationshipField';
|
|
30
|
-
import type { View, Entity } from '../../types/api';
|
|
31
|
-
|
|
32
|
-
type FormMode = 'create' | 'update';
|
|
33
|
-
type ActiveTab = 'edit' | 'evolve';
|
|
34
|
-
|
|
35
|
-
interface StandardFormViewProps {
|
|
36
|
-
view: View;
|
|
37
|
-
spec?: any; // Full spec for controller access
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function FormView({ view, spec }: StandardFormViewProps) {
|
|
41
|
-
// Determine controller and model
|
|
42
|
-
let controllerName: string;
|
|
43
|
-
let modelName: string;
|
|
44
|
-
|
|
45
|
-
if (view.controller && spec) {
|
|
46
|
-
// New: Controller-based
|
|
47
|
-
controllerName = view.controller;
|
|
48
|
-
const controller = spec.controllers[view.controller];
|
|
49
|
-
modelName = controller.model;
|
|
50
|
-
} else {
|
|
51
|
-
// Legacy: Model-based
|
|
52
|
-
modelName = view.model as string;
|
|
53
|
-
controllerName = \`\${modelName}Controller\`;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const [selectedEntity, setSelectedEntity] = useState<Entity | null>(null);
|
|
57
|
-
const [formMode, setFormMode] = useState<FormMode>('create');
|
|
58
|
-
const [activeTab, setActiveTab] = useState<ActiveTab>('edit');
|
|
59
|
-
const [formData, setFormData] = useState<Record<string, any>>({});
|
|
60
|
-
const [errors, setErrors] = useState<Record<string, string>>({});
|
|
61
|
-
const [selectedLifecycleStates, setSelectedLifecycleStates] = useState<Record<string, string>>({});
|
|
62
|
-
|
|
63
|
-
// Fetch entities and schema - USES LOCAL HOOKS
|
|
64
|
-
const { data: entities = [], isLoading: entitiesLoading } = useEntitiesQuery(controllerName, modelName);
|
|
65
|
-
const { data: schema } = useModelSchemaQuery(modelName);
|
|
66
|
-
const { mutate: executeOperation, isPending } = useExecuteOperationMutation();
|
|
67
|
-
const { mutate: transitionState, isPending: isTransitioning } = useTransitionStateMutation();
|
|
68
|
-
|
|
69
|
-
const attributes = schema?.attributes || {};
|
|
70
|
-
const relationships = schema?.relationships || {};
|
|
71
|
-
const lifecycles = schema?.lifecycles || {};
|
|
72
|
-
|
|
73
|
-
// Get entities store for relationship resolution
|
|
74
|
-
const entitiesStore = useAppStore((state) => state.entities);
|
|
75
|
-
|
|
76
|
-
// Sync selected entity with updated entities list
|
|
77
|
-
useEffect(() => {
|
|
78
|
-
if (selectedEntity && entities.length > 0) {
|
|
79
|
-
const updatedEntity = entities.find((e) => e.id === selectedEntity.id);
|
|
80
|
-
if (updatedEntity) {
|
|
81
|
-
const oldStates = JSON.stringify(selectedEntity.metadata?.lifecycleStates || {});
|
|
82
|
-
const newStates = JSON.stringify(updatedEntity.metadata?.lifecycleStates || {});
|
|
83
|
-
|
|
84
|
-
if (oldStates !== newStates) {
|
|
85
|
-
setSelectedEntity(updatedEntity);
|
|
86
|
-
setSelectedLifecycleStates({ ...(updatedEntity.metadata?.lifecycleStates || {}) });
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}, [entities, selectedEntity?.id]);
|
|
91
|
-
|
|
92
|
-
// Initialize form data when schema changes or when switching modes
|
|
93
|
-
useEffect(() => {
|
|
94
|
-
if (selectedEntity && formMode === 'update') {
|
|
95
|
-
setFormData({ ...selectedEntity.data });
|
|
96
|
-
const currentStates = selectedEntity.metadata?.lifecycleStates || {};
|
|
97
|
-
setSelectedLifecycleStates({ ...currentStates });
|
|
98
|
-
} else if (schema?.attributes) {
|
|
99
|
-
setFormData(initializeFormData(schema.attributes, { includeAutoGenerated: false }));
|
|
100
|
-
setSelectedLifecycleStates({});
|
|
101
|
-
}
|
|
102
|
-
}, [selectedEntity, formMode, schema]);
|
|
103
|
-
|
|
104
|
-
// Check if field should be shown
|
|
105
|
-
const shouldShowField = (attrName: string, attrDef: any): boolean => {
|
|
106
|
-
const isAuto = isAutoGeneratedField(attrName, attrDef);
|
|
107
|
-
// In create mode, hide auto-generated fields
|
|
108
|
-
if (formMode === 'create' && isAuto) {
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
111
|
-
// In update mode, show all fields
|
|
112
|
-
return true;
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
// Handle form submission
|
|
116
|
-
const handleSubmit = async (e: React.FormEvent) => {
|
|
117
|
-
e.preventDefault();
|
|
118
|
-
setErrors({});
|
|
119
|
-
|
|
120
|
-
const operation = formMode === 'create' ? 'create' : 'update';
|
|
121
|
-
const params = formMode === 'update' && selectedEntity
|
|
122
|
-
? { ...formData, id: selectedEntity.id }
|
|
123
|
-
: formData;
|
|
124
|
-
|
|
125
|
-
executeOperation(
|
|
126
|
-
{ controllerName, operationName: operation, params },
|
|
127
|
-
{
|
|
128
|
-
onSuccess: () => {
|
|
129
|
-
if (formMode === 'create') {
|
|
130
|
-
setFormData(initializeFormData(schema.attributes, { includeAutoGenerated: false }));
|
|
131
|
-
}
|
|
132
|
-
setFormMode('create');
|
|
133
|
-
setSelectedEntity(null);
|
|
134
|
-
},
|
|
135
|
-
onError: (error: any) => {
|
|
136
|
-
setErrors({ _form: error.message || 'Operation failed' });
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
);
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
// Handle entity selection for editing
|
|
143
|
-
const handleEntitySelect = (entity: Entity) => {
|
|
144
|
-
setSelectedEntity(entity);
|
|
145
|
-
setFormMode('update');
|
|
146
|
-
setActiveTab('edit');
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
// Handle lifecycle state transition
|
|
150
|
-
const handleLifecycleTransition = (lifecycleName: string, toState: string) => {
|
|
151
|
-
if (!selectedEntity) return;
|
|
152
|
-
|
|
153
|
-
transitionState(
|
|
154
|
-
{ modelName, entityId: selectedEntity.id, toState, lifecycleName },
|
|
155
|
-
{
|
|
156
|
-
onSuccess: () => {
|
|
157
|
-
setSelectedLifecycleStates((prev) => ({ ...prev, [lifecycleName]: toState }));
|
|
158
|
-
},
|
|
159
|
-
onError: (error: any) => {
|
|
160
|
-
setErrors({ _lifecycle: error.message || 'Transition failed' });
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
);
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
// Render field input
|
|
167
|
-
const renderFieldInput = (attrName: string, attrDef: any) => {
|
|
168
|
-
const value = formData[attrName] ?? '';
|
|
169
|
-
const isRequired = isFieldRequired(attrDef);
|
|
170
|
-
|
|
171
|
-
const handleChange = (newValue: any) => {
|
|
172
|
-
setFormData((prev) => ({ ...prev, [attrName]: newValue }));
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
// Boolean field
|
|
176
|
-
if (attrDef.type === 'Boolean') {
|
|
177
|
-
return (
|
|
178
|
-
<input
|
|
179
|
-
type="checkbox"
|
|
180
|
-
checked={!!value}
|
|
181
|
-
onChange={(e) => handleChange(e.target.checked)}
|
|
182
|
-
className="h-4 w-4 rounded border-gray-300"
|
|
183
|
-
/>
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Number field
|
|
188
|
-
if (attrDef.type === 'Integer' || attrDef.type === 'Float' || attrDef.type === 'Decimal') {
|
|
189
|
-
return (
|
|
190
|
-
<input
|
|
191
|
-
type="number"
|
|
192
|
-
value={value}
|
|
193
|
-
onChange={(e) => handleChange(e.target.value ? Number(e.target.value) : '')}
|
|
194
|
-
required={isRequired}
|
|
195
|
-
className="block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
|
|
196
|
-
/>
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Text field (default)
|
|
201
|
-
return (
|
|
202
|
-
<input
|
|
203
|
-
type="text"
|
|
204
|
-
value={value}
|
|
205
|
-
onChange={(e) => handleChange(e.target.value)}
|
|
206
|
-
required={isRequired}
|
|
207
|
-
className="block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
|
|
208
|
-
/>
|
|
209
|
-
);
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
return (
|
|
213
|
-
<div className="flex gap-4 h-full">
|
|
214
|
-
{/* Form Panel */}
|
|
215
|
-
<div className="flex-1 bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
|
216
|
-
<h2 className="text-2xl font-bold mb-4 text-gray-800 dark:text-white">
|
|
217
|
-
{formMode === 'create' ? \`Create \${modelName}\` : \`Edit \${modelName}\`}
|
|
218
|
-
</h2>
|
|
219
|
-
|
|
220
|
-
{/* Tabs for Edit/Evolve modes */}
|
|
221
|
-
{formMode === 'update' && Object.keys(lifecycles).length > 0 && (
|
|
222
|
-
<div className="flex gap-2 mb-4 border-b border-gray-200">
|
|
223
|
-
<button
|
|
224
|
-
onClick={() => setActiveTab('edit')}
|
|
225
|
-
className={\`px-4 py-2 font-medium \${activeTab === 'edit' ? 'border-b-2 border-blue-500 text-blue-600' : 'text-gray-500'}\`}
|
|
226
|
-
>
|
|
227
|
-
Edit Data
|
|
228
|
-
</button>
|
|
229
|
-
<button
|
|
230
|
-
onClick={() => setActiveTab('evolve')}
|
|
231
|
-
className={\`px-4 py-2 font-medium \${activeTab === 'evolve' ? 'border-b-2 border-blue-500 text-blue-600' : 'text-gray-500'}\`}
|
|
232
|
-
>
|
|
233
|
-
Lifecycle
|
|
234
|
-
</button>
|
|
235
|
-
</div>
|
|
236
|
-
)}
|
|
237
|
-
|
|
238
|
-
{activeTab === 'edit' ? (
|
|
239
|
-
<form onSubmit={handleSubmit} className="space-y-4">
|
|
240
|
-
{errors._form && (
|
|
241
|
-
<div className="p-3 bg-red-100 text-red-700 rounded-md">{errors._form}</div>
|
|
242
|
-
)}
|
|
243
|
-
|
|
244
|
-
{/* Attributes */}
|
|
245
|
-
{Object.entries(attributes).map(([attrName, attrDef]: [string, any]) => {
|
|
246
|
-
if (!shouldShowField(attrName, attrDef)) return null;
|
|
247
|
-
|
|
248
|
-
return (
|
|
249
|
-
<div key={attrName}>
|
|
250
|
-
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
251
|
-
{attrName}
|
|
252
|
-
{isFieldRequired(attrDef) && <span className="text-red-500">*</span>}
|
|
253
|
-
</label>
|
|
254
|
-
{renderFieldInput(attrName, attrDef)}
|
|
255
|
-
</div>
|
|
256
|
-
);
|
|
257
|
-
})}
|
|
258
|
-
|
|
259
|
-
{/* Relationships */}
|
|
260
|
-
{Object.entries(relationships).map(([relName, relDef]: [string, any]) => (
|
|
261
|
-
<RelationshipField
|
|
262
|
-
key={relName}
|
|
263
|
-
name={relName}
|
|
264
|
-
definition={relDef}
|
|
265
|
-
value={formData[relName]}
|
|
266
|
-
onChange={(value) => setFormData((prev) => ({ ...prev, [relName]: value }))}
|
|
267
|
-
entities={entitiesStore}
|
|
268
|
-
/>
|
|
269
|
-
))}
|
|
270
|
-
|
|
271
|
-
<div className="flex gap-2 pt-4">
|
|
272
|
-
<button
|
|
273
|
-
type="submit"
|
|
274
|
-
disabled={isPending}
|
|
275
|
-
className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 disabled:opacity-50"
|
|
276
|
-
>
|
|
277
|
-
{isPending ? 'Saving...' : formMode === 'create' ? 'Create' : 'Update'}
|
|
278
|
-
</button>
|
|
279
|
-
{formMode === 'update' && (
|
|
280
|
-
<button
|
|
281
|
-
type="button"
|
|
282
|
-
onClick={() => {
|
|
283
|
-
setFormMode('create');
|
|
284
|
-
setSelectedEntity(null);
|
|
285
|
-
setFormData(initializeFormData(schema.attributes, { includeAutoGenerated: false }));
|
|
286
|
-
}}
|
|
287
|
-
className="px-4 py-2 bg-gray-500 text-white rounded-md hover:bg-gray-600"
|
|
288
|
-
>
|
|
289
|
-
Cancel
|
|
290
|
-
</button>
|
|
291
|
-
)}
|
|
292
|
-
</div>
|
|
293
|
-
</form>
|
|
294
|
-
) : (
|
|
295
|
-
/* Lifecycle Tab */
|
|
296
|
-
<div className="space-y-4">
|
|
297
|
-
{errors._lifecycle && (
|
|
298
|
-
<div className="p-3 bg-red-100 text-red-700 rounded-md">{errors._lifecycle}</div>
|
|
299
|
-
)}
|
|
300
|
-
|
|
301
|
-
{Object.entries(lifecycles).map(([lifecycleName, lifecycle]: [string, any]) => {
|
|
302
|
-
const currentState = selectedLifecycleStates[lifecycleName] || lifecycle.initial;
|
|
303
|
-
|
|
304
|
-
return (
|
|
305
|
-
<div key={lifecycleName} className="border border-gray-200 rounded-lg p-4">
|
|
306
|
-
<h3 className="font-semibold text-lg mb-2">{lifecycleName}</h3>
|
|
307
|
-
<p className="text-sm text-gray-600 mb-3">Current: {currentState}</p>
|
|
308
|
-
|
|
309
|
-
<div className="flex flex-wrap gap-2">
|
|
310
|
-
{Object.keys(lifecycle.states || {}).map((state) => (
|
|
311
|
-
<button
|
|
312
|
-
key={state}
|
|
313
|
-
onClick={() => handleLifecycleTransition(lifecycleName, state)}
|
|
314
|
-
disabled={isTransitioning || state === currentState}
|
|
315
|
-
className={\`px-3 py-1 rounded-md \${state === currentState ? 'bg-blue-600 text-white' : 'bg-gray-200 text-gray-700 hover:bg-gray-300'} disabled:opacity-50\`}
|
|
316
|
-
>
|
|
317
|
-
{state}
|
|
318
|
-
</button>
|
|
319
|
-
))}
|
|
320
|
-
</div>
|
|
321
|
-
</div>
|
|
322
|
-
);
|
|
323
|
-
})}
|
|
324
|
-
</div>
|
|
325
|
-
)}
|
|
326
|
-
</div>
|
|
327
|
-
|
|
328
|
-
{/* Entity List Panel */}
|
|
329
|
-
<div className="w-80 bg-white dark:bg-gray-800 rounded-lg shadow p-4">
|
|
330
|
-
<h3 className="text-lg font-semibold mb-3 text-gray-800 dark:text-white">
|
|
331
|
-
{modelName} List
|
|
332
|
-
</h3>
|
|
333
|
-
|
|
334
|
-
{entitiesLoading ? (
|
|
335
|
-
<p className="text-gray-500">Loading...</p>
|
|
336
|
-
) : entities.length === 0 ? (
|
|
337
|
-
<p className="text-gray-500 italic">No entities yet</p>
|
|
338
|
-
) : (
|
|
339
|
-
<div className="space-y-2">
|
|
340
|
-
{entities.map((entity) => (
|
|
341
|
-
<div
|
|
342
|
-
key={entity.id}
|
|
343
|
-
onClick={() => handleEntitySelect(entity)}
|
|
344
|
-
className={\`p-3 rounded-md cursor-pointer \${selectedEntity?.id === entity.id ? 'bg-blue-100 border-blue-500' : 'bg-gray-50 hover:bg-gray-100'} border\`}
|
|
345
|
-
>
|
|
346
|
-
<p className="font-medium text-sm">{entity.id}</p>
|
|
347
|
-
{Object.entries(entity.data).slice(0, 2).map(([key, value]) => (
|
|
348
|
-
<p key={key} className="text-xs text-gray-600">
|
|
349
|
-
{key}: {String(value)}
|
|
350
|
-
</p>
|
|
351
|
-
))}
|
|
352
|
-
</div>
|
|
353
|
-
))}
|
|
354
|
-
</div>
|
|
355
|
-
)}
|
|
356
|
-
</div>
|
|
357
|
-
</div>
|
|
358
|
-
);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
export default FormView;
|
|
362
|
-
`;
|
|
363
|
-
}
|
|
364
|
-
function generateListView() {
|
|
365
|
-
return `/**
|
|
366
|
-
* StandardListView - Controller-Based List View Component
|
|
367
|
-
*
|
|
368
|
-
* LOCAL VERSION - Uses local hooks with instance-factory-specific API client
|
|
369
|
-
*/
|
|
370
|
-
|
|
371
|
-
import { useEntitiesQuery, useModelSchemaQuery } from '../../hooks/useApi';
|
|
372
|
-
import type { View } from '../../types/api';
|
|
373
|
-
|
|
374
|
-
interface StandardListViewProps {
|
|
375
|
-
view: View;
|
|
376
|
-
spec?: any;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
export function ListView({ view, spec }: StandardListViewProps) {
|
|
380
|
-
let controllerName: string;
|
|
381
|
-
let modelName: string;
|
|
382
|
-
|
|
383
|
-
if (view.controller && spec) {
|
|
384
|
-
controllerName = view.controller;
|
|
385
|
-
const controller = spec.controllers[view.controller];
|
|
386
|
-
modelName = controller.model;
|
|
387
|
-
} else {
|
|
388
|
-
modelName = view.model as string;
|
|
389
|
-
controllerName = \`\${modelName}Controller\`;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
const { data: entities = [], isLoading } = useEntitiesQuery(controllerName, modelName);
|
|
393
|
-
const { data: schema } = useModelSchemaQuery(modelName);
|
|
394
|
-
|
|
395
|
-
if (isLoading) {
|
|
396
|
-
return <div className="p-4">Loading...</div>;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
return (
|
|
400
|
-
<div className="p-6">
|
|
401
|
-
<h2 className="text-2xl font-bold mb-4">{modelName} List</h2>
|
|
402
|
-
<div className="overflow-x-auto">
|
|
403
|
-
<table className="min-w-full bg-white border border-gray-200">
|
|
404
|
-
<thead className="bg-gray-50">
|
|
405
|
-
<tr>
|
|
406
|
-
<th className="px-4 py-2 border-b">ID</th>
|
|
407
|
-
{schema && Object.keys(schema.attributes || {}).map((attr) => (
|
|
408
|
-
<th key={attr} className="px-4 py-2 border-b">{attr}</th>
|
|
409
|
-
))}
|
|
410
|
-
</tr>
|
|
411
|
-
</thead>
|
|
412
|
-
<tbody>
|
|
413
|
-
{entities.map((entity) => (
|
|
414
|
-
<tr key={entity.id} className="hover:bg-gray-50">
|
|
415
|
-
<td className="px-4 py-2 border-b">{entity.id}</td>
|
|
416
|
-
{schema && Object.keys(schema.attributes || {}).map((attr) => (
|
|
417
|
-
<td key={attr} className="px-4 py-2 border-b">
|
|
418
|
-
{String(entity.data[attr] ?? '')}
|
|
419
|
-
</td>
|
|
420
|
-
))}
|
|
421
|
-
</tr>
|
|
422
|
-
))}
|
|
423
|
-
</tbody>
|
|
424
|
-
</table>
|
|
425
|
-
</div>
|
|
426
|
-
</div>
|
|
427
|
-
);
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
export default ListView;
|
|
431
|
-
`;
|
|
432
|
-
}
|
|
433
|
-
function generateDetailView() {
|
|
434
|
-
return `/**
|
|
435
|
-
* StandardDetailView - Controller-Based Detail View Component
|
|
436
|
-
*
|
|
437
|
-
* LOCAL VERSION - Uses local hooks with instance-factory-specific API client
|
|
438
|
-
*/
|
|
439
|
-
|
|
440
|
-
import { useEntitiesQuery, useModelSchemaQuery } from '../../hooks/useApi';
|
|
441
|
-
import type { View } from '../../types/api';
|
|
442
|
-
|
|
443
|
-
interface StandardDetailViewProps {
|
|
444
|
-
view: View;
|
|
445
|
-
spec?: any;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
export function DetailView({ view, spec }: StandardDetailViewProps) {
|
|
449
|
-
let controllerName: string;
|
|
450
|
-
let modelName: string;
|
|
451
|
-
|
|
452
|
-
if (view.controller && spec) {
|
|
453
|
-
controllerName = view.controller;
|
|
454
|
-
const controller = spec.controllers[view.controller];
|
|
455
|
-
modelName = controller.model;
|
|
456
|
-
} else {
|
|
457
|
-
modelName = view.model as string;
|
|
458
|
-
controllerName = \`\${modelName}Controller\`;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
const { data: entities = [] } = useEntitiesQuery(controllerName, modelName);
|
|
462
|
-
const { data: schema } = useModelSchemaQuery(modelName);
|
|
463
|
-
const entity = entities[0];
|
|
464
|
-
|
|
465
|
-
if (!entity) {
|
|
466
|
-
return <div className="p-4">No entity selected</div>;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
return (
|
|
470
|
-
<div className="p-6">
|
|
471
|
-
<h2 className="text-2xl font-bold mb-4">{modelName} Detail</h2>
|
|
472
|
-
<div className="space-y-2">
|
|
473
|
-
<div>
|
|
474
|
-
<span className="font-semibold">ID:</span> {entity.id}
|
|
475
|
-
</div>
|
|
476
|
-
{schema && Object.entries(schema.attributes || {}).map(([attr, def]: [string, any]) => (
|
|
477
|
-
<div key={attr}>
|
|
478
|
-
<span className="font-semibold">{attr}:</span> {String(entity.data[attr] ?? '')}
|
|
479
|
-
</div>
|
|
480
|
-
))}
|
|
481
|
-
</div>
|
|
482
|
-
</div>
|
|
483
|
-
);
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
export default DetailView;
|
|
487
|
-
`;
|
|
488
|
-
}
|
|
489
|
-
function generateDashboardView() {
|
|
490
|
-
return `/**
|
|
491
|
-
* StandardDashboardView - Controller-Based Dashboard View Component
|
|
492
|
-
*
|
|
493
|
-
* LOCAL VERSION - Uses local hooks with instance-factory-specific API client
|
|
494
|
-
*/
|
|
495
|
-
|
|
496
|
-
import type { View } from '../../types/api';
|
|
497
|
-
|
|
498
|
-
interface StandardDashboardViewProps {
|
|
499
|
-
view: View;
|
|
500
|
-
spec?: any;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
export function DashboardView({ view, spec }: StandardDashboardViewProps) {
|
|
504
|
-
return (
|
|
505
|
-
<div className="p-6">
|
|
506
|
-
<h2 className="text-2xl font-bold mb-4">Dashboard</h2>
|
|
507
|
-
<div className="grid grid-cols-3 gap-4">
|
|
508
|
-
<div className="bg-white p-4 rounded-lg shadow">
|
|
509
|
-
<h3 className="text-lg font-semibold">Card 1</h3>
|
|
510
|
-
<p>Content here</p>
|
|
511
|
-
</div>
|
|
512
|
-
<div className="bg-white p-4 rounded-lg shadow">
|
|
513
|
-
<h3 className="text-lg font-semibold">Card 2</h3>
|
|
514
|
-
<p>Content here</p>
|
|
515
|
-
</div>
|
|
516
|
-
<div className="bg-white p-4 rounded-lg shadow">
|
|
517
|
-
<h3 className="text-lg font-semibold">Card 3</h3>
|
|
518
|
-
<p>Content here</p>
|
|
519
|
-
</div>
|
|
520
|
-
</div>
|
|
521
|
-
</div>
|
|
522
|
-
);
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
export default DashboardView;
|
|
526
|
-
`;
|
|
527
|
-
}
|
|
528
|
-
export {
|
|
529
|
-
generate
|
|
530
|
-
};
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
function generateAppTsx(context) {
|
|
2
|
-
const { spec } = context;
|
|
3
|
-
let viewList = [];
|
|
4
|
-
if (spec.views) {
|
|
5
|
-
viewList = Array.isArray(spec.views) ? spec.views : Object.entries(spec.views).map(([name, def]) => ({ name, ...def }));
|
|
6
|
-
}
|
|
7
|
-
const modelViews = /* @__PURE__ */ new Map();
|
|
8
|
-
const dashboardView = viewList.find((v) => v.type === "dashboard")?.name || null;
|
|
9
|
-
for (const view of viewList) {
|
|
10
|
-
const model = Array.isArray(view.model) ? view.model[0] : view.model || view.primaryModel;
|
|
11
|
-
if (!model) continue;
|
|
12
|
-
if (!modelViews.has(model)) modelViews.set(model, { other: [] });
|
|
13
|
-
const entry = modelViews.get(model);
|
|
14
|
-
const type = view.type || "list";
|
|
15
|
-
if (type === "list") entry.list = view.name;
|
|
16
|
-
else if (type === "detail") entry.detail = view.name;
|
|
17
|
-
else if (type === "form") entry.form = view.name;
|
|
18
|
-
else if (type !== "dashboard") entry.other.push(view.name);
|
|
19
|
-
}
|
|
20
|
-
const imports = viewList.map((v) => `import ${v.name} from './components/${v.name}';`).join("\n");
|
|
21
|
-
const routes = viewList.map((v) => {
|
|
22
|
-
const path = `/${v.name.toLowerCase().replace("view", "")}`;
|
|
23
|
-
return ` <Route path="${path}" element={<${v.name} />} />`;
|
|
24
|
-
}).join("\n");
|
|
25
|
-
const defaultView = dashboardView || viewList[0]?.name || "div";
|
|
26
|
-
const defaultElement = defaultView === "div" ? '<div className="p-8">Welcome</div>' : `<${defaultView} />`;
|
|
27
|
-
const navGroups = Array.from(modelViews.entries()).map(([model, views]) => {
|
|
28
|
-
const lower = model.charAt(0).toLowerCase() + model.slice(1);
|
|
29
|
-
const listPath = views.list ? `/${views.list.toLowerCase().replace("view", "")}` : "";
|
|
30
|
-
const formPath = views.form ? `/${views.form.toLowerCase().replace("view", "")}` : "";
|
|
31
|
-
return ` <div>
|
|
32
|
-
<h3 className="px-3 text-xs font-semibold text-gray-400 uppercase tracking-wider mb-1">${model}s</h3>
|
|
33
|
-
${listPath ? ` <a href="${listPath}" className="block px-3 py-1.5 text-sm text-gray-700 hover:bg-gray-100 rounded">Browse</a>` : ""}
|
|
34
|
-
${formPath ? ` <a href="${formPath}" className="block px-3 py-1.5 text-sm text-gray-700 hover:bg-gray-100 rounded">+ New</a>` : ""}
|
|
35
|
-
</div>`;
|
|
36
|
-
}).join("\n");
|
|
37
|
-
return `import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
|
38
|
-
${imports}
|
|
39
|
-
|
|
40
|
-
function App() {
|
|
41
|
-
return (
|
|
42
|
-
<Router>
|
|
43
|
-
<div className="flex min-h-screen bg-gray-50">
|
|
44
|
-
{/* Sidebar */}
|
|
45
|
-
<aside className="w-56 bg-white border-r border-gray-200 p-4 space-y-5">
|
|
46
|
-
<div>
|
|
47
|
-
<h1 className="text-lg font-bold text-gray-900">SpecVerse App</h1>
|
|
48
|
-
<p className="text-xs text-gray-400">Generated from specification</p>
|
|
49
|
-
</div>
|
|
50
|
-
${dashboardView ? ` <a href="/dashboard" className="block px-3 py-2 text-sm font-medium text-gray-700 hover:bg-gray-100 rounded">Dashboard</a>` : ""}
|
|
51
|
-
${navGroups}
|
|
52
|
-
</aside>
|
|
53
|
-
|
|
54
|
-
{/* Main content */}
|
|
55
|
-
<main className="flex-1 overflow-auto">
|
|
56
|
-
<Routes>
|
|
57
|
-
<Route path="/" element={${defaultElement}} />
|
|
58
|
-
${dashboardView ? ` <Route path="/dashboard" element={<${dashboardView} />} />` : ""}
|
|
59
|
-
${routes}
|
|
60
|
-
<Route path="*" element={<div className="p-8"><h1 className="text-2xl">404 - Not Found</h1></div>} />
|
|
61
|
-
</Routes>
|
|
62
|
-
</main>
|
|
63
|
-
</div>
|
|
64
|
-
</Router>
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export default App;
|
|
69
|
-
`;
|
|
70
|
-
}
|
|
71
|
-
export {
|
|
72
|
-
generateAppTsx as default
|
|
73
|
-
};
|
package/dist/libs/instance-factories/applications/templates/react/field-helpers-generator.js
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
async function generate(context) {
|
|
2
|
-
return `/**
|
|
3
|
-
* Utility functions for form field handling
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Check if a field should be hidden because it's auto-generated
|
|
8
|
-
*/
|
|
9
|
-
export function isAutoGeneratedField(attrName: string, attrDef: any): boolean {
|
|
10
|
-
const typeStr = typeof attrDef === 'string' ? attrDef : attrDef?.type || '';
|
|
11
|
-
|
|
12
|
-
// Hide UUID id field
|
|
13
|
-
if (attrName === 'id' && typeStr.includes('UUID')) return true;
|
|
14
|
-
|
|
15
|
-
// Hide auto-generated DateTime fields
|
|
16
|
-
const autoDateFields = ['createdAt', 'updatedAt', 'joinedAt', 'publishedAt'];
|
|
17
|
-
if (autoDateFields.includes(attrName) && typeStr.includes('DateTime')) return true;
|
|
18
|
-
|
|
19
|
-
return false;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Check if a field is required
|
|
24
|
-
*/
|
|
25
|
-
export function isFieldRequired(attrDef: any): boolean {
|
|
26
|
-
if (!attrDef) return false;
|
|
27
|
-
if (typeof attrDef === 'string') {
|
|
28
|
-
return attrDef.toLowerCase().includes('required');
|
|
29
|
-
}
|
|
30
|
-
return attrDef.required === true;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Get default value for a field based on its type
|
|
35
|
-
*/
|
|
36
|
-
export function getFieldDefaultValue(attrDef: any): any {
|
|
37
|
-
if (!attrDef) return '';
|
|
38
|
-
|
|
39
|
-
const typeStr = typeof attrDef === 'string' ? attrDef : attrDef?.type || '';
|
|
40
|
-
|
|
41
|
-
// Check if schema has a default value
|
|
42
|
-
if (attrDef && typeof attrDef === 'object' && attrDef.default !== undefined) {
|
|
43
|
-
const defaultVal = attrDef.default;
|
|
44
|
-
// Parse string defaults to proper types
|
|
45
|
-
if (typeStr.toLowerCase().includes('bool')) {
|
|
46
|
-
return defaultVal === 'true' || defaultVal === true;
|
|
47
|
-
} else if (
|
|
48
|
-
typeStr.toLowerCase().includes('int') ||
|
|
49
|
-
typeStr.toLowerCase().includes('number')
|
|
50
|
-
) {
|
|
51
|
-
return typeof defaultVal === 'number' ? defaultVal : parseInt(defaultVal) || 0;
|
|
52
|
-
} else if (typeStr.toLowerCase().includes('string')) {
|
|
53
|
-
// Remove surrounding quotes if present
|
|
54
|
-
return typeof defaultVal === 'string'
|
|
55
|
-
? defaultVal.replace(/^"|"$/g, '')
|
|
56
|
-
: defaultVal;
|
|
57
|
-
} else {
|
|
58
|
-
return defaultVal;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Otherwise use type-based defaults
|
|
63
|
-
if (typeStr.toLowerCase().includes('bool')) {
|
|
64
|
-
return false;
|
|
65
|
-
} else if (
|
|
66
|
-
typeStr.toLowerCase().includes('int') ||
|
|
67
|
-
typeStr.toLowerCase().includes('number')
|
|
68
|
-
) {
|
|
69
|
-
return 0;
|
|
70
|
-
} else {
|
|
71
|
-
return '';
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Initialize form data from schema attributes
|
|
77
|
-
*/
|
|
78
|
-
export function initializeFormData(
|
|
79
|
-
attributes: Record<string, any>,
|
|
80
|
-
options: { includeAutoGenerated?: boolean } = {}
|
|
81
|
-
): Record<string, any> {
|
|
82
|
-
const data: Record<string, any> = {};
|
|
83
|
-
|
|
84
|
-
Object.entries(attributes).forEach(([attrName, attrDef]) => {
|
|
85
|
-
// Skip auto-generated fields unless explicitly included
|
|
86
|
-
if (!options.includeAutoGenerated && isAutoGeneratedField(attrName, attrDef)) {
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
data[attrName] = getFieldDefaultValue(attrDef);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
return data;
|
|
94
|
-
}
|
|
95
|
-
`;
|
|
96
|
-
}
|
|
97
|
-
export {
|
|
98
|
-
generate
|
|
99
|
-
};
|