fix(semaphore): update modal status immediately after manual trigger

Local state tracks in-session triggers so the UI flips to 'Triggered'
without waiting for the parent to re-fetch bookings.
This commit is contained in:
Brückner
2026-06-05 09:44:33 +02:00
parent 70399a00ec
commit c428b12352

View File

@ -42,6 +42,15 @@ export default function BookingDetailsModal({
const [triggerStatus, setTriggerStatus] = useState<string | null>(null); const [triggerStatus, setTriggerStatus] = useState<string | null>(null);
const [triggering, setTriggering] = useState(false); const [triggering, setTriggering] = useState(false);
const [localSetupTriggered, setLocalSetupTriggered] = useState(false);
const [localSetupJobId, setLocalSetupJobId] = useState('');
const [localTeardownTriggered, setLocalTeardownTriggered] = useState(false);
const [localTeardownJobId, setLocalTeardownJobId] = useState('');
const setupTriggered = booking.ansibleSetupTriggered || localSetupTriggered;
const setupJobId = booking.ansibleSetupJobId || localSetupJobId;
const teardownTriggered = booking.ansibleTeardownTriggered || localTeardownTriggered;
const teardownJobId = booking.ansibleTeardownJobId || localTeardownJobId;
async function manualTrigger(type: 'setup' | 'teardown') { async function manualTrigger(type: 'setup' | 'teardown') {
setTriggering(true); setTriggering(true);
@ -55,7 +64,10 @@ export default function BookingDetailsModal({
if (!res.ok) { if (!res.ok) {
setTriggerStatus(`Error: ${data.error || `HTTP ${res.status}`}`); setTriggerStatus(`Error: ${data.error || `HTTP ${res.status}`}`);
} else { } else {
const jobId = data.jobId !== null ? String(data.jobId) : '';
setTriggerStatus(`Job #${data.jobId} triggered successfully.`); setTriggerStatus(`Job #${data.jobId} triggered successfully.`);
if (type === 'setup') { setLocalSetupTriggered(true); setLocalSetupJobId(jobId); }
else { setLocalTeardownTriggered(true); setLocalTeardownJobId(jobId); }
} }
} catch { } catch {
setTriggerStatus('Error: Network error.'); setTriggerStatus('Error: Network error.');
@ -330,10 +342,10 @@ ${ipList.map(ip => ` - "${ip}"`).join('\n') || ' - "No registered targ
{lab.semaphoreSetupTemplateId && ( {lab.semaphoreSetupTemplateId && (
<div className="bg-slate-950/60 border border-slate-800 rounded-lg p-3 space-y-2"> <div className="bg-slate-950/60 border border-slate-800 rounded-lg p-3 space-y-2">
<p className="text-[10px] text-slate-400 uppercase tracking-wide font-semibold">Setup</p> <p className="text-[10px] text-slate-400 uppercase tracking-wide font-semibold">Setup</p>
{booking.ansibleSetupTriggered ? ( {setupTriggered ? (
<div className="flex items-center gap-1.5 text-emerald-400 text-xs font-mono"> <div className="flex items-center gap-1.5 text-emerald-400 text-xs font-mono">
<CheckCircle className="w-3.5 h-3.5" /> <CheckCircle className="w-3.5 h-3.5" />
{booking.ansibleSetupJobId ? `Job #${booking.ansibleSetupJobId}` : 'Triggered'} {setupJobId ? `Job #${setupJobId}` : 'Triggered'}
</div> </div>
) : ( ) : (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@ -352,10 +364,10 @@ ${ipList.map(ip => ` - "${ip}"`).join('\n') || ' - "No registered targ
{lab.semaphoreTeardownTemplateId && ( {lab.semaphoreTeardownTemplateId && (
<div className="bg-slate-950/60 border border-slate-800 rounded-lg p-3 space-y-2"> <div className="bg-slate-950/60 border border-slate-800 rounded-lg p-3 space-y-2">
<p className="text-[10px] text-slate-400 uppercase tracking-wide font-semibold">Teardown</p> <p className="text-[10px] text-slate-400 uppercase tracking-wide font-semibold">Teardown</p>
{booking.ansibleTeardownTriggered ? ( {teardownTriggered ? (
<div className="flex items-center gap-1.5 text-emerald-400 text-xs font-mono"> <div className="flex items-center gap-1.5 text-emerald-400 text-xs font-mono">
<CheckCircle className="w-3.5 h-3.5" /> <CheckCircle className="w-3.5 h-3.5" />
{booking.ansibleTeardownJobId ? `Job #${booking.ansibleTeardownJobId}` : 'Triggered'} {teardownJobId ? `Job #${teardownJobId}` : 'Triggered'}
</div> </div>
) : ( ) : (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">