diff --git a/src/db/schema.ts b/src/db/schema.ts index 0a68a1f..bd8363b 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -362,14 +362,62 @@ export const quotes = pgTable("quotes", { .defaultNow(), }); -// ============ LEADS TABLE (CRM — Phase 10, but needed for quotes FK) ============ -// Placeholder table for Phase 10 CRM integration +// ============ LEADS TABLE (CRM — Phase 10) ============ export const leads = pgTable("leads", { id: text("id") .primaryKey() .$defaultFn(() => nanoid()), name: text("name").notNull(), email: text("email"), + phone: text("phone"), + company: text("company"), + status: text("status") + .notNull() + .default("contacted"), // contacted | qualified | proposal_sent | negotiating | won | lost + last_contact_date: timestamp("last_contact_date", { withTimezone: true }), + next_action: text("next_action"), + next_action_date: timestamp("next_action_date", { withTimezone: true }), + notes: text("notes"), + created_at: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + updated_at: timestamp("updated_at", { withTimezone: true }) + .notNull() + .defaultNow(), +}); + +// ============ ACTIVITIES TABLE (CRM interaction history) ============ +export const activities = pgTable("activities", { + id: text("id") + .primaryKey() + .$defaultFn(() => nanoid()), + lead_id: text("lead_id") + .notNull() + .references(() => leads.id, { onDelete: "cascade" }), + type: text("type") + .notNull(), // call | email | meeting | note + duration_minutes: integer("duration_minutes"), // optional, for calls/meetings + notes: text("notes").notNull(), + activity_date: timestamp("activity_date", { withTimezone: true }) + .notNull(), + created_at: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), +}); + +// ============ REMINDERS TABLE (CRM follow-up reminders) ============ +export const reminders = pgTable("reminders", { + id: text("id") + .primaryKey() + .$defaultFn(() => nanoid()), + lead_id: text("lead_id") + .notNull() + .references(() => leads.id, { onDelete: "cascade" }), + title: text("title").notNull(), + description: text("description"), + due_date: timestamp("due_date", { withTimezone: true }) + .notNull(), + completed_at: timestamp("completed_at", { withTimezone: true }), created_at: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), @@ -497,6 +545,16 @@ export const offerPhaseServicesRelations = relations(offer_phase_services, ({ on export const leadsRelations = relations(leads, ({ many }) => ({ quotes: many(quotes), + activities: many(activities), + reminders: many(reminders), +})); + +export const activitiesRelations = relations(activities, ({ one }) => ({ + lead: one(leads, { fields: [activities.lead_id], references: [leads.id] }), +})); + +export const remindersRelations = relations(reminders, ({ one }) => ({ + lead: one(leads, { fields: [reminders.lead_id], references: [leads.id] }), })); export const quotesRelations = relations(quotes, ({ one, many }) => ({ @@ -553,4 +611,8 @@ export type NewOfferPhaseService = typeof offer_phase_services.$inferInsert; export type Quote = typeof quotes.$inferSelect; export type NewQuote = typeof quotes.$inferInsert; export type Lead = typeof leads.$inferSelect; -export type NewLead = typeof leads.$inferInsert; \ No newline at end of file +export type NewLead = typeof leads.$inferInsert; +export type Activity = typeof activities.$inferSelect; +export type NewActivity = typeof activities.$inferInsert; +export type Reminder = typeof reminders.$inferSelect; +export type NewReminder = typeof reminders.$inferInsert; \ No newline at end of file