Skip to content
This repository was archived by the owner on Aug 10, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/quality-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '24'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json

Expand Down
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,14 @@ terraform-provider-*.log

*.zip

# Large / non-source assets and local artifacts
*.docx
*.pdf
*.png
*.ico
*.patch
*.svg
test-artifacts/

# Serena tool workspace
.serena/
Binary file removed CSV_Loading_Guide.docx
Binary file not shown.
Binary file removed Pending Items 28072026.docx
Binary file not shown.
Binary file removed PostgresDataMigration.ico
Binary file not shown.
Binary file removed csv-table-hub-main.zip
Binary file not shown.
Binary file removed favicon-16.png
Binary file not shown.
Binary file removed favicon-32.png
Binary file not shown.
Binary file removed favicon.ico
Binary file not shown.
Binary file removed frontend/public/favicon.ico
Binary file not shown.
4 changes: 3 additions & 1 deletion frontend/src/hooks/use-local-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { useCallback, useEffect, useRef, useState } from "react";

const KEY = "csv-migrator:jobs:v1";

export function useLocalStorageState<T>(defaultValue: T): [T, (updater: T | ((prev: T) => T)) => void, () => void] {
export function useLocalStorageState<T>(
defaultValue: T,
): [T, (updater: T | ((prev: T) => T)) => void, () => void] {
const [state, setState] = useState<T>(defaultValue);
const hydrated = useRef(false);

Expand Down
86 changes: 71 additions & 15 deletions frontend/src/lib/csv-preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,49 @@
const ch = src[i];
if (inQuotes) {
if (ch === '"') {
if (src[i + 1] === '"') { field += '"'; i += 2; continue; }
inQuotes = false; i++; continue;
if (src[i + 1] === '"') {
field += '"';
i += 2;
continue;
}
inQuotes = false;
i++;
continue;
}
field += ch; i++; continue;
field += ch;
i++;
continue;
}
if (ch === '"') { inQuotes = true; i++; continue; }
if (ch === ",") { row.push(field); field = ""; i++; continue; }
if (ch === "\r") { i++; continue; }
if (ch === "\n") { row.push(field); rows.push(row); row = []; field = ""; i++; continue; }
field += ch; i++;
if (ch === '"') {
inQuotes = true;
i++;
continue;
}
if (ch === ",") {
row.push(field);
field = "";
i++;
continue;
}
if (ch === "\r") {
i++;
continue;
}
if (ch === "\n") {
row.push(field);
rows.push(row);
row = [];
field = "";
i++;
continue;
}
field += ch;
i++;
}
if (field.length > 0 || row.length > 0) {
row.push(field);
rows.push(row);
}
if (field.length > 0 || row.length > 0) { row.push(field); rows.push(row); }
while (rows.length && rows[rows.length - 1].every((c) => c === "")) rows.pop();
return rows;
}
Expand All @@ -44,7 +75,7 @@
const seen = new Map<string, number>();
return headers.map((h, idx) => {
let base = (h || `column_${idx + 1}`)
.toLowerCase().trim()

Check failure on line 78 in frontend/src/lib/csv-preview.ts

View workflow job for this annotation

GitHub Actions / frontend (build + lint)

Insert `⏎······`
.replace(/[^a-z0-9_]+/g, "_")
.replace(/^_+|_+$/g, "");
if (!base) base = `column_${idx + 1}`;
Expand Down Expand Up @@ -91,7 +122,10 @@
return "text";
}

export async function parseCsvPreview(file: File, opts?: { sampleRows?: number; maxBytes?: number }): Promise<CsvPreview> {
export async function parseCsvPreview(
file: File,
opts?: { sampleRows?: number; maxBytes?: number },
): Promise<CsvPreview> {
const maxBytes = opts?.maxBytes ?? 512 * 1024; // 512KB for preview
const sampleCount = opts?.sampleRows ?? 10;
const inferRowLimit = 200;
Expand All @@ -105,8 +139,14 @@

if (rows.length === 0) {
return {
headers: [], sanitizedHeaders: [], sampleRows: [], inferredTypes: [],
totalRowsApprox: 0, bytesRead: slice.size, bytesTotal: file.size, truncated,
headers: [],
sanitizedHeaders: [],
sampleRows: [],
inferredTypes: [],
totalRowsApprox: 0,
bytesRead: slice.size,
bytesTotal: file.size,
truncated,
};
}

Expand All @@ -116,7 +156,17 @@
const sample = dataRows.slice(0, sampleCount);

// Infer types
const perCol: Set<ColumnType>[] = headers.map(() => new Set(["int8", "numeric", "date", "timestamptz", "boolean", "text"]));
const perCol: Set<ColumnType>[] = headers.map(
() =>

Check failure on line 160 in frontend/src/lib/csv-preview.ts

View workflow job for this annotation

GitHub Actions / frontend (build + lint)

Replace `⏎······new·Set([⏎········"int8",⏎········"numeric",⏎········"date",⏎········"timestamptz",⏎········"boolean",⏎········"text",⏎······` with `·new·Set(["int8",·"numeric",·"date",·"timestamptz",·"boolean",·"text"`
new Set([
"int8",
"numeric",
"date",
"timestamptz",
"boolean",
"text",
]),
);
for (let r = 0; r < Math.min(dataRows.length, inferRowLimit); r++) {
const row = dataRows[r];
for (let c = 0; c < headers.length; c++) {
Expand All @@ -138,8 +188,14 @@
}

return {
headers, sanitizedHeaders: sanitized, sampleRows: sample, inferredTypes,
totalRowsApprox, bytesRead: slice.size, bytesTotal: file.size, truncated,
headers,
sanitizedHeaders: sanitized,
sampleRows: sample,
inferredTypes,
totalRowsApprox,
bytesRead: slice.size,
bytesTotal: file.size,
truncated,
};
}

Expand Down
9 changes: 8 additions & 1 deletion frontend/src/routes/_authenticated/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,14 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Badge } from "@/components/ui/badge";
import { toast, Toaster } from "sonner";
import {
Expand Down
Loading
Loading