fix(frontend): SOC C2 tab RBAC gate + safeTab sync + disabled tab contrast (review polish)

- c2TabDisabled: add isSoc condition — SOC role has no access to /c2/tasks (backend 403)
- safeTab sync: useEffect before early returns using detail.data?.status; syncs
  window.location.hash when hash points to a now-disabled tab after status transition
- create-mode submit button: type="submit" form="" → type="button" (form association fix)
- .tab-underline-disabled: text-graphite opacity-50 → text-steel (consistent with
  btn-outline:disabled; better perceptual contrast)
- tests: 254 passing (+ SOC × C2 tab disabled assertion)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Knacky
2026-06-22 10:59:54 +02:00
parent 0378792dc4
commit 578a7c3d1a
4 changed files with 38 additions and 18 deletions

View File

@@ -216,16 +216,24 @@ describe('SimulationFormPage — tabs', () => {
});
});
it('C2 tab is disabled in edit mode (no tasks shown until tab visible)', async () => {
it('C2 tab is enabled for redteam in edit mode', async () => {
renderWithProviders(<EditPage />, {
routerProps: { initialEntries: ['/engagements/42/simulations/7/edit'] },
});
await waitFor(() => expect(screen.getByLabelText(/^Nom/i)).not.toBeDisabled());
// C2 tab should exist and be enabled (it's edit mode, not create mode)
const c2Tab = screen.getByRole('tab', { name: /Tâche C2/i });
expect(c2Tab).not.toBeDisabled();
expect(screen.getByRole('tab', { name: /Tâche C2/i })).not.toBeDisabled();
});
it('C2 tab is disabled for SOC role (backend 403 on /c2/tasks)', async () => {
mockRole = 'soc';
mock.onGet('/simulations/7').reply(200, { ...BASE_SIM, status: 'review_required' });
renderWithProviders(<EditPage />, {
routerProps: { initialEntries: ['/engagements/42/simulations/7/edit'] },
});
await waitFor(() => expect(screen.getByRole('tab', { name: /Tâche C2/i })).toBeDisabled());
});
it('SOC fields are visible after switching to SOC tab', async () => {