Skip to content

Commit 9892cf8

Browse files
break into components
1 parent b1d2429 commit 9892cf8

File tree

10 files changed

+950
-984
lines changed

10 files changed

+950
-984
lines changed
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import { SectionToggle } from './SectionToggle'
2+
import { type TableDiffData, type SampleValue, themeColors } from './types'
3+
4+
interface ColumnStatsSectionProps {
5+
columnStats: TableDiffData['row_diff']['column_stats']
6+
expanded: boolean
7+
onToggle: () => void
8+
}
9+
10+
export function ColumnStatsSection({
11+
columnStats,
12+
expanded,
13+
onToggle,
14+
}: ColumnStatsSectionProps) {
15+
if (Object.keys(columnStats || {}).length === 0) {
16+
return null
17+
}
18+
19+
// Get the first stats object to determine the column headers
20+
const firstStatsValue = Object.values(columnStats)[0]
21+
const statKeys =
22+
firstStatsValue && typeof firstStatsValue === 'object'
23+
? Object.keys(firstStatsValue as Record<string, SampleValue>)
24+
: []
25+
26+
return (
27+
<SectionToggle
28+
id="columnStats"
29+
title="Column Statistics"
30+
badge={`${Object.keys(columnStats).length} columns`}
31+
badgeStyle={{
32+
backgroundColor: 'var(--vscode-input-background)',
33+
color: 'var(--vscode-symbolIcon-classForeground, #9b59b6)',
34+
borderColor: 'var(--vscode-symbolIcon-classForeground, #9b59b6)',
35+
}}
36+
expanded={expanded}
37+
onToggle={onToggle}
38+
>
39+
<div className="px-8 py-3">
40+
<div className="overflow-auto max-h-80">
41+
<table className="w-full text-xs table-fixed">
42+
<thead
43+
className="sticky top-0 z-10"
44+
style={{
45+
backgroundColor: 'var(--vscode-editor-background)',
46+
}}
47+
>
48+
<tr
49+
style={{
50+
borderBottom: `1px solid ${themeColors.border}`,
51+
}}
52+
>
53+
<th
54+
className="text-left py-2 pr-2 font-medium w-28"
55+
style={{ color: themeColors.muted }}
56+
>
57+
Column
58+
</th>
59+
{statKeys.map(stat => (
60+
<th
61+
key={stat}
62+
className="text-left py-2 px-1 font-medium w-16"
63+
title={stat}
64+
style={{ color: themeColors.muted }}
65+
>
66+
{stat.length > 6 ? stat.slice(0, 6) + '..' : stat}
67+
</th>
68+
))}
69+
</tr>
70+
</thead>
71+
<tbody>
72+
{Object.entries(columnStats).map(([col, statsValue]) => (
73+
<tr
74+
key={col}
75+
className="transition-colors"
76+
style={{
77+
borderBottom: `1px solid ${themeColors.border}`,
78+
}}
79+
onMouseEnter={e => {
80+
e.currentTarget.style.backgroundColor =
81+
'var(--vscode-list-hoverBackground)'
82+
}}
83+
onMouseLeave={e => {
84+
e.currentTarget.style.backgroundColor = 'transparent'
85+
}}
86+
>
87+
<td
88+
className="py-2 pr-2 font-mono truncate"
89+
title={col}
90+
>
91+
{col}
92+
</td>
93+
{statsValue && typeof statsValue === 'object'
94+
? Object.values(
95+
statsValue as Record<string, SampleValue>,
96+
).map((value: SampleValue, idx: number) => (
97+
<td
98+
key={idx}
99+
className="py-2 px-1 font-mono text-xs truncate"
100+
title={String(value)}
101+
style={{ color: themeColors.muted }}
102+
>
103+
{typeof value === 'number'
104+
? value.toFixed(1)
105+
: String(value).length > 8
106+
? String(value).slice(0, 8) + '..'
107+
: String(value)}
108+
</td>
109+
))
110+
: [
111+
<td
112+
key="single-value"
113+
className="py-2 px-1 font-mono text-xs truncate"
114+
title={String(statsValue)}
115+
style={{ color: themeColors.muted }}
116+
>
117+
{typeof statsValue === 'number'
118+
? statsValue.toFixed(1)
119+
: String(statsValue)}
120+
</td>,
121+
]}
122+
</tr>
123+
))}
124+
</tbody>
125+
</table>
126+
</div>
127+
</div>
128+
</SectionToggle>
129+
)
130+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { SectionToggle } from './SectionToggle'
2+
import { type TableDiffData, themeColors } from './types'
3+
4+
interface RowStatsSectionProps {
5+
rowDiff: TableDiffData['row_diff']
6+
expanded: boolean
7+
onToggle: () => void
8+
}
9+
10+
export function RowStatsSection({
11+
rowDiff,
12+
expanded,
13+
onToggle,
14+
}: RowStatsSectionProps) {
15+
const formatPercentage = (v: number) => `${(v * 100).toFixed(1)}%`
16+
const formatCount = (v: number) => v.toLocaleString()
17+
18+
const fullMatchCount = Math.round(rowDiff.stats.full_match_count || 0)
19+
const joinCount = Math.round(rowDiff.stats.join_count || 0)
20+
const partialMatchCount = joinCount - fullMatchCount
21+
const sOnlyCount = Math.round(rowDiff.stats.s_only_count || 0)
22+
const tOnlyCount = Math.round(rowDiff.stats.t_only_count || 0)
23+
const totalRows = rowDiff.source_count + rowDiff.target_count
24+
const fullMatchPct = totalRows > 0 ? (2 * fullMatchCount) / totalRows : 0
25+
26+
return (
27+
<SectionToggle
28+
id="rows"
29+
title="Row Statistics"
30+
badge={`${formatPercentage(fullMatchPct)} match rate`}
31+
badgeStyle={{
32+
backgroundColor: 'var(--vscode-input-background)',
33+
color: themeColors.info,
34+
borderColor: themeColors.info,
35+
}}
36+
expanded={expanded}
37+
onToggle={onToggle}
38+
>
39+
<div className="px-8 py-3 space-y-3">
40+
<div className="grid grid-cols-2 gap-4 text-xs">
41+
<div className="space-y-1">
42+
<div className="flex justify-between">
43+
<span style={{ color: themeColors.success }}>✓ Full Matches</span>
44+
<span className="font-medium">{formatCount(fullMatchCount)}</span>
45+
</div>
46+
<div className="flex justify-between">
47+
<span style={{ color: themeColors.info }}>~ Partial Matches</span>
48+
<span className="font-medium">
49+
{formatCount(partialMatchCount)}
50+
</span>
51+
</div>
52+
</div>
53+
<div className="space-y-1">
54+
<div className="flex justify-between">
55+
<span style={{ color: themeColors.warning }}>+ Source Only</span>
56+
<span className="font-medium">{formatCount(sOnlyCount)}</span>
57+
</div>
58+
<div className="flex justify-between">
59+
<span style={{ color: themeColors.error }}>- Target Only</span>
60+
<span className="font-medium">{formatCount(tOnlyCount)}</span>
61+
</div>
62+
</div>
63+
</div>
64+
{/* Match rate progress bar */}
65+
<div className="mt-3 space-y-1">
66+
<div
67+
className="flex items-center gap-2 text-xs"
68+
style={{ color: themeColors.muted }}
69+
>
70+
<span>Match Rate</span>
71+
<span className="font-medium">
72+
{formatPercentage(fullMatchPct)}
73+
</span>
74+
</div>
75+
<div
76+
className="h-2 rounded-full overflow-hidden"
77+
style={{ backgroundColor: 'var(--vscode-input-background)' }}
78+
>
79+
<div
80+
className="h-full transition-all duration-300"
81+
style={{
82+
width: `${fullMatchPct * 100}%`,
83+
backgroundColor: themeColors.success,
84+
}}
85+
/>
86+
</div>
87+
</div>
88+
</div>
89+
</SectionToggle>
90+
)
91+
}

0 commit comments

Comments
 (0)