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

@@ -25,6 +25,9 @@ for f in api/*.js; do
}
done
echo "Running unit tests..."
npm run test:data || exit 1
echo "Running edge function tests..."
node --test tests/edge-functions.test.mjs || exit 1