refactor(sanctions): rename panel, restructure layout, fix stale empty cache (#1799)

* refactor(sanctions): rename panel, restructure layout, fix stale empty cache

- Rename panel from "Sanctions Pressure" to "Sanctions & Designations"
- Make countries the hero section; demote programs to bottom
- Remove redundant Top Country/Program headline rows and Source Mix card
- Summary now shows New, Total, Vessels, Aircraft (4 cards instead of 6)
- Fix stale empty-state cache: circuit breaker now skips caching results
  with totalCount=0, so a failed seed no longer persists zeros in
  IndexedDB across reloads
- Single "Sanctions data unavailable." empty state instead of three
  cascading "No X available" messages
- Remove dead CSS for .sanctions-headlines / .sanctions-headline / -label / -value

* fix(circuit-breaker): evict stale cache when shouldCache rejects SWR result

When a background SWR refresh returns a result that shouldCache() rejects,
evict the stale cache entry rather than silently discarding the result.
Without this, a service returning empty after having real data (e.g. OFAC
seed missing from Redis) continues serving the old payload indefinitely via
stale-while-revalidate, up to the 24h persistent-cache ceiling.

After this fix the stale window is bounded to one SWR cycle: the first
foreground load after the feed goes down still serves cached data, the
background refresh evicts it, and the next load surfaces the unavailable state.

* fix(sanctions): move stale-cache eviction to service, revert circuit-breaker change

The circuit-breaker SWR eviction on shouldCache rejection broke the existing
market-quote test (which correctly expects stale prices to survive a transient
empty response). The concerns are legitimately different:

- Market quotes: shouldCache rejection must preserve stale cache (transient blips)
- Sanctions: feed down must surface as unavailable, not serve old designations

Fix: revert circuit-breaker.ts to original SWR behavior, and instead call
breaker.clearCache() explicitly inside the sanctions fn() callback when
totalCount === 0. shouldCache still prevents writing the empty result, and
the explicit clearCache() evicts any stale entry so the next SWR cycle
returns the unavailable state.

* chore(hooks): add unit test suite to pre-push gate

npm run test:data was missing from pre-push — only edge-functions.test.mjs
and mdx-lint.test.mjs ran locally. CI catches all tests/*.test.mjs but the
hook did not, causing a broken circuit-breaker change to reach GitHub before
the regression was detected.
This commit is contained in:
Elie Habib
2026-03-18 10:32:06 +04:00
committed by GitHub
parent 80cb7d5aa7
commit bb92815fe0
4 changed files with 31 additions and 48 deletions

View File

@@ -8743,7 +8743,6 @@ a.prediction-link:hover {
}
.sanctions-summary-label,
.sanctions-headline-label,
.sanctions-section-title {
font-size: 9px;
text-transform: uppercase;
@@ -8770,7 +8769,6 @@ a.prediction-link:hover {
color: var(--accent);
}
.sanctions-headlines,
.sanctions-sections {
display: flex;
flex-direction: column;
@@ -8778,26 +8776,11 @@ a.prediction-link:hover {
padding: 0 8px;
}
.sanctions-headline,
.sanctions-section {
border: 1px solid var(--border);
background: var(--surface);
}
.sanctions-headline {
display: flex;
justify-content: space-between;
gap: 12px;
padding: 8px 10px;
align-items: baseline;
}
.sanctions-headline-value {
font-size: 12px;
color: var(--text-secondary);
text-align: right;
}
.sanctions-section-title {
padding: 8px 10px 0;
}