feat(frontend): démarrage auto du serveur + erreur port occupé actionnable (#89)
Étend ServerExposureSettings avec autoStart (défaut false, mock aligné sur le backend). useDeployment expose setAutoStart, qui persiste immédiatement le draft sans jamais appeler start() ni toucher le comportement de stop(), et ne met à jour l'état local qu'après confirmation de la sauvegarde (même piste de validation que start/save). DeploymentSettings ajoute la case à cocher sous le panneau Serveur, et des actions « Modifier le port »/« Réessayer » quand le statut échoue avec le code PORT_IN_USE stabilisé côté backend. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@ -23,7 +23,7 @@
|
||||
* a dead end.
|
||||
*/
|
||||
|
||||
import { useState } from "react";
|
||||
import { useRef, useState } from "react";
|
||||
|
||||
import type { ServerExposureMode } from "@/domain";
|
||||
import { Button, Field, Input, Panel, cn } from "@/shared";
|
||||
@ -105,6 +105,9 @@ function ReadOnlyValue({ value, copyLabel }: { value: string; copyLabel: string
|
||||
|
||||
export function DeploymentSettings() {
|
||||
const vm = useDeployment();
|
||||
// "Modifier le port" (#89) jumps into Accès réseau — imperative focus is the
|
||||
// simplest correct answer for a cross-panel affordance on one scrolled page.
|
||||
const portInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
if (!vm.ready || !vm.settings) {
|
||||
return (
|
||||
@ -170,17 +173,72 @@ export function DeploymentSettings() {
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* A failure carries the backend's message: it is the correction. */}
|
||||
{/* A failure carries the backend's message: it is the correction.
|
||||
A port conflict (#89 — most often surfaced by auto-start at
|
||||
launch, but shown the same way for a manual start) gets a
|
||||
dedicated, actionable message instead of the generic one. */}
|
||||
{status.state === "failed" && status.error && (
|
||||
<p role="alert" className="text-sm text-danger">
|
||||
{status.error.message}
|
||||
</p>
|
||||
status.error.code === "PORT_IN_USE" ? (
|
||||
<div className="flex flex-col gap-2">
|
||||
<p role="alert" className="text-sm text-danger">
|
||||
Le serveur n'a pas démarré automatiquement. {status.error.message}
|
||||
</p>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={() => {
|
||||
portInputRef.current?.scrollIntoView?.({
|
||||
behavior: "smooth",
|
||||
block: "center",
|
||||
});
|
||||
portInputRef.current?.focus();
|
||||
}}
|
||||
>
|
||||
Modifier le port
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() => void vm.start()}
|
||||
disabled={vm.busy}
|
||||
>
|
||||
Réessayer
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<p role="alert" className="text-sm text-danger">
|
||||
{status.error.message}
|
||||
</p>
|
||||
)
|
||||
)}
|
||||
{vm.actionError && (
|
||||
<p role="alert" className="text-sm text-danger">
|
||||
{vm.actionError}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Auto-start at launch (#89) — a persistence-only toggle, never a
|
||||
start trigger; it never runs the server right now. */}
|
||||
<label className="flex cursor-pointer items-start gap-2 pt-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="mt-0.5"
|
||||
aria-label="Démarrer le serveur au lancement d'IdeA"
|
||||
checked={settings.autoStart}
|
||||
disabled={vm.autoStartBusy}
|
||||
onChange={(e) => void vm.setAutoStart(e.target.checked)}
|
||||
/>
|
||||
<span className="flex flex-col gap-0.5">
|
||||
<span className="text-sm text-content">
|
||||
Démarrer le serveur au lancement d'IdeA
|
||||
</span>
|
||||
<span className="text-xs text-muted">
|
||||
IdeA utilisera les réglages réseau enregistrés ci-dessous au
|
||||
prochain démarrage de l'application desktop.
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</Panel>
|
||||
|
||||
@ -225,6 +283,7 @@ export function DeploymentSettings() {
|
||||
<Field label="Port">
|
||||
{({ id }) => (
|
||||
<Input
|
||||
ref={portInputRef}
|
||||
id={id}
|
||||
type="number"
|
||||
value={String(settings.port)}
|
||||
|
||||
Reference in New Issue
Block a user