Skip to content

Commit 8cc8fcd

Browse files
viki-shclaude
andcommitted
Reorder sections, fix bar scaling, nest severity in dataset section
- Section order: Overview → Enforcement Style → Main Privacy Laws → From Our Dataset - Fix bars: use light gray (#e5e5e5) track so proportional fills are visible (previously bg-border was black, same as fill, making all bars look identical) - Scale bars relative to largest item (largest=100%, others proportional) - Move Severity Snapshot inside From Our Dataset section - Extract BarSection component for cleaner code Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent b070c87 commit 8cc8fcd

1 file changed

Lines changed: 62 additions & 96 deletions

File tree

src/components/JurisdictionDetail.tsx

Lines changed: 62 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ export default function JurisdictionDetail({ jurisdiction }: JurisdictionDetailP
5959
.sort(([, a], [, b]) => b - a)
6060
.slice(0, 5);
6161

62-
// Top companies by fine — include outcome
6362
const topCompanies = [...jCases]
6463
.sort((a, b) => b.fineAmount - a.fineAmount)
6564
.slice(0, 5)
@@ -96,41 +95,14 @@ export default function JurisdictionDetail({ jurisdiction }: JurisdictionDetailP
9695
</div>
9796

9897
<div className="p-6 space-y-10">
99-
{/* Overview */}
98+
{/* 1. Overview */}
10099
<section>
101100
<h2 className="text-2xl font-bold tracking-tight mb-4">\ OVERVIEW</h2>
102101
<div className="h-[3px] bg-border mb-4" />
103102
<p className="text-[15px] leading-relaxed">{info.overview}</p>
104103
</section>
105104

106-
{/* Severity Snapshot — yellow detail boxes matching CaseDetail */}
107-
<section>
108-
<h2 className="text-2xl font-bold tracking-tight mb-4">\ SEVERITY SNAPSHOT</h2>
109-
<div className="h-[3px] bg-border mb-4" />
110-
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
111-
<div className="detail-yellow-box p-4">
112-
<p className="text-[10px] font-mono font-bold uppercase tracking-wider text-muted-foreground">Total Fines</p>
113-
<p className="text-lg font-bold mt-1">{formatCurrency(stats.totalFines)}</p>
114-
</div>
115-
<div className="detail-yellow-box p-4">
116-
<p className="text-[10px] font-mono font-bold uppercase tracking-wider text-muted-foreground">Largest Fine</p>
117-
<p className="text-lg font-bold mt-1">{formatCurrency(stats.maxFine)}</p>
118-
</div>
119-
<div className="detail-yellow-box p-4">
120-
<p className="text-[10px] font-mono font-bold uppercase tracking-wider text-muted-foreground">Avg Fine</p>
121-
<p className="text-lg font-bold mt-1">{formatCurrency(stats.avgFine)}</p>
122-
</div>
123-
<div className="detail-yellow-box p-4">
124-
<p className="text-[10px] font-mono font-bold uppercase tracking-wider text-muted-foreground">% Monetary</p>
125-
<p className="text-lg font-bold mt-1">{stats.pctMonetary}%</p>
126-
</div>
127-
</div>
128-
<p className="text-[10px] font-mono font-bold uppercase tracking-widest text-muted-foreground mt-3">
129-
{stats.totalCases} cases · {stats.minYear}{stats.maxYear}
130-
</p>
131-
</section>
132-
133-
{/* Enforcement Style */}
105+
{/* 2. Enforcement Style */}
134106
<section>
135107
<h2 className="text-2xl font-bold tracking-tight mb-4">\ ENFORCEMENT STYLE</h2>
136108
<div className="h-[3px] bg-border mb-4" />
@@ -139,7 +111,7 @@ export default function JurisdictionDetail({ jurisdiction }: JurisdictionDetailP
139111
</div>
140112
</section>
141113

142-
{/* Main Privacy Laws — loot-drop accordion */}
114+
{/* 3. Main Privacy Laws — loot-drop accordion */}
143115
<section>
144116
<button
145117
type="button"
@@ -166,81 +138,48 @@ export default function JurisdictionDetail({ jurisdiction }: JurisdictionDetailP
166138
)}
167139
</section>
168140

169-
{/* Stats Grid */}
141+
<div className="border-t-2 border-dashed border-border" />
142+
143+
{/* 4. From Our Dataset — contains severity snapshot + bar charts + table */}
170144
<section>
171145
<h2 className="text-2xl font-bold tracking-tight mb-4">\ FROM OUR DATASET</h2>
172-
<div className="h-[3px] bg-border mb-6" />
146+
<div className="h-[3px] bg-border mb-4" />
147+
<p className="text-[10px] font-mono font-bold uppercase tracking-widest text-muted-foreground mb-6">
148+
{stats.totalCases} cases · {stats.minYear}{stats.maxYear}
149+
</p>
173150

174-
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
175-
{/* Top Sectors */}
176-
<div>
177-
<p className="text-[10px] font-mono font-bold uppercase tracking-widest text-muted-foreground mb-3">Top Sectors Affected</p>
178-
<div className="space-y-3">
179-
{stats.topSectors.map(([sector, count]) => {
180-
const pct = stats.totalCases > 0 ? (count / stats.totalCases) * 100 : 0;
181-
return (
182-
<div key={sector}>
183-
<div className="flex items-baseline justify-between mb-1">
184-
<span className="text-sm font-bold truncate">{sector}</span>
185-
<span className="text-xs font-mono font-bold ml-2 shrink-0">{count}</span>
186-
</div>
187-
<div className="h-2.5 bg-border overflow-hidden">
188-
<div className="h-full bg-black transition-all duration-500" style={{ width: `${pct}%` }} />
189-
</div>
190-
</div>
191-
);
192-
})}
193-
</div>
151+
{/* Severity Snapshot */}
152+
<p className="text-[10px] font-mono font-bold uppercase tracking-widest text-muted-foreground mb-3">Severity Snapshot</p>
153+
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
154+
<div className="detail-yellow-box p-4">
155+
<p className="text-[10px] font-mono font-bold uppercase tracking-wider text-muted-foreground">Total Fines</p>
156+
<p className="text-lg font-bold mt-1">{formatCurrency(stats.totalFines)}</p>
194157
</div>
195-
196-
{/* Most Common Violations */}
197-
<div>
198-
<p className="text-[10px] font-mono font-bold uppercase tracking-widest text-muted-foreground mb-3">Most Common Violations</p>
199-
<div className="space-y-3">
200-
{stats.topViolations.map(([violation, count]) => {
201-
const pct = stats.totalCases > 0 ? (count / stats.totalCases) * 100 : 0;
202-
return (
203-
<div key={violation}>
204-
<div className="flex items-baseline justify-between mb-1">
205-
<span className="text-sm font-bold truncate">{violation}</span>
206-
<span className="text-xs font-mono font-bold ml-2 shrink-0">{count}</span>
207-
</div>
208-
<div className="h-2.5 bg-border overflow-hidden">
209-
<div className="h-full bg-black transition-all duration-500" style={{ width: `${pct}%` }} />
210-
</div>
211-
</div>
212-
);
213-
})}
214-
</div>
158+
<div className="detail-yellow-box p-4">
159+
<p className="text-[10px] font-mono font-bold uppercase tracking-wider text-muted-foreground">Largest Fine</p>
160+
<p className="text-lg font-bold mt-1">{formatCurrency(stats.maxFine)}</p>
161+
</div>
162+
<div className="detail-yellow-box p-4">
163+
<p className="text-[10px] font-mono font-bold uppercase tracking-wider text-muted-foreground">Avg Fine</p>
164+
<p className="text-lg font-bold mt-1">{formatCurrency(stats.avgFine)}</p>
165+
</div>
166+
<div className="detail-yellow-box p-4">
167+
<p className="text-[10px] font-mono font-bold uppercase tracking-wider text-muted-foreground">% Monetary</p>
168+
<p className="text-lg font-bold mt-1">{stats.pctMonetary}%</p>
215169
</div>
170+
</div>
216171

217-
{/* Most Common Outcomes */}
172+
{/* Bar charts grid */}
173+
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 mb-8">
174+
<BarSection title="Top Sectors Affected" data={stats.topSectors} total={stats.totalCases} />
175+
<BarSection title="Most Common Violations" data={stats.topViolations} total={stats.totalCases} />
218176
<div className="md:col-span-2">
219-
<p className="text-[10px] font-mono font-bold uppercase tracking-widest text-muted-foreground mb-3">Most Common Outcomes</p>
220-
<div className="space-y-3">
221-
{stats.topOutcomes.map(([outcome, count]) => {
222-
const pct = stats.totalCases > 0 ? (count / stats.totalCases) * 100 : 0;
223-
return (
224-
<div key={outcome}>
225-
<div className="flex items-baseline justify-between mb-1">
226-
<span className="text-sm font-bold truncate">{outcome}</span>
227-
<span className="text-xs font-mono font-bold ml-2 shrink-0">{count}</span>
228-
</div>
229-
<div className="h-2.5 bg-border overflow-hidden">
230-
<div className="h-full bg-black transition-all duration-500" style={{ width: `${pct}%` }} />
231-
</div>
232-
</div>
233-
);
234-
})}
235-
</div>
177+
<BarSection title="Most Common Outcomes" data={stats.topOutcomes} total={stats.totalCases} />
236178
</div>
237179
</div>
238-
</section>
239180

240-
{/* Top Companies */}
241-
<section>
242-
<h2 className="text-2xl font-bold tracking-tight mb-4">\ TOP COMPANIES</h2>
243-
<div className="h-[3px] bg-border mb-4" />
181+
{/* Top Companies table */}
182+
<p className="text-[10px] font-mono font-bold uppercase tracking-widest text-muted-foreground mb-3">Top Companies</p>
244183
<div className="border-2 border-black">
245184
<table className="w-full text-sm">
246185
<thead>
@@ -266,3 +205,30 @@ export default function JurisdictionDetail({ jurisdiction }: JurisdictionDetailP
266205
</div>
267206
);
268207
}
208+
209+
/** Bar chart section with a label header and proportional bars */
210+
function BarSection({ title, data, total }: { title: string; data: [string, number][]; total: number }) {
211+
const maxCount = data.length > 0 ? data[0][1] : 1;
212+
return (
213+
<div>
214+
<p className="text-[10px] font-mono font-bold uppercase tracking-widest text-muted-foreground mb-3">{title}</p>
215+
<div className="space-y-3">
216+
{data.map(([label, count]) => {
217+
// Bar width relative to the largest item so bars are visually distinct
218+
const barPct = maxCount > 0 ? (count / maxCount) * 100 : 0;
219+
return (
220+
<div key={label}>
221+
<div className="flex items-baseline justify-between mb-1">
222+
<span className="text-sm font-bold truncate">{label}</span>
223+
<span className="text-xs font-mono font-bold ml-2 shrink-0">{count}</span>
224+
</div>
225+
<div className="h-2.5 overflow-hidden" style={{ backgroundColor: "#e5e5e5" }}>
226+
<div className="h-full bg-black transition-all duration-500" style={{ width: `${barPct}%` }} />
227+
</div>
228+
</div>
229+
);
230+
})}
231+
</div>
232+
</div>
233+
);
234+
}

0 commit comments

Comments
 (0)