@y4wee/nupo 0.1.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/dist/App.d.ts +8 -0
- package/dist/App.js +109 -0
- package/dist/__tests__/checks.test.d.ts +1 -0
- package/dist/__tests__/checks.test.js +68 -0
- package/dist/__tests__/config.test.d.ts +1 -0
- package/dist/__tests__/config.test.js +61 -0
- package/dist/components/ConfirmExit.d.ts +9 -0
- package/dist/components/ConfirmExit.js +12 -0
- package/dist/components/ErrorPanel.d.ts +7 -0
- package/dist/components/ErrorPanel.js +12 -0
- package/dist/components/Header.d.ts +10 -0
- package/dist/components/Header.js +17 -0
- package/dist/components/LeftPanel.d.ts +9 -0
- package/dist/components/LeftPanel.js +31 -0
- package/dist/components/OptionsPanel.d.ts +11 -0
- package/dist/components/OptionsPanel.js +18 -0
- package/dist/components/PathInput.d.ts +10 -0
- package/dist/components/PathInput.js +133 -0
- package/dist/components/ProgressBar.d.ts +5 -0
- package/dist/components/ProgressBar.js +21 -0
- package/dist/components/StepsPanel.d.ts +8 -0
- package/dist/components/StepsPanel.js +24 -0
- package/dist/hooks/useConfig.d.ts +8 -0
- package/dist/hooks/useConfig.js +26 -0
- package/dist/hooks/useTerminalSize.d.ts +4 -0
- package/dist/hooks/useTerminalSize.js +18 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +169 -0
- package/dist/screens/ConfigScreen.d.ts +10 -0
- package/dist/screens/ConfigScreen.js +182 -0
- package/dist/screens/ConfigureServiceScreen.d.ts +11 -0
- package/dist/screens/ConfigureServiceScreen.js +499 -0
- package/dist/screens/HomeScreen.d.ts +14 -0
- package/dist/screens/HomeScreen.js +24 -0
- package/dist/screens/IdeScreen.d.ts +9 -0
- package/dist/screens/IdeScreen.js +101 -0
- package/dist/screens/InitScreen.d.ts +9 -0
- package/dist/screens/InitScreen.js +182 -0
- package/dist/screens/InstallVersionScreen.d.ts +10 -0
- package/dist/screens/InstallVersionScreen.js +495 -0
- package/dist/screens/OdooScreen.d.ts +13 -0
- package/dist/screens/OdooScreen.js +76 -0
- package/dist/screens/OdooServiceScreen.d.ts +10 -0
- package/dist/screens/OdooServiceScreen.js +51 -0
- package/dist/screens/StartServiceScreen.d.ts +12 -0
- package/dist/screens/StartServiceScreen.js +386 -0
- package/dist/screens/UpgradeVersionScreen.d.ts +9 -0
- package/dist/screens/UpgradeVersionScreen.js +259 -0
- package/dist/services/checks.d.ts +8 -0
- package/dist/services/checks.js +48 -0
- package/dist/services/config.d.ts +11 -0
- package/dist/services/config.js +146 -0
- package/dist/services/git.d.ts +35 -0
- package/dist/services/git.js +173 -0
- package/dist/services/ide.d.ts +10 -0
- package/dist/services/ide.js +126 -0
- package/dist/services/python.d.ts +14 -0
- package/dist/services/python.js +81 -0
- package/dist/services/system.d.ts +2 -0
- package/dist/services/system.js +22 -0
- package/dist/types/index.d.ts +82 -0
- package/dist/types/index.js +26 -0
- package/package.json +37 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
import { useStdout } from 'ink';
|
|
3
|
+
export function useTerminalSize() {
|
|
4
|
+
const { stdout } = useStdout();
|
|
5
|
+
const [columns, setColumns] = useState(stdout?.columns ?? 80);
|
|
6
|
+
const [rows, setRows] = useState(stdout?.rows ?? 24);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (!stdout)
|
|
9
|
+
return;
|
|
10
|
+
const handler = () => {
|
|
11
|
+
setColumns(stdout.columns);
|
|
12
|
+
setRows(stdout.rows);
|
|
13
|
+
};
|
|
14
|
+
stdout.on('resize', handler);
|
|
15
|
+
return () => { stdout.off('resize', handler); };
|
|
16
|
+
}, [stdout]);
|
|
17
|
+
return { columns, rows };
|
|
18
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { render } from 'ink';
|
|
4
|
+
import { App } from './App.js';
|
|
5
|
+
import { configExists, readConfig } from './services/config.js';
|
|
6
|
+
import { setupVsCode } from './services/ide.js';
|
|
7
|
+
// ── Help ─────────────────────────────────────────────────────────────────────
|
|
8
|
+
const rawArgs = process.argv.slice(2);
|
|
9
|
+
if (rawArgs[0] === '--help' || rawArgs[0] === '-h') {
|
|
10
|
+
process.stdout.write(`
|
|
11
|
+
nupO — gestionnaire d'environnements Odoo
|
|
12
|
+
|
|
13
|
+
USAGE
|
|
14
|
+
nupo Lance l'interface interactive
|
|
15
|
+
nupo start <service> [opts] Lance directement un service Odoo
|
|
16
|
+
nupo code <branche> Configure .vscode et ouvre VS Code
|
|
17
|
+
|
|
18
|
+
COMMANDES
|
|
19
|
+
start <service> Démarre le service nommé <service>
|
|
20
|
+
code <branche> Configure et ouvre la version Odoo dans VS Code
|
|
21
|
+
|
|
22
|
+
OPTIONS DE START
|
|
23
|
+
-d <base> Base de données (--database)
|
|
24
|
+
-u <module> Module à mettre à jour (--update)
|
|
25
|
+
-i <module> Module à installer (--init)
|
|
26
|
+
--stop-after-init Arrête Odoo après l'initialisation
|
|
27
|
+
--shell Lance en mode shell interactif
|
|
28
|
+
|
|
29
|
+
EXEMPLES
|
|
30
|
+
nupo
|
|
31
|
+
nupo start mon_service
|
|
32
|
+
nupo start mon_service -d ma_base -u mon_module
|
|
33
|
+
nupo start mon_service -d ma_base -i mon_module --stop-after-init
|
|
34
|
+
nupo start mon_service --shell
|
|
35
|
+
nupo code 18.0
|
|
36
|
+
nupo code 17.0
|
|
37
|
+
|
|
38
|
+
`);
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
41
|
+
// ── CLI argument parsing ──────────────────────────────────────────────────────
|
|
42
|
+
function parseCliArgs() {
|
|
43
|
+
const args = rawArgs;
|
|
44
|
+
if (args[0] !== 'start' || !args[1])
|
|
45
|
+
return null;
|
|
46
|
+
const result = { serviceName: args[1], stopAfterInit: false, shell: false };
|
|
47
|
+
for (let i = 2; i < args.length; i++) {
|
|
48
|
+
switch (args[i]) {
|
|
49
|
+
case '-d':
|
|
50
|
+
if (args[i + 1])
|
|
51
|
+
result.db = args[++i];
|
|
52
|
+
break;
|
|
53
|
+
case '-u':
|
|
54
|
+
if (args[i + 1])
|
|
55
|
+
result.module = args[++i];
|
|
56
|
+
break;
|
|
57
|
+
case '-i':
|
|
58
|
+
if (args[i + 1])
|
|
59
|
+
result.install = args[++i];
|
|
60
|
+
break;
|
|
61
|
+
case '--stop-after-init':
|
|
62
|
+
result.stopAfterInit = true;
|
|
63
|
+
break;
|
|
64
|
+
case '--shell':
|
|
65
|
+
result.shell = true;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
const startupArgs = parseCliArgs();
|
|
72
|
+
// ── nupo code <branch> ────────────────────────────────────────────────────────
|
|
73
|
+
if (rawArgs[0] === 'code') {
|
|
74
|
+
const branch = rawArgs[1];
|
|
75
|
+
if (!branch) {
|
|
76
|
+
process.stderr.write(`nupo: usage : nupo code <branche>\n`);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
const exists = await configExists();
|
|
80
|
+
if (!exists) {
|
|
81
|
+
process.stderr.write(`nupo: aucune configuration trouvée. Lancez "nupo" pour initialiser.\n`);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
const cfg = await readConfig();
|
|
85
|
+
if (!cfg.initiated) {
|
|
86
|
+
process.stderr.write(`nupo: nupo n'est pas initialisé. Lancez "nupo" pour configurer.\n`);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
const version = (cfg.odoo_versions ?? {})[branch];
|
|
90
|
+
if (!version) {
|
|
91
|
+
const names = Object.keys(cfg.odoo_versions ?? {});
|
|
92
|
+
const list = names.length > 0 ? names.join(', ') : 'aucune';
|
|
93
|
+
process.stderr.write(`nupo: version introuvable : "${branch}"\nVersions disponibles : ${list}\n`);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
const LABELS = {
|
|
97
|
+
vscode_dir: 'Dossier .vscode',
|
|
98
|
+
settings_json: 'settings.json ',
|
|
99
|
+
launch_json: 'launch.json ',
|
|
100
|
+
open_vscode: 'Ouvrir VS Code ',
|
|
101
|
+
};
|
|
102
|
+
const services = Object.values(cfg.odoo_services ?? {});
|
|
103
|
+
const ok = await setupVsCode(version, services, (id, status, detail) => {
|
|
104
|
+
const icon = status === 'running' ? '…' : status === 'success' ? '✓' : '✗';
|
|
105
|
+
const label = LABELS[id] ?? id;
|
|
106
|
+
const info = detail ? ` ${detail}` : '';
|
|
107
|
+
if (status !== 'running')
|
|
108
|
+
process.stdout.write(`${icon} ${label}${info}\n`);
|
|
109
|
+
});
|
|
110
|
+
process.exit(ok ? 0 : 1);
|
|
111
|
+
}
|
|
112
|
+
// ── Pre-flight validation (before alternate screen) ───────────────────────────
|
|
113
|
+
if (startupArgs) {
|
|
114
|
+
const exists = await configExists();
|
|
115
|
+
if (!exists) {
|
|
116
|
+
process.stderr.write(`nupo: aucune configuration trouvée. Lancez "nupo" pour initialiser.\n`);
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
const cfg = await readConfig();
|
|
120
|
+
if (!cfg.initiated) {
|
|
121
|
+
process.stderr.write(`nupo: nupo n'est pas initialisé. Lancez "nupo" pour configurer.\n`);
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
const services = cfg.odoo_services ?? {};
|
|
125
|
+
if (!services[startupArgs.serviceName]) {
|
|
126
|
+
const names = Object.keys(services);
|
|
127
|
+
const list = names.length > 0 ? names.join(', ') : 'aucun';
|
|
128
|
+
process.stderr.write(`nupo: service introuvable : "${startupArgs.serviceName}"\nServices disponibles : ${list}\n`);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// ── Alternate screen buffer ───────────────────────────────────────────────────
|
|
133
|
+
// Enter alternate screen + hide cursor before rendering anything
|
|
134
|
+
process.stdout.write('\x1B[?1049h\x1B[?25l');
|
|
135
|
+
let cleanedUp = false;
|
|
136
|
+
function cleanup() {
|
|
137
|
+
if (cleanedUp)
|
|
138
|
+
return;
|
|
139
|
+
cleanedUp = true;
|
|
140
|
+
// Restore main screen buffer + show cursor
|
|
141
|
+
process.stdout.write('\x1B[?1049l\x1B[?25h');
|
|
142
|
+
}
|
|
143
|
+
// Guarantee cleanup on every possible exit path
|
|
144
|
+
process.on('exit', cleanup);
|
|
145
|
+
process.on('SIGTERM', () => { cleanup(); process.exit(0); });
|
|
146
|
+
process.on('SIGINT', () => { cleanup(); process.exit(0); });
|
|
147
|
+
process.on('uncaughtException', err => {
|
|
148
|
+
cleanup();
|
|
149
|
+
process.stderr.write(`\nnupo: erreur non gérée : ${err.message}\n${err.stack ?? ''}\n`);
|
|
150
|
+
process.exit(1);
|
|
151
|
+
});
|
|
152
|
+
process.on('unhandledRejection', reason => {
|
|
153
|
+
cleanup();
|
|
154
|
+
process.stderr.write(`\nnupo: promesse rejetée : ${String(reason)}\n`);
|
|
155
|
+
process.exit(1);
|
|
156
|
+
});
|
|
157
|
+
// ── Render ────────────────────────────────────────────────────────────────────
|
|
158
|
+
let instance;
|
|
159
|
+
function handleExit() {
|
|
160
|
+
instance.clear();
|
|
161
|
+
instance.unmount();
|
|
162
|
+
cleanup();
|
|
163
|
+
}
|
|
164
|
+
instance = render(React.createElement(App, { onExit: handleExit, startupArgs: startupArgs ?? undefined }), {
|
|
165
|
+
exitOnCtrlC: false,
|
|
166
|
+
});
|
|
167
|
+
process.stdout.on('resize', () => {
|
|
168
|
+
process.stdout.write('\x1B[2J\x1B[H');
|
|
169
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { NupoConfig } from '../types/index.js';
|
|
3
|
+
interface ConfigScreenProps {
|
|
4
|
+
config: NupoConfig;
|
|
5
|
+
leftWidth: number;
|
|
6
|
+
onBack: () => void;
|
|
7
|
+
onSaved: () => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function ConfigScreen({ config, leftWidth, onBack, onSaved }: ConfigScreenProps): React.JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Box, Text, useInput } from 'ink';
|
|
3
|
+
import { PathInput } from '../components/PathInput.js';
|
|
4
|
+
import { access } from 'fs/promises';
|
|
5
|
+
import { getPrimaryColor, getSecondaryColor, getTextColor, getCursorColor } from '../types/index.js';
|
|
6
|
+
import { patchConfig, ensureBaseConf, getBaseConfPath } from '../services/config.js';
|
|
7
|
+
import { openInEditor } from '../services/system.js';
|
|
8
|
+
import { LeftPanel } from '../components/LeftPanel.js';
|
|
9
|
+
const ITEMS = [
|
|
10
|
+
{
|
|
11
|
+
type: 'config',
|
|
12
|
+
key: 'odoo_path_repo',
|
|
13
|
+
label: 'Chemin du dépôt Odoo',
|
|
14
|
+
description: 'Chemin absolu vers le dépôt Odoo sur ce système.',
|
|
15
|
+
validate: async (value) => {
|
|
16
|
+
const v = value.trim();
|
|
17
|
+
if (!v)
|
|
18
|
+
return 'Le chemin ne peut pas être vide.';
|
|
19
|
+
try {
|
|
20
|
+
await access(v);
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return `Chemin introuvable : ${v}`;
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
type: 'config',
|
|
30
|
+
key: 'log_buffer_size',
|
|
31
|
+
label: 'Buffer de logs',
|
|
32
|
+
description: 'Nombre de lignes de logs conservées en mémoire lors de l\'exécution d\'un service Odoo. Valeur recommandée : 500–5000.',
|
|
33
|
+
validate: async (value) => {
|
|
34
|
+
const n = parseInt(value.trim(), 10);
|
|
35
|
+
if (isNaN(n) || n < 100)
|
|
36
|
+
return 'Doit être un entier ≥ 100.';
|
|
37
|
+
return null;
|
|
38
|
+
},
|
|
39
|
+
transform: (value) => parseInt(value.trim(), 10),
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
type: 'config',
|
|
43
|
+
key: 'primary_color',
|
|
44
|
+
label: 'Couleur principale',
|
|
45
|
+
description: 'Couleur principale de l\'interface nupo (logo, accents). Format hexadécimal : #RRGGBB.',
|
|
46
|
+
validate: async (value) => {
|
|
47
|
+
if (!/^#[0-9a-fA-F]{6}$/.test(value.trim()))
|
|
48
|
+
return 'Format invalide. Exemple : #9F0C58';
|
|
49
|
+
return null;
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
type: 'config',
|
|
54
|
+
key: 'secondary_color',
|
|
55
|
+
label: 'Couleur secondaire',
|
|
56
|
+
description: 'Couleur secondaire de l\'interface nupo (titres des écrans). Format hexadécimal : #RRGGBB.',
|
|
57
|
+
validate: async (value) => {
|
|
58
|
+
if (!/^#[0-9a-fA-F]{6}$/.test(value.trim()))
|
|
59
|
+
return 'Format invalide. Exemple : #E79439';
|
|
60
|
+
return null;
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
type: 'config',
|
|
65
|
+
key: 'cursor_color',
|
|
66
|
+
label: 'Couleur du curseur',
|
|
67
|
+
description: 'Couleur de surlignage des éléments sélectionnés dans les listes. Format hexadécimal ou nom CSS : #RRGGBB ou cyan.',
|
|
68
|
+
validate: async (value) => {
|
|
69
|
+
if (!value.trim())
|
|
70
|
+
return 'La valeur ne peut pas être vide.';
|
|
71
|
+
return null;
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
type: 'config',
|
|
76
|
+
key: 'text_color',
|
|
77
|
+
label: 'Couleur des textes',
|
|
78
|
+
description: 'Couleur des textes secondaires de l\'interface nupo (hints, valeurs, labels). Format hexadécimal : #RRGGBB.',
|
|
79
|
+
validate: async (value) => {
|
|
80
|
+
if (!/^#[0-9a-fA-F]{6}$/.test(value.trim()))
|
|
81
|
+
return 'Format invalide. Exemple : #848484';
|
|
82
|
+
return null;
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
type: 'action',
|
|
87
|
+
id: 'open_base_conf',
|
|
88
|
+
label: 'Conf Odoo de base',
|
|
89
|
+
description: `Template de configuration utilisé lors de la création des services Odoo.\nFichier : ${getBaseConfPath()}`,
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
export function ConfigScreen({ config, leftWidth, onBack, onSaved }) {
|
|
93
|
+
const textColor = getTextColor(config);
|
|
94
|
+
const cursorColor = getCursorColor(config);
|
|
95
|
+
const [selected, setSelected] = useState(0);
|
|
96
|
+
const [edit, setEdit] = useState({ active: false });
|
|
97
|
+
useInput((_char, key) => {
|
|
98
|
+
if (edit.active) {
|
|
99
|
+
if (key.escape)
|
|
100
|
+
setEdit({ active: false });
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (key.upArrow)
|
|
104
|
+
setSelected(prev => (prev - 1 + ITEMS.length) % ITEMS.length);
|
|
105
|
+
if (key.downArrow)
|
|
106
|
+
setSelected(prev => (prev + 1) % ITEMS.length);
|
|
107
|
+
if (key.return) {
|
|
108
|
+
const item = ITEMS[selected];
|
|
109
|
+
if (item.type === 'config') {
|
|
110
|
+
setEdit({
|
|
111
|
+
active: true,
|
|
112
|
+
itemIndex: selected,
|
|
113
|
+
value: String(config[item.key] ?? ''),
|
|
114
|
+
error: null,
|
|
115
|
+
saving: false,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
else if (item.type === 'action' && item.id === 'open_base_conf') {
|
|
119
|
+
void ensureBaseConf().then(() => {
|
|
120
|
+
openInEditor(getBaseConfPath());
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (key.escape)
|
|
125
|
+
onBack();
|
|
126
|
+
}, { isActive: !(edit.active && edit.saving) });
|
|
127
|
+
const handleSubmit = async (inputValue) => {
|
|
128
|
+
if (!edit.active)
|
|
129
|
+
return;
|
|
130
|
+
const item = ITEMS[edit.itemIndex];
|
|
131
|
+
if (item.type !== 'config')
|
|
132
|
+
return;
|
|
133
|
+
const trimmed = inputValue.trim();
|
|
134
|
+
setEdit(prev => (prev.active ? { ...prev, saving: true, error: null } : prev));
|
|
135
|
+
const error = item.validate ? await item.validate(trimmed) : null;
|
|
136
|
+
if (error) {
|
|
137
|
+
setEdit(prev => (prev.active ? { ...prev, saving: false, error } : prev));
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const value = item.transform ? item.transform(trimmed) : trimmed;
|
|
141
|
+
await patchConfig({ [item.key]: value });
|
|
142
|
+
setEdit({ active: false });
|
|
143
|
+
onSaved();
|
|
144
|
+
};
|
|
145
|
+
const currentItem = ITEMS[selected];
|
|
146
|
+
return (React.createElement(Box, { flexDirection: "row" },
|
|
147
|
+
React.createElement(LeftPanel, { width: leftWidth, primaryColor: getPrimaryColor(config), textColor: textColor }),
|
|
148
|
+
React.createElement(Box, { flexGrow: 1, flexDirection: "column", paddingX: 3, paddingY: 2, gap: 1 },
|
|
149
|
+
React.createElement(Text, { color: getSecondaryColor(config), bold: true }, "Param\u00E8tres"),
|
|
150
|
+
!edit.active && (React.createElement(Box, { flexDirection: "column", marginTop: 1, gap: 0 }, ITEMS.map((item, i) => {
|
|
151
|
+
const isSel = i === selected;
|
|
152
|
+
const value = item.type === 'config'
|
|
153
|
+
? (String(config[item.key] ?? '') || '(non défini)')
|
|
154
|
+
: '↵ ouvrir dans $EDITOR';
|
|
155
|
+
return (React.createElement(Box, { key: item.type === 'config' ? item.key : item.id, flexDirection: "row", gap: 1 },
|
|
156
|
+
React.createElement(Text, { color: isSel ? 'black' : 'white', backgroundColor: isSel ? cursorColor : undefined, bold: isSel }, ` ${isSel ? '▶' : ' '} ${item.label}`),
|
|
157
|
+
React.createElement(Text, { color: textColor, dimColor: true }, value),
|
|
158
|
+
item.type === 'config' && item.key.endsWith('_color') && config[item.key] && (React.createElement(Text, { color: String(config[item.key]) }, "\u25CF"))));
|
|
159
|
+
}))),
|
|
160
|
+
edit.active && (React.createElement(Box, { flexDirection: "column", gap: 1, marginTop: 1 },
|
|
161
|
+
React.createElement(Text, { color: "white" },
|
|
162
|
+
ITEMS[edit.itemIndex].label,
|
|
163
|
+
" :"),
|
|
164
|
+
React.createElement(Box, null,
|
|
165
|
+
React.createElement(Text, { color: textColor, dimColor: true }, '› '),
|
|
166
|
+
React.createElement(PathInput, { value: edit.value, onChange: value => setEdit(prev => (prev.active ? { ...prev, value, error: null } : prev)), onSubmit: val => void handleSubmit(val), focus: !edit.saving, textColor: textColor })),
|
|
167
|
+
(() => {
|
|
168
|
+
const editItem = ITEMS[edit.itemIndex];
|
|
169
|
+
const val = edit.value.trim();
|
|
170
|
+
const isColorKey = editItem?.key?.endsWith('_color');
|
|
171
|
+
const isHex = /^#[0-9a-fA-F]{6}$/.test(val);
|
|
172
|
+
const isCursorKey = editItem?.key === 'cursor_color';
|
|
173
|
+
return isColorKey && (isHex || (isCursorKey && val.length > 0)) ? (React.createElement(Text, null,
|
|
174
|
+
React.createElement(Text, { color: val }, "\u25CF "),
|
|
175
|
+
React.createElement(Text, { color: textColor, dimColor: true }, val))) : null;
|
|
176
|
+
})(),
|
|
177
|
+
edit.error && React.createElement(Text, { color: "red" }, edit.error),
|
|
178
|
+
edit.saving && React.createElement(Text, { color: "yellow", dimColor: true }, "Sauvegarde\u2026"))),
|
|
179
|
+
React.createElement(Box, { marginTop: 1 }, edit.active ? (React.createElement(Text, { color: textColor, dimColor: true }, "\u21B5 sauvegarder \u00B7 \u00C9chap annuler")) : (React.createElement(Text, { color: textColor, dimColor: true }, "\u2191\u2193 naviguer \u00B7 \u21B5 modifier \u00B7 \u00C9chap retour"))),
|
|
180
|
+
!edit.active && (React.createElement(Box, { borderStyle: "round", borderColor: textColor, paddingX: 1, paddingY: 0 },
|
|
181
|
+
React.createElement(Text, { color: textColor, wrap: "wrap" }, currentItem.description))))));
|
|
182
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { NupoConfig, OdooServiceConfig } from '../types/index.js';
|
|
3
|
+
interface ConfigureServiceScreenProps {
|
|
4
|
+
config: NupoConfig;
|
|
5
|
+
leftWidth: number;
|
|
6
|
+
initialService?: OdooServiceConfig;
|
|
7
|
+
onComplete: () => void;
|
|
8
|
+
onBack: () => void;
|
|
9
|
+
}
|
|
10
|
+
export declare function ConfigureServiceScreen({ config, leftWidth, initialService, onComplete, onBack, }: ConfigureServiceScreenProps): React.JSX.Element;
|
|
11
|
+
export {};
|