diff --git a/frontend/src/components/MitreTechniquePicker.tsx b/frontend/src/components/MitreTechniquePicker.tsx index 74ee798..a86ccbc 100644 --- a/frontend/src/components/MitreTechniquePicker.tsx +++ b/frontend/src/components/MitreTechniquePicker.tsx @@ -37,12 +37,16 @@ export function MitreTechniquePicker({ const debounceRef = useRef | null>(null); const containerRef = useRef(null); const listRef = useRef(null); + // True once we've synced the first real techniqueId from props (parent/API load). + // After that we stop reacting to null, so keystrokes that emit onChange(null,null) + // don't propagate back and wipe the input mid-stroke. + const hasHydratedFromProps = useRef(false); - // Sync display when selection changes from outside (initial load) useEffect(() => { if (techniqueId && techniqueName) { setInputValue(`${techniqueId} — ${techniqueName}`); - } else if (!techniqueId) { + hasHydratedFromProps.current = true; + } else if (!techniqueId && !hasHydratedFromProps.current) { setInputValue(''); } }, [techniqueId, techniqueName]); diff --git a/frontend/src/components/SimulationList.tsx b/frontend/src/components/SimulationList.tsx index 2ea8b92..cc705aa 100644 --- a/frontend/src/components/SimulationList.tsx +++ b/frontend/src/components/SimulationList.tsx @@ -1,4 +1,4 @@ -import { Link } from 'react-router-dom'; +import { Link, useNavigate } from 'react-router-dom'; import { extractApiError } from '@/api/client'; import { useAuth } from '@/hooks/useAuth'; import { useEngagementSimulations } from '@/hooks/useSimulations'; @@ -19,6 +19,7 @@ function formatDate(value: string | null): string { export function SimulationList({ engagementId }: SimulationListProps): JSX.Element { const { data, isLoading, isError, error, refetch } = useEngagementSimulations(engagementId); const { canEditEngagements } = useAuth(); + const navigate = useNavigate(); if (isLoading) return ; @@ -82,14 +83,13 @@ export function SimulationList({ engagementId }: SimulationListProps): JSX.Eleme key={sim.id} className="border-b border-hairline last:border-0 hover:bg-cloud cursor-pointer" onClick={() => - (window.location.href = `/engagements/${engagementId}/simulations/${sim.id}/edit`) + navigate(`/engagements/${engagementId}/simulations/${sim.id}/edit`) } > e.stopPropagation()} > {sim.name} diff --git a/frontend/src/pages/SimulationFormPage.tsx b/frontend/src/pages/SimulationFormPage.tsx index 90f5be9..45dbc88 100644 --- a/frontend/src/pages/SimulationFormPage.tsx +++ b/frontend/src/pages/SimulationFormPage.tsx @@ -86,7 +86,7 @@ export function SimulationFormPage(): JSX.Element { description: s.description ?? '', commands: s.commands ?? '', prerequisites: s.prerequisites ?? '', - executed_at: s.executed_at ? s.executed_at.replace(' ', 'T').slice(0, 16) : '', + executed_at: s.executed_at ? s.executed_at.slice(0, 16) : '', execution_result: s.execution_result ?? '', }); setSoc({ @@ -117,6 +117,7 @@ export function SimulationFormPage(): JSX.Element { const socCanEdit = isSoc && (status === 'review_required' || status === 'done'); const socBlocked = isSoc && (status === 'pending' || status === 'in_progress'); + const canSaveSoc = socCanEdit || canEditEngagements; const rtDisabled = !canEditRT; const socDisabled = !canEditEngagements && !socCanEdit; @@ -395,7 +396,7 @@ export function SimulationFormPage(): JSX.Element { {/* SOC card */}
e.preventDefault()} + onSubmit={canSaveSoc ? onSaveSOC : (e) => e.preventDefault()} noValidate className="card-product flex flex-col gap-md" > @@ -441,7 +442,7 @@ export function SimulationFormPage(): JSX.Element { /> - {(socCanEdit || canEditEngagements) && ( + {canSaveSoc && (