Export CSV based on currently visible columns instead of hardcoded list
- CSV export now respects column visibility settings, exporting only the columns the user has toggled on - Boolean fields export as "Yes"/"No", arrays as comma-separated values - Toast message shows row and column counts for confirmation Made-with: Cursor
This commit is contained in:
parent
ec51c049c3
commit
05a40cca51
1 changed files with 21 additions and 13 deletions
|
|
@ -6,7 +6,7 @@ import { toast } from "sonner";
|
||||||
|
|
||||||
import { StatCards, type StatFilter } from "./stat-cards";
|
import { StatCards, type StatFilter } from "./stat-cards";
|
||||||
import { Filters } from "./filters";
|
import { Filters } from "./filters";
|
||||||
import { DataTable, getDefaultColumnVisibility } from "./data-table";
|
import { DataTable, getDefaultColumnVisibility, ALL_TOGGLEABLE_COLUMNS } from "./data-table";
|
||||||
import { SelectionToolbar } from "./selection-toolbar";
|
import { SelectionToolbar } from "./selection-toolbar";
|
||||||
import { UploadModal, type UploadingFile } from "./upload-modal";
|
import { UploadModal, type UploadingFile } from "./upload-modal";
|
||||||
import { createColumns, COPYABLE_FIELDS, type ResponseCard } from "./columns";
|
import { createColumns, COPYABLE_FIELDS, type ResponseCard } from "./columns";
|
||||||
|
|
@ -173,17 +173,25 @@ export function DashboardContent() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const headers = [
|
const visibleCols = ALL_TOGGLEABLE_COLUMNS.filter(
|
||||||
"Name", "Email", "Phone", "City", "State", "Gender",
|
(col) => columnVisibility[col.id] !== false
|
||||||
"Visit Type", "Attendance", "Service", "OCR Status", "Review Status",
|
);
|
||||||
"Confidence",
|
|
||||||
];
|
if (visibleCols.length === 0) {
|
||||||
const rows = data.map((c) => [
|
toast.error("No columns visible to export");
|
||||||
c.name || "", c.email || "", c.cellPhone || "", c.city || "",
|
return;
|
||||||
c.state || "", c.gender || "", c.visitType || "",
|
}
|
||||||
c.attendanceDuration || "", c.serviceAttended || "", c.ocrStatus,
|
|
||||||
c.reviewStatus, c.ocrConfidence?.toString() || "",
|
const headers = visibleCols.map((c) => c.label);
|
||||||
]);
|
const rows = data.map((card) =>
|
||||||
|
visibleCols.map((col) => {
|
||||||
|
const val = (card as Record<string, unknown>)[col.id];
|
||||||
|
if (val == null) return "";
|
||||||
|
if (typeof val === "boolean") return val ? "Yes" : "No";
|
||||||
|
if (Array.isArray(val)) return val.filter(Boolean).join(", ");
|
||||||
|
return String(val);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const csv = [headers, ...rows]
|
const csv = [headers, ...rows]
|
||||||
.map((r) => r.map((v) => `"${v.replace(/"/g, '""')}"`).join(","))
|
.map((r) => r.map((v) => `"${v.replace(/"/g, '""')}"`).join(","))
|
||||||
|
|
@ -196,7 +204,7 @@ export function DashboardContent() {
|
||||||
a.download = `response-cards-${new Date().toISOString().split("T")[0]}.csv`;
|
a.download = `response-cards-${new Date().toISOString().split("T")[0]}.csv`;
|
||||||
a.click();
|
a.click();
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
toast.success("CSV exported");
|
toast.success(`Exported ${data.length} rows × ${visibleCols.length} columns`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const columns = React.useMemo(
|
const columns = React.useMemo(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue