Skip to content

Commit 21c5158

Browse files
Lexecon Devclaude
andcommitted
fix: resolve all React/TypeScript build errors for Railway deployment
- Add tsconfig.json to frontend (react-scripts requires it for .tsx files) - Move @font-face declarations from global.css to public/index.html to avoid webpack trying to resolve absolute /fonts/ paths as modules - Export missing types (RiskLevel, Outcome, VerifyFailure, DashboardFilters) from types/index.ts - Add index signature to Decision interface to satisfy Table<T extends Record<string,unknown>> - Fix render(val) calls: cast unknown val to String(val) in JSX - Make Input/Select/Alert/Checkbox optional props (error, helperText, id, title, onDismiss) optional via default values so TypeScript infers them as optional - Add style prop to SkeletonProps and spread it in the component - Fix handleKeyDown type: HTMLTableSectionElement → HTMLElement - Exclude *.test.tsx and *.stories.tsx from tsconfig to avoid missing @testing-library/react and @storybook/react type errors during build Build now produces: "The build folder is ready to be deployed." Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 93092ca commit 21c5158

9 files changed

Lines changed: 54 additions & 60 deletions

File tree

frontend/public/index.html

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@
1212
<link rel="preload" as="font" type="font/woff2" crossorigin="anonymous" href="/fonts/inter/Inter-Medium.woff2" />
1313
<link rel="preload" as="font" type="font/woff2" crossorigin="anonymous" href="/fonts/inter/Inter-SemiBold.woff2" />
1414
<link rel="preload" as="font" type="font/woff2" crossorigin="anonymous" href="/fonts/inter/Inter-Bold.woff2" />
15+
<!-- @font-face declarations (moved from CSS to avoid webpack module resolution of absolute paths) -->
16+
<style>
17+
@font-face { font-family: 'Inter'; src: url('/fonts/inter/Inter-Regular.woff2') format('woff2'); font-weight: 400; font-style: normal; font-display: swap; }
18+
@font-face { font-family: 'Inter'; src: url('/fonts/inter/Inter-Medium.woff2') format('woff2'); font-weight: 500; font-style: normal; font-display: swap; }
19+
@font-face { font-family: 'Inter'; src: url('/fonts/inter/Inter-SemiBold.woff2') format('woff2'); font-weight: 600; font-style: normal; font-display: swap; }
20+
@font-face { font-family: 'Inter'; src: url('/fonts/inter/Inter-Bold.woff2') format('woff2'); font-weight: 700; font-style: normal; font-display: swap; }
21+
@font-face { font-family: 'JetBrains Mono'; src: url('/fonts/inter/JetBrainsMono-Regular.woff2') format('woff2'); font-weight: 400; font-style: normal; font-display: swap; }
22+
@font-face { font-family: 'JetBrains Mono'; src: url('/fonts/inter/JetBrainsMono-Medium.woff2') format('woff2'); font-weight: 500; font-style: normal; font-display: swap; }
23+
</style>
1524
</head>
1625
<body>
1726
<noscript>You need to enable JavaScript to run this application.</noscript>

