@specverse/engines 4.1.5 → 4.1.7

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.
Files changed (122) hide show
  1. package/dist/libs/instance-factories/applications/templates/generic/backend-env-generator.js +22 -0
  2. package/dist/libs/instance-factories/applications/templates/generic/backend-package-json-generator.js +66 -0
  3. package/dist/libs/instance-factories/applications/templates/generic/backend-tsconfig-generator.js +54 -0
  4. package/dist/libs/instance-factories/applications/templates/generic/main-generator.js +290 -0
  5. package/dist/libs/instance-factories/applications/templates/react/_view-components-source.js +530 -0
  6. package/dist/libs/instance-factories/applications/templates/react/api-client-generator.js +437 -0
  7. package/dist/libs/instance-factories/applications/templates/react/api-types-generator.js +146 -0
  8. package/dist/libs/instance-factories/applications/templates/react/app-tsx-generator.js +73 -0
  9. package/dist/libs/instance-factories/applications/templates/react/env-example-generator.js +18 -0
  10. package/dist/libs/instance-factories/applications/templates/react/field-helpers-generator.js +99 -0
  11. package/dist/libs/instance-factories/applications/templates/react/gitignore-generator.js +35 -0
  12. package/dist/libs/instance-factories/applications/templates/react/index-css-generator.js +9 -0
  13. package/dist/libs/instance-factories/applications/templates/react/index-html-generator.js +23 -0
  14. package/dist/libs/instance-factories/applications/templates/react/main-tsx-generator.js +29 -0
  15. package/dist/libs/instance-factories/applications/templates/react/package-json-generator.js +49 -0
  16. package/dist/libs/instance-factories/applications/templates/react/pattern-adapter-generator.js +156 -0
  17. package/dist/libs/instance-factories/applications/templates/react/react-pattern-adapter.js +935 -0
  18. package/dist/libs/instance-factories/applications/templates/react/relationship-field-generator.js +143 -0
  19. package/dist/libs/instance-factories/applications/templates/react/runtime-app-tsx-generator.js +101 -0
  20. package/dist/libs/instance-factories/applications/templates/react/runtime-package-json-generator.js +50 -0
  21. package/dist/libs/instance-factories/applications/templates/react/tailwind-adapter-generator.js +646 -0
  22. package/dist/libs/instance-factories/applications/templates/react/tailwind-adapter-wrapper-generator.js +65 -0
  23. package/dist/libs/instance-factories/applications/templates/react/tsconfig-generator.js +28 -0
  24. package/dist/libs/instance-factories/applications/templates/react/use-api-hooks-generator.js +132 -0
  25. package/dist/libs/instance-factories/applications/templates/react/view-dashboard-generator.js +143 -0
  26. package/dist/libs/instance-factories/applications/templates/react/view-detail-generator.js +143 -0
  27. package/dist/libs/instance-factories/applications/templates/react/view-form-generator.js +355 -0
  28. package/dist/libs/instance-factories/applications/templates/react/view-list-generator.js +91 -0
  29. package/dist/libs/instance-factories/applications/templates/react/view-router-generator.js +79 -0
  30. package/dist/libs/instance-factories/applications/templates/react/vite-config-generator.js +42 -0
  31. package/dist/libs/instance-factories/cli/templates/commander/cli-bin-wrapper-generator.js +11 -0
  32. package/dist/libs/instance-factories/cli/templates/commander/cli-entry-generator.js +111 -0
  33. package/dist/libs/instance-factories/cli/templates/commander/command-generator.js +928 -0
  34. package/dist/libs/instance-factories/communication/templates/eventemitter/bus-generator.js +83 -0
  35. package/dist/libs/instance-factories/communication/templates/eventemitter/publisher-generator.js +91 -0
  36. package/dist/libs/instance-factories/communication/templates/eventemitter/subscriber-generator.js +86 -0
  37. package/dist/libs/instance-factories/controllers/templates/fastify/meta-routes-generator.js +93 -0
  38. package/dist/libs/instance-factories/controllers/templates/fastify/routes-generator.js +280 -0
  39. package/dist/libs/instance-factories/controllers/templates/fastify/server-generator.js +125 -0
  40. package/dist/libs/instance-factories/infrastructure/templates/docker-k8s/infrastructure-generator.js +25 -0
  41. package/dist/libs/instance-factories/orms/templates/prisma/schema-generator.js +371 -0
  42. package/dist/libs/instance-factories/orms/templates/prisma/services-generator.js +266 -0
  43. package/dist/libs/instance-factories/scaffolding/templates/generic/env-example-generator.js +51 -0
  44. package/dist/libs/instance-factories/scaffolding/templates/generic/env-generator.js +61 -0
  45. package/dist/libs/instance-factories/scaffolding/templates/generic/gitignore-generator.js +59 -0
  46. package/dist/libs/instance-factories/scaffolding/templates/generic/package-json-generator.js +126 -0
  47. package/dist/libs/instance-factories/scaffolding/templates/generic/readme-generator.js +159 -0
  48. package/dist/libs/instance-factories/scaffolding/templates/generic/tsconfig-generator.js +56 -0
  49. package/dist/libs/instance-factories/scaffolding/templates/generic/tsconfig-react-generator.js +37 -0
  50. package/dist/libs/instance-factories/sdks/templates/python/sdk-generator.js +29 -0
  51. package/dist/libs/instance-factories/sdks/templates/typescript/sdk-generator.js +28 -0
  52. package/dist/libs/instance-factories/services/templates/memory/generate-interpreter.js +14 -0
  53. package/dist/libs/instance-factories/services/templates/memory/step-conventions-memory.js +415 -0
  54. package/dist/libs/instance-factories/services/templates/prisma/behavior-generator.js +177 -0
  55. package/dist/libs/instance-factories/services/templates/prisma/controller-generator.js +413 -0
  56. package/dist/libs/instance-factories/services/templates/prisma/service-generator.js +243 -0
  57. package/dist/libs/instance-factories/services/templates/prisma/step-conventions.js +264 -0
  58. package/dist/libs/instance-factories/services/templates/shared-patterns.js +24 -0
  59. package/dist/libs/instance-factories/shared/path-resolver.js +59 -0
  60. package/dist/libs/instance-factories/storage/templates/mongodb/config-generator.js +13 -0
  61. package/dist/libs/instance-factories/storage/templates/mongodb/docker-generator.js +16 -0
  62. package/dist/libs/instance-factories/storage/templates/postgresql/config-generator.js +45 -0
  63. package/dist/libs/instance-factories/storage/templates/postgresql/docker-generator.js +46 -0
  64. package/dist/libs/instance-factories/storage/templates/redis/config-generator.js +14 -0
  65. package/dist/libs/instance-factories/storage/templates/redis/docker-generator.js +16 -0
  66. package/dist/libs/instance-factories/test-generation.js +145 -0
  67. package/dist/libs/instance-factories/testing/templates/vitest/tests-generator.js +30 -0
  68. package/dist/libs/instance-factories/tools/templates/mcp/mcp-server-generator.js +149 -0
  69. package/dist/libs/instance-factories/tools/templates/mcp/static/src/controllers/MCPServerController.js +232 -0
  70. package/dist/libs/instance-factories/tools/templates/mcp/static/src/events/EventEmitter.js +49 -0
  71. package/dist/libs/instance-factories/tools/templates/mcp/static/src/index.js +18 -0
  72. package/dist/libs/instance-factories/tools/templates/mcp/static/src/interfaces/ResourceProvider.js +0 -0
  73. package/dist/libs/instance-factories/tools/templates/mcp/static/src/models/LibrarySuggestion.js +97 -0
  74. package/dist/libs/instance-factories/tools/templates/mcp/static/src/models/SpecVerseResource.js +64 -0
  75. package/dist/libs/instance-factories/tools/templates/mcp/static/src/server/mcp-server.js +182 -0
  76. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/CLIProxyService.js +1210 -0
  77. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/EmbeddedResourcesAdapter.js +172 -0
  78. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/EntityModuleService.js +240 -0
  79. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/HybridResourcesProvider.js +147 -0
  80. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/LibraryToolsService.js +281 -0
  81. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/OrchestratorBridge.js +409 -0
  82. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/OrchestratorToolsService.js +414 -0
  83. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/PromptToolsService.js +467 -0
  84. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/ResourcesProviderService.js +135 -0
  85. package/dist/libs/instance-factories/tools/templates/mcp/static/src/types/index.js +0 -0
  86. package/dist/libs/instance-factories/tools/templates/vscode/static/extension.js +965 -0
  87. package/dist/libs/instance-factories/tools/templates/vscode/vscode-extension-generator.js +238 -0
  88. package/dist/libs/instance-factories/validation/templates/zod/validation-generator.js +25 -0
  89. package/dist/libs/instance-factories/views/index.js +48 -0
  90. package/dist/libs/instance-factories/views/templates/react/adapters/antd-adapter.js +742 -0
  91. package/dist/libs/instance-factories/views/templates/react/adapters/mui-adapter.js +824 -0
  92. package/dist/libs/instance-factories/views/templates/react/adapters/shadcn-adapter.js +719 -0
  93. package/dist/libs/instance-factories/views/templates/react/app-generator.js +45 -0
  94. package/dist/libs/instance-factories/views/templates/react/components-generator.js +779 -0
  95. package/dist/libs/instance-factories/views/templates/react/forms-generator.js +285 -0
  96. package/dist/libs/instance-factories/views/templates/react/frontend-package-json-generator.js +46 -0
  97. package/dist/libs/instance-factories/views/templates/react/hooks-generator.js +111 -0
  98. package/dist/libs/instance-factories/views/templates/react/index-css-generator.js +9 -0
  99. package/dist/libs/instance-factories/views/templates/react/index-html-generator.js +23 -0
  100. package/dist/libs/instance-factories/views/templates/react/main-tsx-generator.js +21 -0
  101. package/dist/libs/instance-factories/views/templates/react/react-component-generator.js +299 -0
  102. package/dist/libs/instance-factories/views/templates/react/router-generator.js +136 -0
  103. package/dist/libs/instance-factories/views/templates/react/router-generic-generator.js +107 -0
  104. package/dist/libs/instance-factories/views/templates/react/shared-utils-generator.js +179 -0
  105. package/dist/libs/instance-factories/views/templates/react/spec-json-generator.js +7 -0
  106. package/dist/libs/instance-factories/views/templates/react/types-generator.js +56 -0
  107. package/dist/libs/instance-factories/views/templates/react/views-metadata-generator.js +27 -0
  108. package/dist/libs/instance-factories/views/templates/react/vite-config-generator.js +29 -0
  109. package/dist/libs/instance-factories/views/templates/runtime/runtime-view-renderer.js +261 -0
  110. package/dist/libs/instance-factories/views/templates/shared/adapter-types.js +34 -0
  111. package/dist/libs/instance-factories/views/templates/shared/atomic-components-registry.js +800 -0
  112. package/dist/libs/instance-factories/views/templates/shared/base-generator.js +305 -0
  113. package/dist/libs/instance-factories/views/templates/shared/component-metadata.js +517 -0
  114. package/dist/libs/instance-factories/views/templates/shared/composite-pattern-types.js +0 -0
  115. package/dist/libs/instance-factories/views/templates/shared/composite-patterns.js +445 -0
  116. package/dist/libs/instance-factories/views/templates/shared/index.js +80 -0
  117. package/dist/libs/instance-factories/views/templates/shared/pattern-validator.js +210 -0
  118. package/dist/libs/instance-factories/views/templates/shared/property-mapper.js +492 -0
  119. package/dist/libs/instance-factories/views/templates/shared/syntax-mapper.js +321 -0
  120. package/dist/realize/index.js +36 -12
  121. package/dist/realize/index.js.map +1 -1
  122. package/package.json +3 -2
@@ -0,0 +1,530 @@
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
+ };