- 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
123 lines
2.7 KiB
TypeScript
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",
|
|
};
|
|
}
|
|
},
|
|
};
|