bigtech-sucks/packages/web/src/screens/ShowdownScreen.tsx
CLU de53ac41a5 bigtech.sucks: living database + first assessment
The database is the source of truth; the site is a view over it.

- packages/core/db/schema.sql: vendors, products, categories, the 8-dimension
  instrument, assessments, scores, evidence, findings, off-ramp, plus the
  derived v_product_score view the game reads.
- packages/core/db/build.mjs: rebuilds bigtech.db, absorbs the legacy 43-product
  roster as unassessed, applies seeds, then verifies the instrument's own rules
  (weights sum to 100, totals recompute, grades match bands, every score has a
  rationale, no tier-4 evidence on a scored dimension, reproducible-build caps
  actually enforced).
- packages/core/db/seed/0001-signal.sql: Signal Desktop 8.36.0-alpha.1 on linux,
  75.9/100 grade B, weakest dimension local_data_exposure. Evidence points at
  the re-runnable audit artifacts in the Servetus vault.
- packages/core/src/data/generated/catalog.{ts,json}: generated export.

Verified: 44 products, 1 assessed, all checks pass.
2026-10-29 13:16:29 -05:00

74 lines
No EOL
2.5 KiB
TypeScript

import {
getCaptureStatus,
getOutlawById,
redDialogue,
type PlayerJourneyState,
} from '@bigtech-sucks/core';
import { ArtSlotView } from '../components/ArtSlotView';
interface Props {
state: PlayerJourneyState;
onStep: (step: 'round_up' | 'validate' | 'cut') => void;
onBack: () => void;
}
export function ShowdownScreen({ state, onStep, onBack }: Props) {
const outlaw = state.activeOutlawId ? getOutlawById(state.activeOutlawId) : undefined;
if (!outlaw) return null;
const status = getCaptureStatus(state, outlaw.id);
const isBoss = !!outlaw.boss;
return (
<section className="screen showdown-screen">
<button type="button" className="btn btn-ghost back-btn" onClick={onBack}>
← Territory
</button>
<div className="showdown-stage">
<ArtSlotView slotId={outlaw.faceArtSlot} className="showdown-face" />
<h2>{outlaw.name}</h2>
<p className="wanted-crime">{outlaw.crime}</p>
{isBoss && <p className="boss-note">{redDialogue.mailRiverBoss[0]}</p>}
<div className="capture-tracker">
<div className={`step ${status === 'at_large' ? 'active' : status !== 'at_large' ? 'done' : ''}`}>
1. At Large
</div>
<div className={`step ${status === 'in_custody' ? 'active' : status === 'captured' ? 'done' : ''}`}>
2. In Custody
</div>
<div className={`step ${status === 'captured' ? 'done' : ''}`}>3. Captured</div>
</div>
{status === 'at_large' && (
<div className="showdown-action">
<p>{redDialogue.showdownRoundUp[0]}</p>
<button type="button" className="btn btn-primary" onClick={() => onStep('round_up')}>
Round up
</button>
</div>
)}
{status === 'in_custody' && (
<div className="showdown-action">
<p>{redDialogue.showdownValidate[0]}</p>
<button type="button" className="btn btn-secondary" onClick={() => onStep('validate')}>
Validate (check your land)
</button>
<button type="button" className="btn btn-primary" onClick={() => onStep('cut')}>
Cut the cord
</button>
</div>
)}
{status === 'captured' && (
<div className="captured-stamp">
<ArtSlotView slotId="captured_stamp" />
<p>Bounty collected: ${outlaw.bounty}/mo</p>
</div>
)}
</div>
</section>
);
}