echos-ocr/src/lib/integrations/providers/csv-export.ts
Randall Stillwell a975043670 Add onboarding wizard, email verification, integration architecture, and settings restructure
- Auto-sign-in after registration instead of redirect to login
- Email verification system with token generation, send/confirm API routes, and persistent banner
- 7-step onboarding wizard (org, location, services, upload source, AI, integrations, complete)
- Middleware redirects owners with incomplete onboarding to /onboarding
- Integration provider plugin architecture with registry and 6 providers (Planning Center, Monday.com, Airtable, Google Sheets, Webhook, CSV Export)
- Full integration CRUD API with test, sync, fields, and OAuth authorize/callback routes
- Refactored fireIntegrationEvent to use Integration model with legacy AppSettings fallback
- Migration script for existing Monday.com/webhook config to Integration rows
- Settings page restructured from monolithic 1290-line file into focused sub-routes with section navigation
- Integration hub UI with provider tiles, connect flow, and individual config pages
- Post-onboarding contextual guidance cards on dashboard with dismissible hints
- Schema: Integration model, onboardingComplete/onboardingStep on Organization, dismissedHints on OrgMember

Made-with: Cursor
2026-04-15 01:29:13 -05:00

123 lines
2.7 KiB
TypeScript

import type {
IntegrationProvider,
CardData,
TestResult,
PushResult,
JsonValue,
} from "../types";
const DEFAULT_COLUMNS = [
"name",
"email",
"cellPhone",
"homePhone",
"address",
"city",
"state",
"zip",
"gender",
"dateOfBirth",
"maritalStatus",
"visitType",
"prayerRequests",
"followUp",
"notes",
"serviceAttended",
"firstTimeGuestDate",
"salvationDate",
];
interface CsvConfig {
format: "csv" | "tsv";
columns: string[];
includeHeaders: boolean;
}
function parseConfig(config: JsonValue): CsvConfig {
const c = config as Record<string, unknown>;
return {
format: (c.format as "csv" | "tsv") || "csv",
columns: (c.columns as string[]) || DEFAULT_COLUMNS,
includeHeaders: c.includeHeaders !== false,
};
}
function escapeCell(val: string, delimiter: string): string {
if (
val.includes(delimiter) ||
val.includes('"') ||
val.includes("\n")
) {
return `"${val.replace(/"/g, '""')}"`;
}
return val;
}
export function cardToCsvRow(
card: CardData,
columns: string[],
delimiter: string
): string {
return columns
.map((col) => {
const val = card[col];
if (val === null || val === undefined) return "";
if (val instanceof Date) return val.toISOString().split("T")[0];
if (typeof val === "object") return escapeCell(JSON.stringify(val), delimiter);
return escapeCell(String(val), delimiter);
})
.join(delimiter);
}
export const csvExportProvider: IntegrationProvider = {
id: "csv_export",
name: "CSV / Excel Export",
description: "Generate CSV or TSV files from processed cards",
icon: "csv_export",
category: "export",
supportsFieldMapping: false,
supportsOAuth: false,
configFields: [
{
key: "format",
label: "Format",
type: "select",
options: [
{ value: "csv", label: "CSV (comma-separated)" },
{ value: "tsv", label: "TSV (tab-separated, Excel-friendly)" },
],
},
{
key: "includeHeaders",
label: "Include column headers",
type: "boolean",
},
],
async testConnection(): Promise<TestResult> {
return {
success: true,
message: "CSV export is always available — no external connection needed",
};
},
async pushCard(
card: CardData,
config: JsonValue,
): Promise<PushResult> {
try {
const { format, columns } = parseConfig(config);
const delimiter = format === "tsv" ? "\t" : ",";
const row = cardToCsvRow(card, columns, delimiter);
return {
success: true,
message: `Generated ${format.toUpperCase()} row (${row.length} chars)`,
};
} catch (err) {
return {
success: false,
message: err instanceof Error ? err.message : "Export failed",
};
}
},
};