- Implement maintenance tool for adding, logging, and retrieving tasks - Create meals tool for managing recipes, meal plans, and shopping lists - Introduce reparse metadata tool for updating thought metadata - Add household knowledge, home maintenance, family calendar, meal planning, and professional CRM database migrations - Grant necessary permissions for new database tables
72 lines
3.1 KiB
PL/PgSQL
72 lines
3.1 KiB
PL/PgSQL
-- Extension 5: Professional CRM
|
|
-- Contacts, interaction logs, and opportunities (single-user, no RLS)
|
|
|
|
CREATE TABLE IF NOT EXISTS professional_contacts (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
name TEXT NOT NULL,
|
|
company TEXT,
|
|
title TEXT,
|
|
email TEXT,
|
|
phone TEXT,
|
|
linkedin_url TEXT,
|
|
how_we_met TEXT,
|
|
tags TEXT[] NOT NULL DEFAULT '{}',
|
|
notes TEXT,
|
|
last_contacted TIMESTAMPTZ,
|
|
follow_up_date DATE,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS contact_interactions (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
contact_id UUID NOT NULL REFERENCES professional_contacts(id) ON DELETE CASCADE,
|
|
interaction_type TEXT NOT NULL CHECK (interaction_type IN ('meeting', 'email', 'call', 'coffee', 'event', 'linkedin', 'other')),
|
|
occurred_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
summary TEXT NOT NULL,
|
|
follow_up_needed BOOLEAN NOT NULL DEFAULT false,
|
|
follow_up_notes TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS opportunities (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
contact_id UUID REFERENCES professional_contacts(id) ON DELETE SET NULL,
|
|
title TEXT NOT NULL,
|
|
description TEXT,
|
|
stage TEXT NOT NULL DEFAULT 'identified' CHECK (stage IN ('identified', 'in_conversation', 'proposal', 'negotiation', 'won', 'lost')),
|
|
value DECIMAL(12, 2),
|
|
expected_close_date DATE,
|
|
notes TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_contacts_last_contacted ON professional_contacts(last_contacted);
|
|
CREATE INDEX IF NOT EXISTS idx_contacts_follow_up ON professional_contacts(follow_up_date) WHERE follow_up_date IS NOT NULL;
|
|
CREATE INDEX IF NOT EXISTS idx_interactions_contact ON contact_interactions(contact_id, occurred_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_opportunities_stage ON opportunities(stage);
|
|
|
|
DROP TRIGGER IF EXISTS update_professional_contacts_updated_at ON professional_contacts;
|
|
CREATE TRIGGER update_professional_contacts_updated_at
|
|
BEFORE UPDATE ON professional_contacts
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
DROP TRIGGER IF EXISTS update_opportunities_updated_at ON opportunities;
|
|
CREATE TRIGGER update_opportunities_updated_at
|
|
BEFORE UPDATE ON opportunities
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
CREATE OR REPLACE FUNCTION update_last_contacted()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
UPDATE professional_contacts SET last_contacted = NEW.occurred_at WHERE id = NEW.contact_id;
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
DROP TRIGGER IF EXISTS update_contact_last_contacted ON contact_interactions;
|
|
CREATE TRIGGER update_contact_last_contacted
|
|
AFTER INSERT ON contact_interactions
|
|
FOR EACH ROW EXECUTE FUNCTION update_last_contacted();
|