@mostajs/setup 1.4.11 → 1.4.13
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/dist/api/wire-module.route.d.ts +11 -0
- package/dist/api/wire-module.route.js +126 -0
- package/dist/components/ReconfigPanel.d.ts +3 -1
- package/dist/components/ReconfigPanel.js +98 -2
- package/dist/components/SetupWizard.d.ts +3 -0
- package/dist/components/SetupWizard.js +107 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lib/setup.js +6 -2
- package/dist/register.d.ts +8 -0
- package/dist/register.js +24 -0
- package/dist/types/index.d.ts +3 -1
- package/package.json +17 -1
- package/wire.json +12 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory for POST /api/setup/wire-module
|
|
3
|
+
* Calls @mostajs/socle installModule() or uninstallModule()
|
|
4
|
+
*
|
|
5
|
+
* Body: { action: 'install' | 'uninstall', module: string }
|
|
6
|
+
* Response: { data: { ok: boolean, module: string, steps: [...] } }
|
|
7
|
+
*/
|
|
8
|
+
export declare function createWireModuleHandler(): {
|
|
9
|
+
GET: () => Promise<Response>;
|
|
10
|
+
POST: (req: Request) => Promise<Response>;
|
|
11
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// @mostajs/setup — Wire module API route factory
|
|
2
|
+
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
3
|
+
/**
|
|
4
|
+
* Factory for POST /api/setup/wire-module
|
|
5
|
+
* Calls @mostajs/socle installModule() or uninstallModule()
|
|
6
|
+
*
|
|
7
|
+
* Body: { action: 'install' | 'uninstall', module: string }
|
|
8
|
+
* Response: { data: { ok: boolean, module: string, steps: [...] } }
|
|
9
|
+
*/
|
|
10
|
+
export function createWireModuleHandler() {
|
|
11
|
+
async function POST(req) {
|
|
12
|
+
let body;
|
|
13
|
+
try {
|
|
14
|
+
body = await req.json();
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return Response.json({ error: { code: 'VALIDATION_ERROR', message: 'JSON invalide' } }, { status: 400 });
|
|
18
|
+
}
|
|
19
|
+
const { action, module: moduleName } = body;
|
|
20
|
+
if (!action || !moduleName) {
|
|
21
|
+
return Response.json({ error: { code: 'VALIDATION_ERROR', message: 'action et module requis' } }, { status: 400 });
|
|
22
|
+
}
|
|
23
|
+
if (!['install', 'uninstall'].includes(action)) {
|
|
24
|
+
return Response.json({ error: { code: 'VALIDATION_ERROR', message: "action doit etre 'install' ou 'uninstall'" } }, { status: 400 });
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
// Dynamic import to avoid hard dependency on @mostajs/socle
|
|
28
|
+
// Dynamic import — string indirection to avoid TS static resolution
|
|
29
|
+
const pkg = '@mostajs' + '/socle';
|
|
30
|
+
const socle = await import(/* webpackIgnore: true */ pkg);
|
|
31
|
+
const logs = [];
|
|
32
|
+
const opts = {
|
|
33
|
+
projectRoot: process.cwd(),
|
|
34
|
+
log: (msg) => logs.push(msg),
|
|
35
|
+
};
|
|
36
|
+
const result = action === 'install'
|
|
37
|
+
? socle.installModule(moduleName, opts)
|
|
38
|
+
: socle.uninstallModule(moduleName, opts);
|
|
39
|
+
return Response.json({
|
|
40
|
+
data: {
|
|
41
|
+
ok: result.success,
|
|
42
|
+
action,
|
|
43
|
+
module: moduleName,
|
|
44
|
+
steps: result.steps,
|
|
45
|
+
logs,
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
51
|
+
return Response.json({ error: { code: 'WIRE_ERROR', message } }, { status: 500 });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// GET — list available wire manifests
|
|
55
|
+
async function GET() {
|
|
56
|
+
try {
|
|
57
|
+
const fs = await import('fs');
|
|
58
|
+
const path = await import('path');
|
|
59
|
+
const root = process.cwd();
|
|
60
|
+
const found = [];
|
|
61
|
+
// Check modules/ directory (host overrides)
|
|
62
|
+
const modulesDir = path.join(root, 'modules');
|
|
63
|
+
if (fs.existsSync(modulesDir)) {
|
|
64
|
+
for (const f of fs.readdirSync(modulesDir).filter((f) => f.endsWith('.wire.json'))) {
|
|
65
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(modulesDir, f), 'utf8'));
|
|
66
|
+
const name = f.replace('.wire.json', '');
|
|
67
|
+
// Check if already wired (has the package in package.json deps or schemas wired)
|
|
68
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
|
|
69
|
+
const installed = !!pkg.dependencies?.[manifest.package];
|
|
70
|
+
found.push({
|
|
71
|
+
name,
|
|
72
|
+
source: 'modules/',
|
|
73
|
+
package: manifest.package,
|
|
74
|
+
version: manifest.version,
|
|
75
|
+
type: manifest.type || 'functional',
|
|
76
|
+
installed,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Check node_modules/@mostajs/*/<name>.wire.json
|
|
81
|
+
const mostaDir = path.join(root, 'node_modules', '@mostajs');
|
|
82
|
+
if (fs.existsSync(mostaDir)) {
|
|
83
|
+
for (const dir of fs.readdirSync(mostaDir)) {
|
|
84
|
+
if (found.some(f => f.name === dir))
|
|
85
|
+
continue;
|
|
86
|
+
const wireFile = path.join(mostaDir, dir, `${dir}.wire.json`);
|
|
87
|
+
if (fs.existsSync(wireFile)) {
|
|
88
|
+
const manifest = JSON.parse(fs.readFileSync(wireFile, 'utf8'));
|
|
89
|
+
// Check if already wired by looking at schemas or permissions in host files
|
|
90
|
+
const permFile = path.join(root, 'src/lib/permissions.ts');
|
|
91
|
+
let installed = false;
|
|
92
|
+
if (manifest.permissions?.permissionsConst && fs.existsSync(permFile)) {
|
|
93
|
+
installed = fs.readFileSync(permFile, 'utf8').includes(manifest.permissions.permissionsConst);
|
|
94
|
+
}
|
|
95
|
+
else if (manifest.schemas?.exports?.[0]) {
|
|
96
|
+
const regFile = path.join(root, 'src/dal/registry.ts');
|
|
97
|
+
if (fs.existsSync(regFile)) {
|
|
98
|
+
installed = fs.readFileSync(regFile, 'utf8').includes(manifest.schemas.exports[0]);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else if (manifest.menu?.name) {
|
|
102
|
+
const sidebarFile = path.join(root, 'src/components/layout/Sidebar.tsx');
|
|
103
|
+
if (fs.existsSync(sidebarFile)) {
|
|
104
|
+
installed = fs.readFileSync(sidebarFile, 'utf8').includes(manifest.menu.name);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
found.push({
|
|
108
|
+
name: dir,
|
|
109
|
+
source: `@mostajs/${dir}/`,
|
|
110
|
+
package: manifest.package,
|
|
111
|
+
version: manifest.version,
|
|
112
|
+
type: manifest.type || 'functional',
|
|
113
|
+
installed,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return Response.json({ data: found });
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
122
|
+
return Response.json({ error: { code: 'LIST_ERROR', message } }, { status: 500 });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return { GET, POST };
|
|
126
|
+
}
|
|
@@ -5,6 +5,8 @@ export interface ReconfigPanelProps {
|
|
|
5
5
|
detectEndpoint?: string;
|
|
6
6
|
/** API endpoint for JAR upload (default: '/api/setup/upload-jar') */
|
|
7
7
|
jarEndpoint?: string;
|
|
8
|
+
/** API endpoint for wire module install/uninstall (default: '/api/setup/wire-module') */
|
|
9
|
+
wireEndpoint?: string;
|
|
8
10
|
/** Translate function */
|
|
9
11
|
t?: (key: string) => string;
|
|
10
12
|
/** Called after successful DB change */
|
|
@@ -16,4 +18,4 @@ export interface ReconfigPanelProps {
|
|
|
16
18
|
/** Callback to run seed after DB change */
|
|
17
19
|
onSeedRequested?: () => Promise<void>;
|
|
18
20
|
}
|
|
19
|
-
export default function ReconfigPanel({ apiEndpoint, detectEndpoint, jarEndpoint, t, onDbChanged, onModulesChanged, showSeedOption, onSeedRequested, }: ReconfigPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
export default function ReconfigPanel({ apiEndpoint, detectEndpoint, jarEndpoint, wireEndpoint, t, onDbChanged, onModulesChanged, showSeedOption, onSeedRequested, }: ReconfigPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -98,6 +98,38 @@ const S = {
|
|
|
98
98
|
}),
|
|
99
99
|
checkbox: { marginRight: 8, width: 16, height: 16, cursor: 'pointer' },
|
|
100
100
|
checkboxLabel: { display: 'flex', alignItems: 'center', fontSize: 13, cursor: 'pointer', padding: '8px 0' },
|
|
101
|
+
toggleBtn: (installed, busy) => ({
|
|
102
|
+
padding: '6px 16px',
|
|
103
|
+
border: 'none',
|
|
104
|
+
borderRadius: 6,
|
|
105
|
+
fontSize: 12,
|
|
106
|
+
fontWeight: 700,
|
|
107
|
+
cursor: busy ? 'wait' : 'pointer',
|
|
108
|
+
opacity: busy ? 0.6 : 1,
|
|
109
|
+
backgroundColor: installed ? '#dc2626' : '#059669',
|
|
110
|
+
color: '#fff',
|
|
111
|
+
transition: 'all 0.2s',
|
|
112
|
+
minWidth: 90,
|
|
113
|
+
}),
|
|
114
|
+
wireCard: (installed) => ({
|
|
115
|
+
padding: 16,
|
|
116
|
+
border: `2px solid ${installed ? '#059669' : '#e5e7eb'}`,
|
|
117
|
+
borderRadius: 10,
|
|
118
|
+
backgroundColor: installed ? '#f0fdf4' : '#fafafa',
|
|
119
|
+
transition: 'all 0.2s',
|
|
120
|
+
display: 'flex',
|
|
121
|
+
flexDirection: 'column',
|
|
122
|
+
gap: 8,
|
|
123
|
+
}),
|
|
124
|
+
wireStatus: (installed) => ({
|
|
125
|
+
display: 'inline-block',
|
|
126
|
+
width: 10,
|
|
127
|
+
height: 10,
|
|
128
|
+
borderRadius: '50%',
|
|
129
|
+
backgroundColor: installed ? '#22c55e' : '#ef4444',
|
|
130
|
+
marginRight: 6,
|
|
131
|
+
flexShrink: 0,
|
|
132
|
+
}),
|
|
101
133
|
currentBadge: {
|
|
102
134
|
display: 'inline-block',
|
|
103
135
|
padding: '2px 8px',
|
|
@@ -110,7 +142,7 @@ const S = {
|
|
|
110
142
|
},
|
|
111
143
|
};
|
|
112
144
|
// ── Component ────────────────────────────────────────────────
|
|
113
|
-
export default function ReconfigPanel({ apiEndpoint = '/api/setup/reconfig', detectEndpoint = '/api/setup/detect-modules', jarEndpoint = '/api/setup/upload-jar', t = (k) => k, onDbChanged, onModulesChanged, showSeedOption = true, onSeedRequested, }) {
|
|
145
|
+
export default function ReconfigPanel({ apiEndpoint = '/api/setup/reconfig', detectEndpoint = '/api/setup/detect-modules', jarEndpoint = '/api/setup/upload-jar', wireEndpoint = '/api/setup/wire-module', t = (k) => k, onDbChanged, onModulesChanged, showSeedOption = true, onSeedRequested, }) {
|
|
114
146
|
// --- State ---
|
|
115
147
|
const [loading, setLoading] = useState(true);
|
|
116
148
|
const [currentDialect, setCurrentDialect] = useState('');
|
|
@@ -140,6 +172,10 @@ export default function ReconfigPanel({ apiEndpoint = '/api/setup/reconfig', det
|
|
|
140
172
|
// Module saving
|
|
141
173
|
const [moduleSaving, setModuleSaving] = useState(false);
|
|
142
174
|
const [moduleMessage, setModuleMessage] = useState(null);
|
|
175
|
+
const [wireModules, setWireModules] = useState([]);
|
|
176
|
+
const [wireLoading, setWireLoading] = useState(true);
|
|
177
|
+
const [wireBusy, setWireBusy] = useState(null);
|
|
178
|
+
const [wireMessage, setWireMessage] = useState(null);
|
|
143
179
|
// --- Load current config ---
|
|
144
180
|
const loadConfig = useCallback(async () => {
|
|
145
181
|
setLoading(true);
|
|
@@ -197,6 +233,57 @@ export default function ReconfigPanel({ apiEndpoint = '/api/setup/reconfig', det
|
|
|
197
233
|
}
|
|
198
234
|
}, [apiEndpoint, detectEndpoint, jarEndpoint]);
|
|
199
235
|
useEffect(() => { loadConfig(); }, [loadConfig]);
|
|
236
|
+
// --- Load wire modules ---
|
|
237
|
+
const loadWireModules = useCallback(async () => {
|
|
238
|
+
setWireLoading(true);
|
|
239
|
+
try {
|
|
240
|
+
const res = await fetch(wireEndpoint);
|
|
241
|
+
const data = await res.json();
|
|
242
|
+
if (data.data)
|
|
243
|
+
setWireModules(data.data);
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
// wire endpoint may not exist
|
|
247
|
+
}
|
|
248
|
+
finally {
|
|
249
|
+
setWireLoading(false);
|
|
250
|
+
}
|
|
251
|
+
}, [wireEndpoint]);
|
|
252
|
+
useEffect(() => { loadWireModules(); }, [loadWireModules]);
|
|
253
|
+
// --- Toggle wire module install/uninstall ---
|
|
254
|
+
const handleWireToggle = async (mod) => {
|
|
255
|
+
const action = mod.installed ? 'uninstall' : 'install';
|
|
256
|
+
setWireBusy(mod.name);
|
|
257
|
+
setWireMessage(null);
|
|
258
|
+
try {
|
|
259
|
+
const res = await fetch(wireEndpoint, {
|
|
260
|
+
method: 'POST',
|
|
261
|
+
headers: { 'Content-Type': 'application/json' },
|
|
262
|
+
body: JSON.stringify({ action, module: mod.name }),
|
|
263
|
+
});
|
|
264
|
+
const data = await res.json();
|
|
265
|
+
if (data.data?.ok) {
|
|
266
|
+
setWireMessage({
|
|
267
|
+
type: 'success',
|
|
268
|
+
text: action === 'install'
|
|
269
|
+
? `${mod.package} cable avec succes`
|
|
270
|
+
: `${mod.package} decable avec succes`,
|
|
271
|
+
module: mod.name,
|
|
272
|
+
});
|
|
273
|
+
await loadWireModules();
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
const errMsg = data.error?.message || data.data?.steps?.find((s) => s.status === 'error')?.detail || 'Erreur';
|
|
277
|
+
setWireMessage({ type: 'error', text: `${action} ${mod.package}: ${errMsg}`, module: mod.name });
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
setWireMessage({ type: 'error', text: 'Erreur reseau', module: mod.name });
|
|
282
|
+
}
|
|
283
|
+
finally {
|
|
284
|
+
setWireBusy(null);
|
|
285
|
+
}
|
|
286
|
+
};
|
|
200
287
|
// --- JAR upload ---
|
|
201
288
|
const handleJarUpload = async (e) => {
|
|
202
289
|
const file = e.target.files?.[0];
|
|
@@ -413,7 +500,16 @@ export default function ReconfigPanel({ apiEndpoint = '/api/setup/reconfig', det
|
|
|
413
500
|
}
|
|
414
501
|
const dialectInfo = DIALECTS.find((d) => d.key === selectedDialect);
|
|
415
502
|
const isSqlite = selectedDialect === 'sqlite';
|
|
416
|
-
return (_jsxs("div", { style: S.panel, children: [_jsxs("div", { style: S.section, children: [_jsxs("div", { style: S.sectionTitle, children: [_jsx("span", { children: "\uD83D\
|
|
503
|
+
return (_jsxs("div", { style: S.panel, children: [_jsxs("div", { style: S.section, children: [_jsxs("div", { style: S.sectionTitle, children: [_jsx("span", { children: "\uD83D\uDD0C" }), " Cablage des modules"] }), _jsx("div", { style: S.sectionDesc, children: "Installez ou desinstallez les modules @mostajs. Un module cable injecte ses schemas, routes API, pages, permissions et menus dans l'application hote. Les modules business (schemas + repos) sont marques." }), wireMessage && (_jsx("div", { style: S.alert(wireMessage.type), children: wireMessage.text })), wireLoading ? (_jsx("div", { style: { textAlign: 'center', padding: 20, color: '#6b7280' }, children: "Chargement des manifestes..." })) : wireModules.length === 0 ? (_jsx("div", { style: { textAlign: 'center', padding: 20, color: '#9ca3af' }, children: "Aucun manifeste de cablage trouve" })) : (_jsx("div", { style: S.grid, children: wireModules.map((mod) => (_jsxs("div", { style: S.wireCard(mod.installed), children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx("span", { style: S.wireStatus(mod.installed) }), _jsxs("span", { style: { fontWeight: 700, fontSize: 14 }, children: ["@mostajs/", mod.name] })] }), _jsx("span", { style: {
|
|
504
|
+
fontSize: 10,
|
|
505
|
+
fontWeight: 600,
|
|
506
|
+
padding: '2px 6px',
|
|
507
|
+
borderRadius: 4,
|
|
508
|
+
backgroundColor: mod.type === 'business' ? '#dbeafe' : '#f3e8ff',
|
|
509
|
+
color: mod.type === 'business' ? '#1e40af' : '#6b21a8',
|
|
510
|
+
}, children: mod.type })] }), _jsxs("div", { style: { fontSize: 12, color: '#6b7280', fontFamily: 'monospace' }, children: ["v", mod.version, " \u2014 ", mod.source] }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginTop: 4 }, children: [_jsx("span", { style: { fontSize: 12, fontWeight: 600, color: mod.installed ? '#059669' : '#6b7280' }, children: mod.installed ? 'Cable' : 'Non cable' }), _jsx("button", { style: S.toggleBtn(mod.installed, wireBusy === mod.name), onClick: () => handleWireToggle(mod), disabled: wireBusy !== null, children: wireBusy === mod.name
|
|
511
|
+
? (mod.installed ? 'Decablage...' : 'Cablage...')
|
|
512
|
+
: (mod.installed ? 'Desinstaller' : 'Installer') })] })] }, mod.name))) }))] }), _jsxs("div", { style: S.section, children: [_jsxs("div", { style: S.sectionTitle, children: [_jsx("span", { children: "\uD83D\uDCE6" }), " Modules actifs"] }), _jsx("div", { style: S.sectionDesc, children: "Activez ou desactivez les modules @mostajs. Les modules requis ne peuvent pas etre desactives. Seuls les modules installes (dans node_modules) peuvent etre actives." }), moduleMessage && (_jsx("div", { style: S.alert(moduleMessage.type), children: moduleMessage.text })), _jsx("div", { style: S.grid, children: allModules.map((mod) => {
|
|
417
513
|
const isInstalled = installedModules.includes(mod.key);
|
|
418
514
|
const isActive = activeModules.has(mod.key);
|
|
419
515
|
const isRequired = !!mod.required;
|
|
@@ -10,6 +10,9 @@ export interface SetupWizardProps {
|
|
|
10
10
|
installModules?: string;
|
|
11
11
|
install?: string;
|
|
12
12
|
uploadJar?: string;
|
|
13
|
+
wireModule?: string;
|
|
14
|
+
/** Seed endpoint — runs module seeds from the runtime registry */
|
|
15
|
+
seed?: string;
|
|
13
16
|
};
|
|
14
17
|
/** Default database name prefix (e.g. 'secuaccessdb') */
|
|
15
18
|
dbNamePrefix?: string;
|
|
@@ -167,6 +167,40 @@ const S = {
|
|
|
167
167
|
flex: (gap = 8) => ({ display: 'flex', alignItems: 'center', gap }),
|
|
168
168
|
flexBetween: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' },
|
|
169
169
|
flexWrap: { display: 'flex', flexWrap: 'wrap', gap: 8 },
|
|
170
|
+
// Wire module styles
|
|
171
|
+
wireGrid: { display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: 12, marginTop: 16 },
|
|
172
|
+
wireCard: (installed) => ({
|
|
173
|
+
padding: 16,
|
|
174
|
+
border: `2px solid ${installed ? '#059669' : '#e5e7eb'}`,
|
|
175
|
+
borderRadius: 10,
|
|
176
|
+
backgroundColor: installed ? '#f0fdf4' : '#fafafa',
|
|
177
|
+
transition: 'all 0.2s',
|
|
178
|
+
display: 'flex',
|
|
179
|
+
flexDirection: 'column',
|
|
180
|
+
gap: 8,
|
|
181
|
+
}),
|
|
182
|
+
wireStatus: (installed) => ({
|
|
183
|
+
display: 'inline-block',
|
|
184
|
+
width: 10,
|
|
185
|
+
height: 10,
|
|
186
|
+
borderRadius: '50%',
|
|
187
|
+
backgroundColor: installed ? '#22c55e' : '#ef4444',
|
|
188
|
+
marginRight: 6,
|
|
189
|
+
flexShrink: 0,
|
|
190
|
+
}),
|
|
191
|
+
toggleBtn: (installed, busy) => ({
|
|
192
|
+
padding: '6px 16px',
|
|
193
|
+
border: 'none',
|
|
194
|
+
borderRadius: 6,
|
|
195
|
+
fontSize: 12,
|
|
196
|
+
fontWeight: 700,
|
|
197
|
+
cursor: busy ? 'wait' : 'pointer',
|
|
198
|
+
opacity: busy ? 0.6 : 1,
|
|
199
|
+
backgroundColor: installed ? '#dc2626' : '#059669',
|
|
200
|
+
color: '#fff',
|
|
201
|
+
transition: 'all 0.2s',
|
|
202
|
+
minWidth: 90,
|
|
203
|
+
}),
|
|
170
204
|
};
|
|
171
205
|
// ── Helpers ──────────────────────────────────────────────────
|
|
172
206
|
function resolveModuleDeps(selected, all) {
|
|
@@ -318,6 +352,8 @@ export default function SetupWizard({ t: tProp, onComplete, endpoints = {}, dbNa
|
|
|
318
352
|
installModules: endpoints.installModules || '/api/setup/install-modules',
|
|
319
353
|
install: endpoints.install || '/api/setup/install',
|
|
320
354
|
uploadJar: endpoints.uploadJar || '/api/setup/upload-jar',
|
|
355
|
+
wireModule: endpoints.wireModule || '/api/setup/wire-module',
|
|
356
|
+
seed: endpoints.seed || '/api/setup/seed',
|
|
321
357
|
};
|
|
322
358
|
// --- State ---
|
|
323
359
|
const [currentStep, setCurrentStep] = useState(0);
|
|
@@ -335,6 +371,10 @@ export default function SetupWizard({ t: tProp, onComplete, endpoints = {}, dbNa
|
|
|
335
371
|
const [installing, setInstalling] = useState(false);
|
|
336
372
|
const [installResult, setInstallResult] = useState(null);
|
|
337
373
|
const [hydrated, setHydrated] = useState(false);
|
|
374
|
+
const [wireModules, setWireModules] = useState([]);
|
|
375
|
+
const [wireLoading, setWireLoading] = useState(false);
|
|
376
|
+
const [wireBusy, setWireBusy] = useState(null);
|
|
377
|
+
const [wireMessage, setWireMessage] = useState(null);
|
|
338
378
|
const step = STEPS[currentStep];
|
|
339
379
|
// --- Persist / Restore ---
|
|
340
380
|
useEffect(() => {
|
|
@@ -420,6 +460,59 @@ export default function SetupWizard({ t: tProp, onComplete, endpoints = {}, dbNa
|
|
|
420
460
|
}
|
|
421
461
|
});
|
|
422
462
|
}, [availableModules]);
|
|
463
|
+
// --- Wire modules (load after installation success) ---
|
|
464
|
+
const loadWireModules = useCallback(async () => {
|
|
465
|
+
setWireLoading(true);
|
|
466
|
+
try {
|
|
467
|
+
const res = await fetch(ep.wireModule);
|
|
468
|
+
const data = await res.json();
|
|
469
|
+
if (data.data)
|
|
470
|
+
setWireModules(data.data);
|
|
471
|
+
}
|
|
472
|
+
catch {
|
|
473
|
+
// wire endpoint may not exist
|
|
474
|
+
}
|
|
475
|
+
finally {
|
|
476
|
+
setWireLoading(false);
|
|
477
|
+
}
|
|
478
|
+
}, [ep.wireModule]);
|
|
479
|
+
// Auto-load wire modules on modules step and after install
|
|
480
|
+
useEffect(() => {
|
|
481
|
+
if (step === 'modules' || installResult?.ok)
|
|
482
|
+
loadWireModules();
|
|
483
|
+
}, [step, installResult?.ok, loadWireModules]);
|
|
484
|
+
const handleWireToggle = async (mod) => {
|
|
485
|
+
const action = mod.installed ? 'uninstall' : 'install';
|
|
486
|
+
setWireBusy(mod.name);
|
|
487
|
+
setWireMessage(null);
|
|
488
|
+
try {
|
|
489
|
+
const res = await fetch(ep.wireModule, {
|
|
490
|
+
method: 'POST',
|
|
491
|
+
headers: { 'Content-Type': 'application/json' },
|
|
492
|
+
body: JSON.stringify({ action, module: mod.name }),
|
|
493
|
+
});
|
|
494
|
+
const data = await res.json();
|
|
495
|
+
if (data.data?.ok) {
|
|
496
|
+
setWireMessage({
|
|
497
|
+
type: 'success',
|
|
498
|
+
text: action === 'install'
|
|
499
|
+
? `${mod.package} cable avec succes`
|
|
500
|
+
: `${mod.package} decable avec succes`,
|
|
501
|
+
});
|
|
502
|
+
await loadWireModules();
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
const errMsg = data.error?.message || 'Erreur';
|
|
506
|
+
setWireMessage({ type: 'error', text: `${action} ${mod.package}: ${errMsg}` });
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
catch {
|
|
510
|
+
setWireMessage({ type: 'error', text: 'Erreur reseau' });
|
|
511
|
+
}
|
|
512
|
+
finally {
|
|
513
|
+
setWireBusy(null);
|
|
514
|
+
}
|
|
515
|
+
};
|
|
423
516
|
// --- Dialect select ---
|
|
424
517
|
function selectDialect(d) {
|
|
425
518
|
setDialect(d);
|
|
@@ -534,7 +627,13 @@ export default function SetupWizard({ t: tProp, onComplete, endpoints = {}, dbNa
|
|
|
534
627
|
const isSelected = selectedModules.includes(mod.key);
|
|
535
628
|
const isDetected = detectedModules.includes(mod.key);
|
|
536
629
|
return (_jsxs("div", { style: S.moduleCard(isSelected, !!mod.required), onClick: () => toggleModule(mod.key), children: [_jsxs("div", { style: S.moduleHeader, children: [_jsxs("div", { style: S.moduleLeft, children: [_jsx("span", { style: { fontSize: 20 }, children: mod.icon }), _jsx("span", { style: S.moduleName, children: mod.label })] }), _jsxs("div", { style: S.moduleBadges, children: [mod.discovered && _jsx("span", { style: S.badge('new'), children: "Nouveau" }), isDetected && _jsx("span", { style: S.badge('installed'), children: t('setup.modules.installed') }), mod.required && _jsx("span", { style: S.badge('required'), children: t('setup.modules.required') }), _jsx("input", { type: "checkbox", checked: isSelected, disabled: mod.required, readOnly: true, style: S.checkbox })] })] }), _jsx("div", { style: S.moduleDesc, children: mod.description }), mod.dependsOn?.length ? (_jsxs("div", { style: { fontSize: 11, color: '#9ca3af', marginTop: 4 }, children: ["Depend de : ", mod.dependsOn.join(', ')] })) : null] }, mod.key));
|
|
537
|
-
}) }), _jsxs("div", { style:
|
|
630
|
+
}) }), _jsxs("div", { style: { marginTop: 28, paddingTop: 20, borderTop: '1px solid #e5e7eb' }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }, children: [_jsx("span", { style: { fontSize: 18 }, children: "\uD83D\uDD0C" }), _jsxs("div", { children: [_jsx("div", { style: { fontSize: 16, fontWeight: 700, color: '#111827' }, children: "Cablage des modules" }), _jsx("div", { style: { fontSize: 12, color: '#6b7280' }, children: "Cablez les modules pour injecter schemas, routes API, pages et permissions." })] })] }), wireMessage && (_jsx("div", { style: S.alert(wireMessage.type), children: wireMessage.text })), wireLoading ? (_jsx("div", { style: { textAlign: 'center', padding: 12, color: '#6b7280' }, children: "Chargement des manifestes..." })) : wireModules.length === 0 ? (_jsx("div", { style: { textAlign: 'center', padding: 12, color: '#9ca3af', fontSize: 13 }, children: "Aucun manifeste de cablage trouve. Les manifestes seront disponibles apres l'installation." })) : (_jsx("div", { style: S.wireGrid, children: wireModules.map((mod) => (_jsxs("div", { style: S.wireCard(mod.installed), children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx("span", { style: S.wireStatus(mod.installed) }), _jsxs("span", { style: { fontWeight: 700, fontSize: 14 }, children: ["@mostajs/", mod.name] })] }), _jsx("span", { style: {
|
|
631
|
+
fontSize: 10, fontWeight: 600, padding: '2px 6px', borderRadius: 4,
|
|
632
|
+
backgroundColor: mod.type === 'business' ? '#dbeafe' : '#f3e8ff',
|
|
633
|
+
color: mod.type === 'business' ? '#1e40af' : '#6b21a8',
|
|
634
|
+
}, children: mod.type })] }), _jsxs("div", { style: { fontSize: 12, color: '#6b7280', fontFamily: 'monospace' }, children: ["v", mod.version] }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginTop: 4 }, children: [_jsx("span", { style: { fontSize: 12, fontWeight: 600, color: mod.installed ? '#059669' : '#6b7280' }, children: mod.installed ? 'ON' : 'OFF' }), _jsx("button", { style: S.toggleBtn(mod.installed, wireBusy === mod.name), onClick: (e) => { e.stopPropagation(); handleWireToggle(mod); }, disabled: wireBusy !== null, children: wireBusy === mod.name
|
|
635
|
+
? (mod.installed ? 'Decablage...' : 'Cablage...')
|
|
636
|
+
: (mod.installed ? 'Desinstaller' : 'Installer') })] })] }, mod.name))) }))] }), _jsxs("div", { style: S.navRow, children: [_jsxs("button", { style: S.btn('outline'), onClick: goBack, children: ["\u2190 ", t('setup.back')] }), _jsxs("button", { style: S.btn('primary', !canGoNext()), onClick: goNext, disabled: !canGoNext(), children: [t('setup.next'), " \u2192"] })] })] })), step === 'dialect' && (_jsxs("div", { children: [_jsxs("div", { style: S.sectionHeader, children: [_jsx("span", { style: S.sectionIcon, children: "\uD83D\uDCBE" }), _jsxs("div", { children: [_jsx("div", { style: S.sectionTitle, children: t('setup.dialect.title') }), _jsx("div", { style: S.sectionDesc, children: t('setup.dialect.description') })] })] }), _jsx("div", { style: S.grid3, children: DIALECT_INFO.map(d => (_jsxs("div", { style: S.dialectCard(dialect === d.key, !!d.premium), onClick: () => !d.premium && selectDialect(d.key), title: d.premium ? `${d.name} — disponible en version Premium` : d.name, children: [_jsx("div", { style: S.dialectIcon, children: d.icon }), _jsx("div", { style: S.dialectName, children: d.name }), d.premium && (_jsx("span", { style: { ...S.badge('premium'), marginLeft: 0, marginTop: 4 }, children: "Premium" }))] }, d.key))) }), _jsxs("div", { style: S.navRow, children: [_jsxs("button", { style: S.btn('outline'), onClick: goBack, children: ["\u2190 ", t('setup.back')] }), _jsxs("button", { style: S.btn('primary'), onClick: goNext, children: [t('setup.next'), " \u2192"] })] })] })), step === 'database' && (_jsxs("div", { children: [_jsxs("div", { style: S.sectionHeader, children: [_jsx("span", { style: S.sectionIcon, children: "\uD83D\uDDC4\uFE0F" }), _jsxs("div", { children: [_jsx("div", { style: S.sectionTitle, children: t('setup.database.title') }), _jsx("div", { style: S.sectionDesc, children: t('setup.database.description') })] })] }), dialect === 'sqlite' ? (_jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.database.name') }), _jsx("input", { style: S.input, value: dbConfig.name, onChange: e => { setDbConfig({ ...dbConfig, name: e.target.value }); setDbTestResult(null); }, placeholder: dbNamePrefix }), _jsxs("p", { style: { fontSize: 11, color: '#9ca3af', marginTop: 4 }, children: [t('setup.database.sqliteInfo'), " ", _jsxs("code", { style: { fontFamily: 'monospace', backgroundColor: '#f3f4f6', padding: '1px 4px', borderRadius: 3 }, children: ["./data/", dbConfig.name, ".db"] })] })] })) : dialect === 'spanner' ? (_jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.database.spannerPath') }), _jsx("input", { style: S.input, value: dbConfig.name, onChange: e => { setDbConfig({ ...dbConfig, name: e.target.value }); setDbTestResult(null); }, placeholder: "my-project/my-instance/mydb" }), _jsx("p", { style: { fontSize: 11, color: '#9ca3af', marginTop: 4 }, children: t('setup.database.spannerInfo') })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { style: S.formRow, children: [_jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.database.host') }), _jsx("input", { style: S.input, value: dbConfig.host, onChange: e => { setDbConfig({ ...dbConfig, host: e.target.value }); setDbTestResult(null); } })] }), _jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.database.port') }), _jsx("input", { style: S.input, type: "number", value: dbConfig.port, onChange: e => { setDbConfig({ ...dbConfig, port: parseInt(e.target.value) || 0 }); setDbTestResult(null); } })] })] }), _jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.database.name') }), _jsx("input", { style: S.input, value: dbConfig.name, onChange: e => { setDbConfig({ ...dbConfig, name: e.target.value }); setDbTestResult(null); } })] }), _jsxs("div", { style: S.formRow, children: [_jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.database.user') }), _jsx("input", { style: S.input, value: dbConfig.user, onChange: e => { setDbConfig({ ...dbConfig, user: e.target.value }); setDbTestResult(null); }, placeholder: dialect === 'hsqldb' ? 'SA' : '' })] }), _jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.database.password') }), _jsx("input", { style: S.input, type: "password", value: dbConfig.password, onChange: e => { setDbConfig({ ...dbConfig, password: e.target.value }); setDbTestResult(null); } })] })] })] })), dialect === 'hsqldb' && (_jsxs("div", { style: { ...S.alert('warning'), marginTop: 12, fontSize: 12 }, children: [_jsx("strong", { children: "Prerequis :" }), " Le serveur HSQLDB doit etre lance avant le bridge.", _jsx("br", {}), _jsxs("code", { style: { fontFamily: 'monospace', backgroundColor: '#fef3c7', padding: '2px 6px', borderRadius: 3, display: 'inline-block', marginTop: 4, fontSize: 11 }, children: ["java -cp hsqldb*.jar org.hsqldb.server.Server --database.0 file:./data/", dbConfig.name, " --dbname.0 ", dbConfig.name] })] })), dialect !== 'mongodb' && dialect !== 'sqlite' && (_jsxs("div", { style: { ...S.alert('warning'), marginTop: 12 }, children: [t('setup.database.driverHint'), ' ', _jsx("code", { style: { fontFamily: 'monospace', backgroundColor: '#fef3c7', padding: '1px 4px', borderRadius: 3 }, children: DRIVER_HINTS[dialect] })] })), JDBC_DIALECTS.includes(dialect) && (_jsx(JarUploadInline, { dialect: dialect, jarEndpoint: ep.uploadJar, dbConfig: dbConfig })), dialect !== 'sqlite' && dialect !== 'spanner' && (_jsxs("div", { style: { ...S.checkRow, marginTop: 12, padding: '10px 14px', backgroundColor: '#fffbeb', border: '1px solid #fde68a', borderRadius: 8 }, children: [_jsx("input", { type: "checkbox", style: S.checkbox, checked: createIfNotExists, onChange: e => setCreateIfNotExists(e.target.checked) }), _jsxs("div", { children: [_jsx("div", { style: { fontSize: 13, fontWeight: 500 }, children: t('setup.database.createIfNotExists') }), _jsx("div", { style: { fontSize: 12, color: '#92400e' }, children: t('setup.database.createIfNotExistsDesc') })] })] })), dialect !== 'sqlite' && dialect !== 'spanner' && (_jsxs("div", { style: { ...S.flex(16), marginTop: 16 }, children: [_jsxs("button", { style: S.btn('outline', dbTesting), onClick: testDb, disabled: dbTesting, children: [dbTesting ? '⏳ ' : '🗄️ ', dbTesting ? t('setup.database.testing') : t('setup.database.test')] }), dbTestResult && (_jsx("span", { style: { fontSize: 13, color: dbTestResult.ok ? '#059669' : '#dc2626' }, children: dbTestResult.ok
|
|
538
637
|
? `✅ ${t('setup.database.success')}${dbTestResult.dbVersion ? ` (v${dbTestResult.dbVersion})` : ''}`
|
|
539
638
|
: `❌ ${t('setup.database.error')}: ${dbTestResult.error}` }))] })), _jsxs("div", { style: S.navRow, children: [_jsxs("button", { style: S.btn('outline'), onClick: goBack, children: ["\u2190 ", t('setup.back')] }), _jsxs("button", { style: S.btn('primary', !canGoNext()), onClick: goNext, disabled: !canGoNext(), children: [t('setup.next'), " \u2192"] })] })] })), step === 'admin' && (_jsxs("div", { children: [_jsxs("div", { style: S.sectionHeader, children: [_jsx("span", { style: S.sectionIcon, children: "\uD83D\uDC64" }), _jsxs("div", { children: [_jsx("div", { style: S.sectionTitle, children: t('setup.admin.title') }), _jsx("div", { style: S.sectionDesc, children: t('setup.admin.description') })] })] }), _jsxs("div", { style: S.formRow, children: [_jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.admin.firstName') }), _jsx("input", { style: S.input, value: adminConfig.firstName, onChange: e => setAdminConfig({ ...adminConfig, firstName: e.target.value }) })] }), _jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.admin.lastName') }), _jsx("input", { style: S.input, value: adminConfig.lastName, onChange: e => setAdminConfig({ ...adminConfig, lastName: e.target.value }) })] })] }), _jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.admin.email') }), _jsx("input", { style: S.input, type: "email", value: adminConfig.email, onChange: e => setAdminConfig({ ...adminConfig, email: e.target.value }), placeholder: "admin@example.com" })] }), _jsxs("div", { style: S.formRow, children: [_jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.admin.password') }), _jsx("input", { style: S.input, type: "password", value: adminConfig.password, onChange: e => setAdminConfig({ ...adminConfig, password: e.target.value }) })] }), _jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.admin.confirmPassword') }), _jsx("input", { style: S.input, type: "password", value: adminConfig.confirmPassword, onChange: e => setAdminConfig({ ...adminConfig, confirmPassword: e.target.value }) })] })] }), adminConfig.password && adminConfig.confirmPassword && adminConfig.password !== adminConfig.confirmPassword && (_jsx("p", { style: { fontSize: 13, color: '#dc2626' }, children: t('setup.admin.passwordMismatch') })), _jsxs("div", { style: S.navRow, children: [_jsxs("button", { style: S.btn('outline'), onClick: goBack, children: ["\u2190 ", t('setup.back')] }), _jsxs("button", { style: S.btn('primary', !canGoNext()), onClick: goNext, disabled: !canGoNext(), children: [t('setup.next'), " \u2192"] })] })] })), step === 'summary' && (_jsxs("div", { children: [_jsxs("div", { style: S.sectionHeader, children: [_jsx("span", { style: S.sectionIcon, children: "\u2699\uFE0F" }), _jsxs("div", { children: [_jsx("div", { style: S.sectionTitle, children: t('setup.summary.title') }), _jsx("div", { style: S.sectionDesc, children: t('setup.summary.description') })] })] }), _jsxs("div", { style: S.summaryCard, children: [_jsx("div", { style: S.summaryTitle, children: t('setup.summary.dbConfig') }), _jsxs("div", { style: S.summaryText, children: [_jsx("span", { style: { fontFamily: 'monospace' }, children: dbSummaryLabel() }), dialect !== 'sqlite' && dbConfig.user && _jsxs("span", { style: { display: 'block', marginTop: 4 }, children: ["Utilisateur: ", dbConfig.user] })] })] }), _jsxs("div", { style: S.summaryCard, children: [_jsx("div", { style: S.summaryTitle, children: t('setup.summary.adminConfig') }), _jsxs("div", { style: S.summaryText, children: [_jsxs("div", { children: [adminConfig.firstName, " ", adminConfig.lastName] }), _jsx("div", { children: adminConfig.email })] })] }), _jsxs("div", { style: S.summaryCard, children: [_jsx("div", { style: S.summaryTitle, children: t('setup.modules.title') }), _jsx("div", { style: S.flexWrap, children: selectedModules.map(key => {
|
|
540
639
|
const mod = availableModules.find(m => m.key === key);
|
|
@@ -543,5 +642,11 @@ export default function SetupWizard({ t: tProp, onComplete, endpoints = {}, dbNa
|
|
|
543
642
|
fontSize: 12, backgroundColor: '#f0f9ff', color: '#0369a1',
|
|
544
643
|
border: '1px solid #bae6fd', borderRadius: 16, padding: '4px 10px',
|
|
545
644
|
}, children: [mod.icon, " ", mod.label] }, key)) : null;
|
|
546
|
-
}) })] }), _jsxs("div", { style: S.summaryCard, children: [_jsx("div", { style: S.summaryTitle, children: t('setup.summary.seedTitle') }), _jsx("p", { style: { ...S.summaryText, marginBottom: 12 }, children: t('setup.summary.seedInfo') }), _jsxs("div", { style: S.checkRow, children: [_jsx("input", { type: "checkbox", style: S.checkbox, checked: seedOptions.activities, onChange: e => setSeedOptions({ ...seedOptions, activities: e.target.checked, demoData: e.target.checked ? seedOptions.demoData : false }), disabled: installing || !!installResult?.ok }), _jsxs("div", { children: [_jsx("div", { style: { fontSize: 13, fontWeight: 500 }, children: t('setup.summary.seedActivities') }), _jsx("div", { style: { fontSize: 12, color: '#9ca3af' }, children: t('setup.summary.seedActivitiesDesc') })] })] }), _jsxs("div", { style: S.checkRow, children: [_jsx("input", { type: "checkbox", style: S.checkbox, checked: seedOptions.demoUsers, onChange: e => setSeedOptions({ ...seedOptions, demoUsers: e.target.checked }), disabled: installing || !!installResult?.ok }), _jsxs("div", { children: [_jsx("div", { style: { fontSize: 13, fontWeight: 500 }, children: t('setup.summary.seedDemoUsers') }), _jsx("div", { style: { fontSize: 12, color: '#9ca3af' }, children: t('setup.summary.seedDemoUsersDesc') })] })] }), _jsxs("div", { style: S.checkRow, children: [_jsx("input", { type: "checkbox", style: S.checkbox, checked: seedOptions.demoData, onChange: e => setSeedOptions({ ...seedOptions, demoData: e.target.checked, activities: e.target.checked ? true : seedOptions.activities }), disabled: installing || !!installResult?.ok }), _jsxs("div", { children: [_jsx("div", { style: { fontSize: 13, fontWeight: 500 }, children: t('setup.summary.seedDemoData') }), _jsx("div", { style: { fontSize: 12, color: '#9ca3af' }, children: t('setup.summary.seedDemoDataDesc') })] })] })] }), installResult && (_jsx("div", { style: S.alert(installResult.ok ? 'success' : 'error'), children: installResult.ok ? (_jsxs(_Fragment, { children: [_jsxs("div", { style: { fontWeight: 600, marginBottom: 4 }, children: ["\u2705 ", t('setup.summary.success')] }), _jsx("div", { children: t('setup.summary.successDesc') }), installResult.needsRestart && (_jsx("div", { style: { color: '#92400e', fontWeight: 500, marginTop: 8 }, children: t('setup.summary.needsRestart') }))] })) : (_jsxs("div", { children: ["\u274C ", installResult.error] })) })),
|
|
645
|
+
}) })] }), _jsxs("div", { style: S.summaryCard, children: [_jsx("div", { style: S.summaryTitle, children: t('setup.summary.seedTitle') }), _jsx("p", { style: { ...S.summaryText, marginBottom: 12 }, children: t('setup.summary.seedInfo') }), _jsxs("div", { style: S.checkRow, children: [_jsx("input", { type: "checkbox", style: S.checkbox, checked: seedOptions.activities, onChange: e => setSeedOptions({ ...seedOptions, activities: e.target.checked, demoData: e.target.checked ? seedOptions.demoData : false }), disabled: installing || !!installResult?.ok }), _jsxs("div", { children: [_jsx("div", { style: { fontSize: 13, fontWeight: 500 }, children: t('setup.summary.seedActivities') }), _jsx("div", { style: { fontSize: 12, color: '#9ca3af' }, children: t('setup.summary.seedActivitiesDesc') })] })] }), _jsxs("div", { style: S.checkRow, children: [_jsx("input", { type: "checkbox", style: S.checkbox, checked: seedOptions.demoUsers, onChange: e => setSeedOptions({ ...seedOptions, demoUsers: e.target.checked }), disabled: installing || !!installResult?.ok }), _jsxs("div", { children: [_jsx("div", { style: { fontSize: 13, fontWeight: 500 }, children: t('setup.summary.seedDemoUsers') }), _jsx("div", { style: { fontSize: 12, color: '#9ca3af' }, children: t('setup.summary.seedDemoUsersDesc') })] })] }), _jsxs("div", { style: S.checkRow, children: [_jsx("input", { type: "checkbox", style: S.checkbox, checked: seedOptions.demoData, onChange: e => setSeedOptions({ ...seedOptions, demoData: e.target.checked, activities: e.target.checked ? true : seedOptions.activities }), disabled: installing || !!installResult?.ok }), _jsxs("div", { children: [_jsx("div", { style: { fontSize: 13, fontWeight: 500 }, children: t('setup.summary.seedDemoData') }), _jsx("div", { style: { fontSize: 12, color: '#9ca3af' }, children: t('setup.summary.seedDemoDataDesc') })] })] })] }), installResult && (_jsx("div", { style: S.alert(installResult.ok ? 'success' : 'error'), children: installResult.ok ? (_jsxs(_Fragment, { children: [_jsxs("div", { style: { fontWeight: 600, marginBottom: 4 }, children: ["\u2705 ", t('setup.summary.success')] }), _jsx("div", { children: t('setup.summary.successDesc') }), installResult.needsRestart && (_jsx("div", { style: { color: '#92400e', fontWeight: 500, marginTop: 8 }, children: t('setup.summary.needsRestart') }))] })) : (_jsxs("div", { children: ["\u274C ", installResult.error] })) })), installResult?.ok && (_jsxs("div", { style: { ...S.summaryCard, marginTop: 16 }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }, children: [_jsx("span", { style: { fontSize: 18 }, children: "\uD83D\uDD0C" }), _jsx("div", { style: S.summaryTitle, children: "Cablage des modules" })] }), _jsx("div", { style: { fontSize: 13, color: '#6b7280', marginBottom: 12 }, children: "Cablez les modules pour injecter schemas, routes API, pages et permissions dans l'application." }), wireMessage && (_jsx("div", { style: S.alert(wireMessage.type), children: wireMessage.text })), wireLoading ? (_jsx("div", { style: { textAlign: 'center', padding: 12, color: '#6b7280' }, children: "Chargement..." })) : wireModules.length === 0 ? (_jsx("div", { style: { textAlign: 'center', padding: 12, color: '#9ca3af' }, children: "Aucun manifeste de cablage trouve" })) : (_jsx("div", { style: S.wireGrid, children: wireModules.map((mod) => (_jsxs("div", { style: S.wireCard(mod.installed), children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx("span", { style: S.wireStatus(mod.installed) }), _jsxs("span", { style: { fontWeight: 700, fontSize: 14 }, children: ["@mostajs/", mod.name] })] }), _jsx("span", { style: {
|
|
646
|
+
fontSize: 10, fontWeight: 600, padding: '2px 6px', borderRadius: 4,
|
|
647
|
+
backgroundColor: mod.type === 'business' ? '#dbeafe' : '#f3e8ff',
|
|
648
|
+
color: mod.type === 'business' ? '#1e40af' : '#6b21a8',
|
|
649
|
+
}, children: mod.type })] }), _jsxs("div", { style: { fontSize: 12, color: '#6b7280', fontFamily: 'monospace' }, children: ["v", mod.version, " \u2014 ", mod.source] }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginTop: 4 }, children: [_jsx("span", { style: { fontSize: 12, fontWeight: 600, color: mod.installed ? '#059669' : '#6b7280' }, children: mod.installed ? 'Cable' : 'Non cable' }), _jsx("button", { style: S.toggleBtn(mod.installed, wireBusy === mod.name), onClick: () => handleWireToggle(mod), disabled: wireBusy !== null, children: wireBusy === mod.name
|
|
650
|
+
? (mod.installed ? 'Decablage...' : 'Cablage...')
|
|
651
|
+
: (mod.installed ? 'Desinstaller' : 'Installer') })] })] }, mod.name))) }))] })), _jsx("div", { style: S.navRow, children: !installResult?.ok ? (_jsxs(_Fragment, { children: [_jsxs("button", { style: S.btn('outline', installing), onClick: goBack, disabled: installing, children: ["\u2190 ", t('setup.back')] }), _jsxs("button", { style: S.btn('primary', installing), onClick: runInstallation, disabled: installing, children: [installing ? '⏳ ' : '⚙️ ', installing ? t('setup.summary.installing') : t('setup.summary.install')] })] })) : (_jsx("div", { style: { width: '100%', textAlign: 'center' }, children: _jsxs("button", { style: S.btn('lg'), onClick: handleComplete, children: [t('setup.summary.goToLogin'), " \u2192"] }) })) })] }))] })] }) }));
|
|
547
652
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export { createDetectModulesHandler } from './api/detect-modules.route';
|
|
|
12
12
|
export { createInstallModulesHandler } from './api/install-modules.route';
|
|
13
13
|
export { createReconfigHandlers } from './api/reconfig.route';
|
|
14
14
|
export { createUploadJarHandlers } from './api/upload-jar.route';
|
|
15
|
+
export { createWireModuleHandler } from './api/wire-module.route';
|
|
15
16
|
export { default as ReconfigPanel } from './components/ReconfigPanel';
|
|
16
17
|
export { default as SetupWizard } from './components/SetupWizard';
|
|
17
18
|
export { setupMenuContribution } from './lib/menu';
|
package/dist/index.js
CHANGED
|
@@ -18,6 +18,7 @@ export { createDetectModulesHandler } from './api/detect-modules.route';
|
|
|
18
18
|
export { createInstallModulesHandler } from './api/install-modules.route';
|
|
19
19
|
export { createReconfigHandlers } from './api/reconfig.route';
|
|
20
20
|
export { createUploadJarHandlers } from './api/upload-jar.route';
|
|
21
|
+
export { createWireModuleHandler } from './api/wire-module.route';
|
|
21
22
|
// Components
|
|
22
23
|
export { default as ReconfigPanel } from './components/ReconfigPanel';
|
|
23
24
|
export { default as SetupWizard } from './components/SetupWizard';
|
package/dist/lib/setup.js
CHANGED
|
@@ -79,8 +79,12 @@ export async function runInstall(installConfig, setupConfig) {
|
|
|
79
79
|
});
|
|
80
80
|
seeded.push('admin');
|
|
81
81
|
}
|
|
82
|
-
// 6. Optional seeds
|
|
83
|
-
if (setupConfig.
|
|
82
|
+
// 6. Optional seeds (runtime registry or legacy)
|
|
83
|
+
if (setupConfig.runModuleSeeds) {
|
|
84
|
+
await setupConfig.runModuleSeeds(installConfig.modules);
|
|
85
|
+
seeded.push('module-seeds');
|
|
86
|
+
}
|
|
87
|
+
else if (setupConfig.optionalSeeds && installConfig.seed) {
|
|
84
88
|
for (const seedDef of setupConfig.optionalSeeds) {
|
|
85
89
|
if (installConfig.seed[seedDef.key]) {
|
|
86
90
|
await seedDef.run({});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ModuleRegistration } from '@mostajs/socle';
|
|
2
|
+
/**
|
|
3
|
+
* Setup provides the installation wizard and reconfiguration panel.
|
|
4
|
+
* No schemas of its own — orchestrates other modules' seeds.
|
|
5
|
+
*/
|
|
6
|
+
export declare function register(registry: {
|
|
7
|
+
register(r: ModuleRegistration): void;
|
|
8
|
+
}): void;
|
package/dist/register.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// @mostajs/setup — Runtime module registration
|
|
2
|
+
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
3
|
+
import { setupMenuContribution } from './lib/menu.js';
|
|
4
|
+
/**
|
|
5
|
+
* Setup provides the installation wizard and reconfiguration panel.
|
|
6
|
+
* No schemas of its own — orchestrates other modules' seeds.
|
|
7
|
+
*/
|
|
8
|
+
export function register(registry) {
|
|
9
|
+
registry.register({
|
|
10
|
+
manifest: {
|
|
11
|
+
name: 'setup',
|
|
12
|
+
package: '@mostajs/setup',
|
|
13
|
+
version: '2.0.0',
|
|
14
|
+
type: 'core',
|
|
15
|
+
priority: 10,
|
|
16
|
+
dependencies: ['auth', 'settings'],
|
|
17
|
+
displayName: 'Setup',
|
|
18
|
+
description: 'Installation wizard — DB config, module selection, admin creation, seeding',
|
|
19
|
+
icon: 'Wrench',
|
|
20
|
+
register: './dist/register.js',
|
|
21
|
+
},
|
|
22
|
+
menu: setupMenuContribution,
|
|
23
|
+
});
|
|
24
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -54,8 +54,10 @@ export interface MostaSetupConfig {
|
|
|
54
54
|
firstName: string;
|
|
55
55
|
lastName: string;
|
|
56
56
|
}) => Promise<void>;
|
|
57
|
-
/** Optional seeds shown in the wizard */
|
|
57
|
+
/** Optional seeds shown in the wizard (legacy — prefer runModuleSeeds) */
|
|
58
58
|
optionalSeeds?: SeedDefinition[];
|
|
59
|
+
/** Run seeds from the module registry (Phase 4 runtime) */
|
|
60
|
+
runModuleSeeds?: (modules?: string[]) => Promise<void>;
|
|
59
61
|
/** Extra env vars to write to .env.local */
|
|
60
62
|
extraEnvVars?: Record<string, string>;
|
|
61
63
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mostajs/setup",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.13",
|
|
4
4
|
"description": "Reusable setup wizard module — multi-dialect DB configuration, .env.local writer, seed runner",
|
|
5
5
|
"author": "Dr Hamid MADANI <drmdh@msn.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -57,10 +57,21 @@
|
|
|
57
57
|
"types": "./dist/api/upload-jar.route.d.ts",
|
|
58
58
|
"import": "./dist/api/upload-jar.route.js",
|
|
59
59
|
"default": "./dist/api/upload-jar.route.js"
|
|
60
|
+
},
|
|
61
|
+
"./api/wire-module": {
|
|
62
|
+
"types": "./dist/api/wire-module.route.d.ts",
|
|
63
|
+
"import": "./dist/api/wire-module.route.js",
|
|
64
|
+
"default": "./dist/api/wire-module.route.js"
|
|
65
|
+
},
|
|
66
|
+
"./register": {
|
|
67
|
+
"types": "./dist/register.d.ts",
|
|
68
|
+
"import": "./dist/register.js",
|
|
69
|
+
"default": "./dist/register.js"
|
|
60
70
|
}
|
|
61
71
|
},
|
|
62
72
|
"files": [
|
|
63
73
|
"dist",
|
|
74
|
+
"wire.json",
|
|
64
75
|
"setup.wire.json",
|
|
65
76
|
"LICENSE",
|
|
66
77
|
"README.md"
|
|
@@ -92,6 +103,7 @@
|
|
|
92
103
|
},
|
|
93
104
|
"devDependencies": {
|
|
94
105
|
"@mostajs/menu": "^1.0.2",
|
|
106
|
+
"@mostajs/socle": "^2.0.0",
|
|
95
107
|
"@types/bcryptjs": "^2.4.0",
|
|
96
108
|
"@types/node": "^25.3.3",
|
|
97
109
|
"@types/react": "^19.2.14",
|
|
@@ -99,12 +111,16 @@
|
|
|
99
111
|
},
|
|
100
112
|
"peerDependencies": {
|
|
101
113
|
"@mostajs/menu": ">=1.0.2",
|
|
114
|
+
"@mostajs/socle": ">=2.0.0",
|
|
102
115
|
"react": ">=18.0.0"
|
|
103
116
|
},
|
|
104
117
|
"peerDependenciesMeta": {
|
|
105
118
|
"@mostajs/menu": {
|
|
106
119
|
"optional": true
|
|
107
120
|
},
|
|
121
|
+
"@mostajs/socle": {
|
|
122
|
+
"optional": true
|
|
123
|
+
},
|
|
108
124
|
"react": {
|
|
109
125
|
"optional": true
|
|
110
126
|
}
|
package/wire.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "setup",
|
|
3
|
+
"package": "@mostajs/setup",
|
|
4
|
+
"version": "2.0.0",
|
|
5
|
+
"type": "core",
|
|
6
|
+
"priority": 10,
|
|
7
|
+
"dependencies": ["auth", "settings"],
|
|
8
|
+
"displayName": "Setup",
|
|
9
|
+
"description": "Installation wizard — DB config, module selection, admin creation, seeding",
|
|
10
|
+
"icon": "Wrench",
|
|
11
|
+
"register": "./dist/register.js"
|
|
12
|
+
}
|