Skip to content
Merged
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
34 changes: 34 additions & 0 deletions csv-table-hub-main/src/__tests__/files-refresh.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// src/__tests__/files-refresh.test.ts
// Regression test for #64: after an upload, router.invalidate() re-runs the
// home route loader. The loader must refetch the files list rather than hand
// back the cached one, or the new file only appears after navigating away.

import { describe, it, expect, vi, afterEach } from "vitest";
import { QueryClient } from "@tanstack/react-query";
import { filesQuery, loadFiles } from "@/lib/csv.functions";

const file = (name: string) => ({
id: name,
file_name: name,
table_name: `csv_${name}`,
row_count: 1,
column_names: ["a"],
created_at: "2026-10-26T00:00:00Z",
});

afterEach(() => vi.unstubAllGlobals());

describe("loadFiles", () => {
it("refetches the files list when the loader re-runs", async () => {
const responses = [[file("a")], [file("a"), file("b")]];
const fetchMock = vi.fn(async () => new Response(JSON.stringify(responses.shift())));
vi.stubGlobal("fetch", fetchMock);
const qc = new QueryClient();

await loadFiles(qc); // first page load
await loadFiles(qc); // router.invalidate() after an upload

await vi.waitFor(() => expect(qc.getQueryData(filesQuery.queryKey)).toHaveLength(2));
expect(fetchMock).toHaveBeenCalledTimes(2);
});
});
8 changes: 8 additions & 0 deletions csv-table-hub-main/src/lib/csv.functions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { z } from "zod";
import type { QueryClient } from "@tanstack/react-query";

const columnTypeSchema = z.enum(["int8", "numeric", "date", "timestamptz", "boolean", "text"]);
export type ColumnType = z.infer<typeof columnTypeSchema>;
Expand Down Expand Up @@ -163,6 +164,13 @@ export async function listCsvFiles(): Promise<CsvFileSummary[]> {
return apiFetch("/api/csv/files", csvFileSummaryListSchema);
}

export const filesQuery = { queryKey: ["csv-files"], queryFn: listCsvFiles };

// Route loader for the files list. revalidateIfStale makes router.invalidate()
// (called after each upload) refetch instead of returning the cached list (#64).
export const loadFiles = (queryClient: QueryClient) =>
queryClient.ensureQueryData({ ...filesQuery, revalidateIfStale: true });

const csvPreviewResultSchema = z.discriminatedUnion("status", [
z.object({
status: z.literal("ok"),
Expand Down
8 changes: 2 additions & 6 deletions csv-table-hub-main/src/routes/_authenticated/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { createFileRoute, Link, useRouter } from "@tanstack/react-router";
import { useQuery, useSuspenseQuery } from "@tanstack/react-query";
import { useCallback, useEffect, useRef, useState, type ReactNode } from "react";
import {
listCsvFiles, previewCsvTable, previewCsv, listTeTables, uploadCsv,
filesQuery, loadFiles, previewCsvTable, previewCsv, listTeTables, uploadCsv,
type CsvFileSummary, type ColumnType, type TeTableInfo,
} from "@/lib/csv.functions";
import { parseCsvPreview, COLUMN_TYPE_LABEL, sanitizeColumns, type CsvPreview } from "@/lib/csv-preview";
Expand Down Expand Up @@ -59,13 +59,9 @@ import {
import { Progress } from "@/components/ui/progress";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";

// ─── query ───────────────────────────────────────────────────────────────────

const filesQuery = { queryKey: ["csv-files"], queryFn: () => listCsvFiles() };

export const Route = createFileRoute("/_authenticated/")({
head: () => ({ meta: [{ title: "CSV Migrator — Upload CSV files to your database" }, { name: "description", content: "Upload any number of CSV files. Each becomes its own database table, with duplicates automatically skipped." }, { property: "og:title", content: "CSV Migrator" }] }),
loader: ({ context }) => context.queryClient.ensureQueryData(filesQuery),
loader: ({ context }) => loadFiles(context.queryClient),
component: Home,
});

Expand Down
Loading