frontend/src/components/AuditDashboard/AuditDashboard.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ function transformEntry(entry: LedgerEntry): Decision {
6767
return {
6868
id: (d.decision_id || entry.entry_id || 'UNKNOWN').toUpperCase(),
6969
timestamp: formatTs(entry.timestamp),
70-
action: d.action || d.proposed_action || d.event_type || entry.event_type || 'unknown',
70+
action: String(d.action || d.proposed_action || d.event_type || entry.event_type || 'unknown'),
7171
actor: d.actor || d.user_id || 'system',
7272
riskLevel: inferRisk(d.risk_level ?? d.riskLevel),
7373
outcome: OUTCOME_MAP[d.decision || ''] || (d.decision as Outcome) || 'pending',
@@ -173,23 +173,23 @@ export default function AuditDashboard() {
173173
key: 'id', header: 'Decision ID', sortable: true, width: '130px',
174174
render: (val) => (
175175
<span style={{ fontFamily: 'monospace', fontSize: '0.8125rem', fontWeight: 600, color: 'var(--brand-primary)' }}>
176-
{val}
176+
{String(val)}
177177
</span>
178178
),
179179
},
180180
{
181181
key: 'timestamp', header: 'Timestamp', sortable: true, width: '180px',
182182
render: (val) => (
183183
<span style={{ fontFamily: 'monospace', fontSize: '0.8125rem', color: 'var(--text-secondary)' }}>
184-
{val}
184+
{String(val)}
185185
</span>
186186
),
187187
},
188188
{
189189
key: 'action', header: 'Action', sortable: true,
190190
render: (val, row) => (
191191
<div>
192-
<span style={{ fontSize: '0.875rem', color: 'var(--text-primary)' }}>{val}</span>
192+
<span style={{ fontSize: '0.875rem', color: 'var(--text-primary)' }}>{String(val)}</span>
193193
<br />
194194
<span style={{ fontSize: '0.75rem', color: 'var(--text-tertiary)' }}>by {(row as Decision).actor}</span>
195195
</div>
@@ -535,10 +535,13 @@ function LedgerTab({
535535
}}>
536536
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(160px, 1fr))', gap: 14 }}>
537537
<Input
538+
id="search"
538539
label="Search"
539540
placeholder="Search decisions…"
540541
value={filters.search}
541542
onChange={(e: React.ChangeEvent<HTMLInputElement>) => onFiltersChange({ ...filters, search: e.target.value })}
543+
error={false}
544+
helperText=""
542545
/>
543546
<Select
544547
label="Risk Level"
@@ -700,11 +703,11 @@ function ExportsTab() {
700703
];
701704

702705
const columns: TableColumn<typeof exports[0]>[] = [
703-
{ key: 'id', header: 'Export ID', width: '100px', render: (v) => <span style={{ fontFamily: 'monospace', fontWeight: 600, color: 'var(--brand-primary)' }}>{v}</span> },
706+
{ key: 'id', header: 'Export ID', width: '100px', render: (v) => <span style={{ fontFamily: 'monospace', fontWeight: 600, color: 'var(--brand-primary)' }}>{String(v)}</span> },
704707
{ key: 'dateRange', header: 'Date Range' },
705708
{ key: 'format', header: 'Format', width: '80px', render: (v) => <Badge variant="default" size="sm">{String(v)}</Badge> },
706709
{ key: 'entries', header: 'Entries', width: '80px', render: (v) => <span style={{ fontFamily: 'monospace' }}>{Number(v).toLocaleString()}</span> },
707-
{ key: 'size', header: 'Size', width: '80px', render: (v) => <span style={{ fontFamily: 'monospace' }}>{v}</span> },
710+
{ key: 'size', header: 'Size', width: '80px', render: (v) => <span style={{ fontFamily: 'monospace' }}>{String(v)}</span> },
708711
{ key: 'status', header: 'Status', width: '100px', render: (v) => <Badge variant={v === 'ready' ? 'success' : 'warning'} size="sm" dot={v === 'ready'}>{String(v).toUpperCase()}</Badge> },
709712
];
710713

frontend/src/design-system/components/Skeleton/Skeleton.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ interface SkeletonProps {
55
height?: string | number;
66
borderRadius?: string | number;
77
className?: string;
8+
style?: React.CSSProperties;
89
}
910

1011
const shimmerBase: React.CSSProperties = {
1112
background: 'linear-gradient(90deg, var(--bg-tertiary) 25%, var(--border) 37%, var(--bg-tertiary) 63%) 200% 0 / 400% 100%',
1213
animation: 'shimmer 1.4s ease-in-out infinite',
1314
};
1415

15-
export function Skeleton({ width = '100%', height = 16, borderRadius = 4, className = '' }: SkeletonProps) {
16+
export function Skeleton({ width = '100%', height = 16, borderRadius = 4, className = '', style }: SkeletonProps) {
1617
return (
1718
<div
1819
className={className}
@@ -21,6 +22,7 @@ export function Skeleton({ width = '100%', height = 16, borderRadius = 4, classN
2122
height,
2223
borderRadius,
2324
...shimmerBase,
25+
...style,
2426
}}
2527
aria-hidden="true"
2628
/>

frontend/src/design-system/components/Table/Table.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ export function Table<T extends Record<string, unknown>>({
112112
}, [sortKey, sortDir]);
113113

114114
// Keyboard nav: arrow keys on tbody
115-
const handleKeyDown = useCallback((e: React.KeyboardEvent<HTMLTableSectionElement>, rowIndex: number) => {
115+
const handleKeyDown = useCallback((e: React.KeyboardEvent<HTMLElement>, rowIndex: number) => {
116116
const rows = tableRef.current?.querySelectorAll('tbody tr');
117117
if (!rows) return;
118118
let target: Element | null = null;

frontend/src/design-system/lexecon-components.jsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,11 @@ export const Input = ({
141141
value,
142142
onChange,
143143
placeholder,
144-
error,
145-
helperText,
144+
error = undefined,
145+
helperText = '',
146146
disabled = false,
147147
required = false,
148-
id,
148+
id = undefined,
149149
...props
150150
}) => {
151151
const inputId = id || `input-${Math.random().toString(36).substr(2, 9)}`;
@@ -328,10 +328,10 @@ export const Badge = ({
328328
*/
329329
export const Alert = ({
330330
children,
331-
title,
331+
title = undefined,
332332
variant = 'info',
333333
dismissible = false,
334-
onDismiss,
334+
onDismiss = undefined,
335335
icon = true,
336336
...props
337337
}) => {
@@ -589,10 +589,10 @@ export const Select = ({
589589
value,
590590
onChange,
591591
placeholder = 'Select an option',
592-
error,
592+
error = undefined,
593593
disabled = false,
594594
required = false,
595-
id,
595+
id = undefined,
596596
...props
597597
}) => {
598598
const selectId = id || `select-${Math.random().toString(36).substr(2, 9)}`;
@@ -664,7 +664,7 @@ export const Checkbox = ({
664664
checked,
665665
onChange,
666666
disabled = false,
667-
id,
667+
id = undefined,
668668
...props
669669
}) => {
670670
const checkboxId = id || `checkbox-${Math.random().toString(36).substr(2, 9)}`;

frontend/src/styles/global.css

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,49 +3,7 @@
33
Unified theming layer. All components consume these vars.
44
============================================================ */
55

6-
/* ---------- Self-hosted fonts ---------- */
7-
@font-face {
8-
font-family: 'Inter';
9-
src: url('/fonts/inter/Inter-Regular.woff2') format('woff2');
10-
font-weight: 400;
11-
font-style: normal;
12-
font-display: swap;
13-
}
14-
@font-face {
15-
font-family: 'Inter';
16-
src: url('/fonts/inter/Inter-Medium.woff2') format('woff2');
17-
font-weight: 500;
18-
font-style: normal;
19-
font-display: swap;
20-
}
21-
@font-face {
22-
font-family: 'Inter';
23-
src: url('/fonts/inter/Inter-SemiBold.woff2') format('woff2');
24-
font-weight: 600;
25-
font-style: normal;
26-
font-display: swap;
27-
}
28-
@font-face {
29-
font-family: 'Inter';
30-
src: url('/fonts/inter/Inter-Bold.woff2') format('woff2');
31-
font-weight: 700;
32-
font-style: normal;
33-
font-display: swap;
34-
}
35-
@font-face {
36-
font-family: 'JetBrains Mono';
37-
src: url('/fonts/inter/JetBrainsMono-Regular.woff2') format('woff2');
38-
font-weight: 400;
39-
font-style: normal;
40-
font-display: swap;
41-
}
42-
@font-face {
43-
font-family: 'JetBrains Mono';
44-
src: url('/fonts/inter/JetBrainsMono-Medium.woff2') format('woff2');
45-
font-weight: 500;
46-
font-style: normal;
47-
font-display: swap;
48-
}
6+
/* @font-face declarations moved to public/index.html to avoid webpack resolving absolute font paths */
497

508
/* ---------- Light mode (default) ---------- */
519
:root {

frontend/src/types/governance.types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export interface LedgerEntry {
2323

2424
// Transformed decision used by the dashboard
2525
export interface Decision {
26+
[key: string]: unknown;
2627
id: string;
2728
timestamp: string;
2829
action: string;

frontend/src/types/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
export type { LedgerEntry, Decision, VerifyResponse, UsageData, ExportEntry } from './governance.types';
1+
export type { LedgerEntry, Decision, VerifyResponse, UsageData, ExportEntry, RiskLevel, Outcome, VerifyFailure, DashboardFilters } from './governance.types';
22
export type { ApiResponse, ApiError } from './api.types';
33
export type { Theme, ThemeMode } from './theme.types';

frontend/tsconfig.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"compilerOptions": {
3+
"target": "es5",
4+
"lib": ["dom", "dom.iterable", "esnext"],
5+
"allowJs": true,
6+
"skipLibCheck": true,
7+
"esModuleInterop": true,
8+
"allowSyntheticDefaultImports": true,
9+
"strict": false,
10+
"forceConsistentCasingInFileNames": true,
11+
"noFallthroughCasesInSwitch": true,
12+
"module": "esnext",
13+
"moduleResolution": "node",
14+
"resolveJsonModule": true,
15+
"isolatedModules": true,
16+
"noEmit": true,
17+
"jsx": "react-jsx"
18+
},
19+
"include": ["src"],
20+
"exclude": ["src/**/*.test.ts", "src/**/*.test.tsx", "src/**/*.spec.ts", "src/**/*.spec.tsx", "src/**/*.stories.ts", "src/**/*.stories.tsx", "node_modules"]
21+
}

0 commit comments

Comments
 (0)