-
-
-
- Les champs dynamiques seront dépliés ici selon les profils sélectionnés.
+
+ {profiles.map((p) => {
+ const isOpen = open.has(p.id);
+ return (
+
+
+ {isOpen && (
+
+
{p.description}
+ {p.fields.map((f) => (
+
+ ))}
+
+
+
+
+
+ )}
+
+ );
+ })}
+
+ {msg &&
{msg.text}
}
+
+
setPreview(null)} title={`Preview — ${preview?.title ?? ""}`} width={560} footer={}>
+ {preview?.script}
+
+
+
setConfirm(null)}
+ title={`Confirmer — ${confirm?.label ?? ""}`}
+ width={440}
+ footer={
+ <>
+
+
+ >
+ }
+ >
+ Le profil {confirm?.label} ({riskLabel(confirm?.risk ?? "")}) sera exécuté sur {machine.name}.
+ Action tracée comme demande validée (action_request).
+
);
}
+function ProfileFieldInput({
+ field,
+ value,
+ onChange,
+}: {
+ field: ProfileManifestView["fields"][number];
+ value: string | number | boolean | undefined;
+ onChange: (v: string | number | boolean) => void;
+}) {
+ if (field.type === "bool") {
+ return (
+
+ onChange(e.target.checked)} />
+
+ );
+ }
+ if (field.type === "select" && field.options?.length) {
+ return (
+
+ );
+ }
+ return (
+
onChange(e.target.value)}
+ />
+ );
+}
+
function formatDate(value: string | null): string {
if (!value) return "-";
const date = new Date(value);
@@ -761,7 +935,7 @@ export function MachineDetailPanel({
setDockerOpen((v) => !v)} />
{dockerOpen && }
setPostOpen((v) => !v)} />
- {postOpen && }
+ {postOpen && }
{configOpen && (
diff --git a/client/src/lib/api.ts b/client/src/lib/api.ts
index a74ca12..467b22d 100644
--- a/client/src/lib/api.ts
+++ b/client/src/lib/api.ts
@@ -42,6 +42,13 @@ export const api = {
}),
deleteMachine: (id: string) => req<{ ok: boolean }>(`/machines/${id}`, { method: "DELETE" }),
+ // --- Post-install (profils) ---
+ getProfiles: () => req