mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-22 03:02:35 +00:00
Move Patrol investigation context behind findings
Patrol now renders investigation context beneath the findings and history workspace, keeping the primary summary card focused on assessment and verification while preserving the same context content and toggle behavior.
This commit is contained in:
parent
dda60b3d21
commit
247bccded4
4 changed files with 66 additions and 62 deletions
|
|
@ -192,6 +192,10 @@ operator whether Patrol recently completed a successful full patrol, only ran
|
|||
scoped alert-triggered checks, or ended its most recent full patrol with
|
||||
errors, so the page does not leave trust and coverage as implicit background
|
||||
knowledge.
|
||||
The same hierarchy applies to investigation context. Correlations, recent
|
||||
changes, and policy posture are secondary evidence for deeper investigation, so
|
||||
the `Investigation context` section belongs beneath the primary findings/history
|
||||
workspace rather than inside the assessment card itself.
|
||||
The Patrol status bar should stay factual and operational within that same
|
||||
hierarchy. `frontend-modern/src/components/patrol/PatrolStatusBar.tsx` is a
|
||||
recent-activity strip, not a second health verdict: when Patrol is active it
|
||||
|
|
|
|||
|
|
@ -13,9 +13,6 @@ import {
|
|||
} from '@/utils/patrolSummaryPresentation';
|
||||
import { getPatrolRuntimePresentation } from '@/utils/patrolRuntimePresentation';
|
||||
import { getSemanticTonePresentation } from '@/utils/semanticTonePresentation';
|
||||
import { ResourcePolicySummary } from '@/components/Infrastructure/ResourcePolicySummary';
|
||||
import { ResourceCorrelationSummary } from '@/components/Infrastructure/ResourceCorrelationSummary';
|
||||
import { ResourceChangeSummary } from '@/components/Infrastructure/ResourceChangeSummary';
|
||||
import { formatRelativeTime } from '@/utils/format';
|
||||
import type { PatrolIntelligenceState } from './usePatrolIntelligenceState';
|
||||
|
||||
|
|
@ -190,66 +187,7 @@ export function PatrolIntelligenceSummary(props: { state: PatrolIntelligenceStat
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<Show when={state.hasInvestigationContext()}>
|
||||
<div class="mt-4 rounded-md border border-border-subtle bg-base/90 p-3">
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.16em] text-muted">
|
||||
Investigation context
|
||||
</p>
|
||||
<p class="mt-1 text-sm text-muted">
|
||||
Secondary change and policy signals for deeper investigation.
|
||||
</p>
|
||||
<Show when={state.investigationContextSummary()}>
|
||||
<p class="mt-1 text-xs text-base-content">
|
||||
{state.investigationContextSummary()}
|
||||
</p>
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => state.setShowInvestigationContext((value) => !value)}
|
||||
class="inline-flex items-center rounded-md border border-border bg-surface px-3 py-1.5 text-xs font-medium text-base-content transition-colors hover:bg-surface-hover"
|
||||
>
|
||||
{state.showInvestigationContext() ? 'Hide context' : 'Show context'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Show when={state.showInvestigationContext()}>
|
||||
<div class="mt-4 grid gap-4 lg:grid-cols-[minmax(0,1.4fr)_minmax(0,1fr)]">
|
||||
<Show when={state.recentChangeCount() > 0}>
|
||||
<ResourceChangeSummary
|
||||
class="space-y-0"
|
||||
title="Recent changes"
|
||||
subtitle="Last 24 hours"
|
||||
changes={summary().recent_changes}
|
||||
maxChanges={3}
|
||||
compact
|
||||
/>
|
||||
</Show>
|
||||
|
||||
<div class="space-y-4">
|
||||
<Show when={state.correlations().length > 0}>
|
||||
<ResourceCorrelationSummary
|
||||
title="Correlations"
|
||||
correlations={state.correlations()}
|
||||
summaryText={`${state.correlationTotal()} total`}
|
||||
/>
|
||||
</Show>
|
||||
|
||||
<ResourcePolicySummary
|
||||
posture={state.policyPosture()}
|
||||
title="Policy posture"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</Show>
|
||||
</section>
|
||||
)}
|
||||
</Show>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ import { ApprovalBanner, PatrolStatusBar, RunHistoryPanel } from '@/components/p
|
|||
import { getFindingSeverityToneClasses } from '@/utils/aiFindingPresentation';
|
||||
import { formatRelativeTime } from '@/utils/format';
|
||||
import { formatTriggerReason } from '@/utils/patrolFormat';
|
||||
import { ResourcePolicySummary } from '@/components/Infrastructure/ResourcePolicySummary';
|
||||
import { ResourceCorrelationSummary } from '@/components/Infrastructure/ResourceCorrelationSummary';
|
||||
import { ResourceChangeSummary } from '@/components/Infrastructure/ResourceChangeSummary';
|
||||
import type { PatrolIntelligenceState } from './usePatrolIntelligenceState';
|
||||
|
||||
export function PatrolIntelligenceWorkspace(props: { state: PatrolIntelligenceState }) {
|
||||
|
|
@ -120,6 +123,59 @@ export function PatrolIntelligenceWorkspace(props: { state: PatrolIntelligenceSt
|
|||
patrolStream={state.patrolStream}
|
||||
/>
|
||||
</Show>
|
||||
|
||||
<Show when={state.hasInvestigationContext()}>
|
||||
<section class="rounded-md border border-border-subtle bg-base/90 p-3">
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.16em] text-muted">
|
||||
Investigation context
|
||||
</p>
|
||||
<p class="mt-1 text-sm text-muted">
|
||||
Secondary change and policy signals for deeper investigation.
|
||||
</p>
|
||||
<Show when={state.investigationContextSummary()}>
|
||||
<p class="mt-1 text-xs text-base-content">{state.investigationContextSummary()}</p>
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => state.setShowInvestigationContext((value) => !value)}
|
||||
class="inline-flex items-center rounded-md border border-border bg-surface px-3 py-1.5 text-xs font-medium text-base-content transition-colors hover:bg-surface-hover"
|
||||
>
|
||||
{state.showInvestigationContext() ? 'Hide context' : 'Show context'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Show when={state.showInvestigationContext()}>
|
||||
<div class="mt-4 grid gap-4 lg:grid-cols-[minmax(0,1.4fr)_minmax(0,1fr)]">
|
||||
<Show when={state.recentChangeCount() > 0}>
|
||||
<ResourceChangeSummary
|
||||
class="space-y-0"
|
||||
title="Recent changes"
|
||||
subtitle="Last 24 hours"
|
||||
changes={state.intelligenceSummary()?.recent_changes}
|
||||
maxChanges={3}
|
||||
compact
|
||||
/>
|
||||
</Show>
|
||||
|
||||
<div class="space-y-4">
|
||||
<Show when={state.correlations().length > 0}>
|
||||
<ResourceCorrelationSummary
|
||||
title="Correlations"
|
||||
correlations={state.correlations()}
|
||||
summaryText={`${state.correlationTotal()} total`}
|
||||
/>
|
||||
</Show>
|
||||
|
||||
<ResourcePolicySummary posture={state.policyPosture()} title="Policy posture" />
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
</section>
|
||||
</Show>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -600,6 +600,12 @@ describe('AIIntelligence entitlement gating', () => {
|
|||
expect(screen.getByText('No active issues detected')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const findingsPanel = screen.getByTestId('findings-panel');
|
||||
const contextHeading = screen.getByText('Investigation context');
|
||||
expect(
|
||||
Boolean(findingsPanel.compareDocumentPosition(contextHeading) & Node.DOCUMENT_POSITION_FOLLOWING),
|
||||
).toBe(true);
|
||||
|
||||
expect(screen.getByText(/Health A · 91\/100/)).toBeInTheDocument();
|
||||
expect(screen.getByText('Investigation context')).toBeInTheDocument();
|
||||
expect(screen.getByText('1 recent change · 4 governed resources')).toBeInTheDocument();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue