Added skip diagnostics

X-Lovable-Edit-ID: edt-f62beb1b-ada6-4d4f-b7b9-e60c91317d0c
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot]
2026-04-18 02:25:43 +00:00
co-authored by renee-png
+30 -5
View File
@@ -499,6 +499,7 @@ interface ImportResult {
inserted: number;
skipped: number;
errors: string[];
skipReasons?: Record<string, number>;
}
function ImportPage() {
@@ -582,6 +583,11 @@ function ImportPage() {
const mapped: Row[] = [];
let skipped = 0;
const errors: string[] = [];
const skipReasons: Record<string, number> = {};
const bumpSkip = (reason: string) => {
skipped++;
skipReasons[reason] = (skipReasons[reason] ?? 0) + 1;
};
for (const raw of rawRows) {
const row: Row = {};
@@ -598,7 +604,19 @@ function ImportPage() {
// transform
const transformed = cfg.transform ? cfg.transform(row, ctx) : row;
if (!transformed) { skipped++; continue; }
if (!transformed) {
// Diagnose common transform-rejection reasons for clearer feedback
if (cfg.table === "homeowners" && !row.client_id && !row._clientext && !row._clientname) {
bumpSkip("no client match (missing companyid/company)");
} else if (cfg.table === "homeowners" && (row._clientext || row._clientname) && !row.client_id) {
bumpSkip("client not found in existing clients (import companies first)");
} else if ((cfg.table === "cases" || cfg.table === "invoices") && !row.client_id) {
bumpSkip("client not found");
} else {
bumpSkip("rejected by transform");
}
continue;
}
// archived → archived_at (only on tables that support it)
if ("_archived" in transformed) {
@@ -611,7 +629,7 @@ function ImportPage() {
// required check
const missing = cfg.required.find((k) => !transformed[k] && transformed[k] !== 0);
if (missing) { skipped++; continue; }
if (missing) { bumpSkip(`missing required: ${missing}`); continue; }
// attach created_by where not present
if (!transformed.created_by && !["time_entries", "expenses", "comments"].includes(cfg.table)) {
@@ -621,7 +639,7 @@ function ImportPage() {
}
if (mapped.length === 0) {
setResults((r) => ({ ...r, [cfg.key]: { total: rawRows.length, inserted: 0, skipped, errors: ["No valid rows after mapping. Check headers/required fields."] } }));
setResults((r) => ({ ...r, [cfg.key]: { total: rawRows.length, inserted: 0, skipped, errors: ["No valid rows after mapping. Check headers/required fields."], skipReasons } }));
return;
}
@@ -663,7 +681,7 @@ function ImportPage() {
if (cfg.postInsert) cfg.postInsert(inserted_rows, ctx);
const summary = { total: rawRows.length, inserted, skipped, errors };
const summary = { total: rawRows.length, inserted, skipped, errors, skipReasons };
setResults((r) => ({ ...r, [cfg.key]: summary }));
if (errors.length === 0) toast.success(`${cfg.label}: ${inserted} imported`);
else toast.error(`${cfg.label}: ${errors.length} batch error(s)`);
@@ -733,9 +751,16 @@ function ImportPage() {
</Badge>
)}
{r.skipped > 0 && (
<span className="text-muted-foreground">{r.skipped} skipped (missing required fields)</span>
<span className="text-muted-foreground">{r.skipped} skipped</span>
)}
</div>
{r.skipReasons && Object.keys(r.skipReasons).length > 0 && (
<div className="mt-2 text-xs text-muted-foreground space-y-0.5">
{Object.entries(r.skipReasons).map(([reason, count]) => (
<div key={reason}>• {count} × {reason}</div>
))}
</div>
)}
{r.errors.length > 0 && (
<div className="mt-2 text-xs text-destructive space-y-1">
{r.errors.slice(0, 3).map((e, i) => <div key={i}>• {e}</div>)}