feat(10-redo-C): CRM leads module — schema, services, UI (Phase 10 redo)
Stage C of staged Phase 10 redo. Restores dangling phase10-wip work: - schema.ts: leads expansion + activities/reminders tables + relations - lead-service.ts / lead-validators.ts service layer - /admin/leads list + detail + actions, LeadTable/LeadDetail/LeadForm - LogActivityModal, SendQuoteModal, FollowUpWidget on dashboard - migration 0005 (applied to prod DB in Stage B, along with previously-missing 0001/0003/0004 — root cause of all post-deploy 500s: prod DB had no migrations applied since 0000) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
import postgres from "postgres";
|
||||
import { readFileSync } from "fs";
|
||||
import { join } from "path";
|
||||
|
||||
// Applies 0005_phase_10_crm_leads_activities_reminders.sql (additive-only:
|
||||
// ALTER TABLE leads ADD COLUMN x8, CREATE TABLE activities/reminders, indexes).
|
||||
// Idempotent: skips if already applied. Never drops or truncates anything.
|
||||
|
||||
async function push() {
|
||||
const databaseUrl = process.env.DATABASE_URL;
|
||||
if (!databaseUrl) {
|
||||
console.error("DATABASE_URL environment variable is required");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const sql = postgres(databaseUrl, { max: 1 });
|
||||
|
||||
try {
|
||||
const cols = await sql`
|
||||
SELECT column_name FROM information_schema.columns
|
||||
WHERE table_name = 'leads' ORDER BY ordinal_position
|
||||
`;
|
||||
const tables = await sql`
|
||||
SELECT table_name FROM information_schema.tables
|
||||
WHERE table_schema = 'public' AND table_name IN ('activities', 'reminders')
|
||||
`;
|
||||
const colNames = cols.map((c) => c.column_name);
|
||||
console.log("Pre-state — leads columns:", colNames.join(", "));
|
||||
console.log(
|
||||
"Pre-state — CRM tables:",
|
||||
tables.map((t) => t.table_name).join(", ") || "none"
|
||||
);
|
||||
|
||||
if (colNames.includes("status") && tables.length === 2) {
|
||||
console.log("✓ Migration 0005 already applied (skipped)");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const migrationSql = readFileSync(
|
||||
join(
|
||||
__dirname,
|
||||
"../src/db/migrations/0005_phase_10_crm_leads_activities_reminders.sql"
|
||||
),
|
||||
"utf-8"
|
||||
);
|
||||
|
||||
console.log("Applying migration 0005 in a transaction...");
|
||||
await sql.begin(async (tx) => {
|
||||
await tx.unsafe(migrationSql);
|
||||
});
|
||||
|
||||
const postCols = await sql`
|
||||
SELECT column_name FROM information_schema.columns
|
||||
WHERE table_name = 'leads' ORDER BY ordinal_position
|
||||
`;
|
||||
const postTables = await sql`
|
||||
SELECT table_name FROM information_schema.tables
|
||||
WHERE table_schema = 'public' AND table_name IN ('activities', 'reminders')
|
||||
`;
|
||||
console.log(
|
||||
"Post-state — leads columns:",
|
||||
postCols.map((c) => c.column_name).join(", ")
|
||||
);
|
||||
console.log(
|
||||
"Post-state — CRM tables:",
|
||||
postTables.map((t) => t.table_name).join(", ")
|
||||
);
|
||||
console.log("✓ Migration 0005 applied successfully");
|
||||
process.exit(0);
|
||||
} catch (err: unknown) {
|
||||
console.error(
|
||||
"Error applying migration:",
|
||||
err instanceof Error ? err.message : err
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
push();
|
||||
Reference in New Issue
Block a user