sprint/2-simulations #3
@@ -37,12 +37,16 @@ export function MitreTechniquePicker({
|
||||
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const listRef = useRef<HTMLUListElement>(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]);
|
||||
|
||||
@@ -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 <LoadingState label="Loading simulations…" />;
|
||||
|
||||
@@ -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`)
|
||||
}
|
||||
>
|
||||
<td className="px-xl py-md">
|
||||
<Link
|
||||
to={`/engagements/${engagementId}/simulations/${sim.id}/edit`}
|
||||
className="text-ink font-medium hover:underline"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{sim.name}
|
||||
</Link>
|
||||
|
||||
@@ -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 */}
|
||||
<form
|
||||
id="soc-form"
|
||||
onSubmit={socCanEdit ? onSaveSOC : canEditEngagements ? onSaveSOC : (e) => 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 {
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
{(socCanEdit || canEditEngagements) && (
|
||||
{canSaveSoc && (
|
||||
<div className="flex items-center gap-md pt-sm border-t border-hairline">
|
||||
<button type="submit" form="soc-form" className="btn-primary" disabled={submitting}>
|
||||
{updateMutation.isPending ? 'Saving…' : 'Save SOC'}
|
||||
|
||||
Reference in New Issue
Block a user