mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-15 09:49:48 +00:00
Align Patrol header recency labels
Patrol header metadata now uses the canonical recency label so the top row no longer falls back to a generic last-run prefix when the page distinguishes activity from full verification.
This commit is contained in:
parent
f9fc8203fa
commit
0703e68766
3 changed files with 15 additions and 4 deletions
|
|
@ -146,6 +146,9 @@ The summary recency chip must follow the same governed scope distinction. When
|
|||
the latest completed activity was only a scoped run, the summary should label
|
||||
that timestamp as `Last activity` instead of `Last patrol`; `Last full patrol`
|
||||
belongs only to the most recent completed full Patrol run.
|
||||
That same recency contract also applies to the header metadata row. The top
|
||||
header must not revert to a generic `Last:` timestamp when the rest of Patrol
|
||||
is explicitly distinguishing activity from full verification recency.
|
||||
That summary surface must also avoid reintroducing a second compact assessment
|
||||
or verification layer beneath the primary card. Supporting metric strips
|
||||
belong to counts and outcomes such as active findings, critical findings,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import { groupModelsByProvider } from '@/utils/patrolFormat';
|
|||
import { getAIQuickstartCreditsPresentation } from '@/utils/aiQuickstartPresentation';
|
||||
import { buildPatrolScheduleOptions } from '@/utils/aiPatrolSchedulePresentation';
|
||||
import { getPatrolRuntimePresentation } from '@/utils/patrolRuntimePresentation';
|
||||
import { getPatrolRecencyPresentation } from '@/utils/patrolSummaryPresentation';
|
||||
import type { PatrolIntelligenceState } from './usePatrolIntelligenceState';
|
||||
|
||||
export function PatrolIntelligenceHeader(props: { state: PatrolIntelligenceState }) {
|
||||
|
|
@ -27,6 +28,12 @@ export function PatrolIntelligenceHeader(props: { state: PatrolIntelligenceState
|
|||
const runtimePresentation = createMemo(() =>
|
||||
getPatrolRuntimePresentation(state.runtimeState(), state.blockedReason()),
|
||||
);
|
||||
const recency = createMemo(() =>
|
||||
getPatrolRecencyPresentation({
|
||||
runs: state.patrolRunHistory() ?? [],
|
||||
lastPatrolAt: state.patrolStatus()?.last_patrol_at,
|
||||
}),
|
||||
);
|
||||
const showQuickstartStatus = createMemo(() => {
|
||||
const patrolStatus = state.patrolStatus();
|
||||
if (!patrolStatus) return false;
|
||||
|
|
@ -61,11 +68,11 @@ export function PatrolIntelligenceHeader(props: { state: PatrolIntelligenceState
|
|||
class="mb-3"
|
||||
actions={
|
||||
<div class="flex flex-wrap items-center justify-end gap-3">
|
||||
<Show when={state.patrolStatus()?.last_patrol_at}>
|
||||
<Show when={recency().timestamp}>
|
||||
<div class="hidden sm:flex items-center gap-3 text-xs text-muted">
|
||||
<span>
|
||||
Last:{' '}
|
||||
{formatRelativeTime(state.patrolStatus()?.last_patrol_at, {
|
||||
{recency().label}:{' '}
|
||||
{formatRelativeTime(recency().timestamp, {
|
||||
compact: true,
|
||||
emptyText: 'Never',
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -817,12 +817,13 @@ describe('AIIntelligence entitlement gating', () => {
|
|||
await waitFor(() => {
|
||||
expect(screen.getAllByText('Coverage incomplete').length).toBeGreaterThan(0);
|
||||
expect(screen.getByText('No recent full patrol')).toBeInTheDocument();
|
||||
expect(screen.getByText(/Last activity/i)).toBeInTheDocument();
|
||||
expect(screen.getAllByText(/Last activity/i).length).toBeGreaterThan(0);
|
||||
expect(screen.getByText('Warnings')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(screen.queryByText('No issues found')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/Last patrol/i)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/^Last:/i)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Partial verification')).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.getAllByText(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue