feat(tools): add maintenance and meal planning tools with CRUD operations
- 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
This commit is contained in:
@@ -1,7 +0,0 @@
|
||||
-- Grant these permissions to the database role used by the application.
|
||||
-- Replace amcs_user with the actual role in your deployment before applying.
|
||||
grant ALL ON TABLE public.thoughts to amcs;
|
||||
grant ALL ON TABLE public.projects to amcs;
|
||||
grant ALL ON TABLE public.thought_links to amcs;
|
||||
grant ALL ON TABLE public.embeddings to amcs;
|
||||
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO amcs;
|
||||
42
migrations/011_household_knowledge.sql
Normal file
42
migrations/011_household_knowledge.sql
Normal file
@@ -0,0 +1,42 @@
|
||||
-- Extension 1: Household Knowledge Base
|
||||
-- Stores household facts and vendor contacts (single-user, no RLS)
|
||||
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = now();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS household_items (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name TEXT NOT NULL,
|
||||
category TEXT,
|
||||
location TEXT,
|
||||
details JSONB NOT NULL DEFAULT '{}',
|
||||
notes TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS household_vendors (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name TEXT NOT NULL,
|
||||
service_type TEXT,
|
||||
phone TEXT,
|
||||
email TEXT,
|
||||
website TEXT,
|
||||
notes TEXT,
|
||||
rating INTEGER CHECK (rating >= 1 AND rating <= 5),
|
||||
last_used DATE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_household_items_category ON household_items(category);
|
||||
CREATE INDEX IF NOT EXISTS idx_household_vendors_service ON household_vendors(service_type);
|
||||
|
||||
DROP TRIGGER IF EXISTS update_household_items_updated_at ON household_items;
|
||||
CREATE TRIGGER update_household_items_updated_at
|
||||
BEFORE UPDATE ON household_items
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
56
migrations/012_home_maintenance.sql
Normal file
56
migrations/012_home_maintenance.sql
Normal file
@@ -0,0 +1,56 @@
|
||||
-- Extension 2: Home Maintenance Tracker
|
||||
-- Tracks recurring maintenance tasks and logs completed work (single-user, no RLS)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS maintenance_tasks (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name TEXT NOT NULL,
|
||||
category TEXT,
|
||||
frequency_days INTEGER,
|
||||
last_completed TIMESTAMPTZ,
|
||||
next_due TIMESTAMPTZ,
|
||||
priority TEXT NOT NULL DEFAULT 'medium' CHECK (priority IN ('low', 'medium', 'high', 'urgent')),
|
||||
notes TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS maintenance_logs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
task_id UUID NOT NULL REFERENCES maintenance_tasks(id) ON DELETE CASCADE,
|
||||
completed_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
performed_by TEXT,
|
||||
cost DECIMAL(10, 2),
|
||||
notes TEXT,
|
||||
next_action TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_maintenance_tasks_next_due ON maintenance_tasks(next_due);
|
||||
CREATE INDEX IF NOT EXISTS idx_maintenance_logs_task ON maintenance_logs(task_id, completed_at DESC);
|
||||
|
||||
DROP TRIGGER IF EXISTS update_maintenance_tasks_updated_at ON maintenance_tasks;
|
||||
CREATE TRIGGER update_maintenance_tasks_updated_at
|
||||
BEFORE UPDATE ON maintenance_tasks
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE OR REPLACE FUNCTION update_task_after_maintenance_log()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
task_frequency INTEGER;
|
||||
BEGIN
|
||||
SELECT frequency_days INTO task_frequency FROM maintenance_tasks WHERE id = NEW.task_id;
|
||||
UPDATE maintenance_tasks
|
||||
SET last_completed = NEW.completed_at,
|
||||
next_due = CASE
|
||||
WHEN task_frequency IS NOT NULL THEN NEW.completed_at + (task_frequency || ' days')::INTERVAL
|
||||
ELSE NULL
|
||||
END,
|
||||
updated_at = now()
|
||||
WHERE id = NEW.task_id;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
DROP TRIGGER IF EXISTS update_task_after_log ON maintenance_logs;
|
||||
CREATE TRIGGER update_task_after_log
|
||||
AFTER INSERT ON maintenance_logs
|
||||
FOR EACH ROW EXECUTE FUNCTION update_task_after_maintenance_log();
|
||||
42
migrations/013_family_calendar.sql
Normal file
42
migrations/013_family_calendar.sql
Normal file
@@ -0,0 +1,42 @@
|
||||
-- Extension 3: Family Calendar
|
||||
-- Multi-person family scheduling (single-user, no RLS)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS family_members (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name TEXT NOT NULL,
|
||||
relationship TEXT,
|
||||
birth_date DATE,
|
||||
notes TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS activities (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
family_member_id UUID REFERENCES family_members(id) ON DELETE SET NULL,
|
||||
title TEXT NOT NULL,
|
||||
activity_type TEXT,
|
||||
day_of_week TEXT,
|
||||
start_time TIME,
|
||||
end_time TIME,
|
||||
start_date DATE,
|
||||
end_date DATE,
|
||||
location TEXT,
|
||||
notes TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS important_dates (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
family_member_id UUID REFERENCES family_members(id) ON DELETE SET NULL,
|
||||
title TEXT NOT NULL,
|
||||
date_value DATE NOT NULL,
|
||||
recurring_yearly BOOLEAN NOT NULL DEFAULT false,
|
||||
reminder_days_before INTEGER NOT NULL DEFAULT 7,
|
||||
notes TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_activities_dow ON activities(day_of_week);
|
||||
CREATE INDEX IF NOT EXISTS idx_activities_member ON activities(family_member_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_activities_dates ON activities(start_date, end_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_important_dates_date ON important_dates(date_value);
|
||||
54
migrations/014_meal_planning.sql
Normal file
54
migrations/014_meal_planning.sql
Normal file
@@ -0,0 +1,54 @@
|
||||
-- Extension 4: Meal Planning
|
||||
-- Recipes, weekly meal plans, and shopping lists (single-user, no RLS)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS recipes (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name TEXT NOT NULL,
|
||||
cuisine TEXT,
|
||||
prep_time_minutes INTEGER,
|
||||
cook_time_minutes INTEGER,
|
||||
servings INTEGER,
|
||||
ingredients JSONB NOT NULL DEFAULT '[]',
|
||||
instructions JSONB NOT NULL DEFAULT '[]',
|
||||
tags TEXT[] NOT NULL DEFAULT '{}',
|
||||
rating INTEGER CHECK (rating >= 1 AND rating <= 5),
|
||||
notes TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS meal_plans (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
week_start DATE NOT NULL,
|
||||
day_of_week TEXT NOT NULL,
|
||||
meal_type TEXT NOT NULL CHECK (meal_type IN ('breakfast', 'lunch', 'dinner', 'snack')),
|
||||
recipe_id UUID REFERENCES recipes(id) ON DELETE SET NULL,
|
||||
custom_meal TEXT,
|
||||
servings INTEGER,
|
||||
notes TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS shopping_lists (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
week_start DATE NOT NULL UNIQUE,
|
||||
items JSONB NOT NULL DEFAULT '[]',
|
||||
notes TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_recipes_cuisine ON recipes(cuisine);
|
||||
CREATE INDEX IF NOT EXISTS idx_recipes_tags ON recipes USING GIN (tags);
|
||||
CREATE INDEX IF NOT EXISTS idx_meal_plans_week ON meal_plans(week_start);
|
||||
CREATE INDEX IF NOT EXISTS idx_shopping_lists_week ON shopping_lists(week_start);
|
||||
|
||||
DROP TRIGGER IF EXISTS update_recipes_updated_at ON recipes;
|
||||
CREATE TRIGGER update_recipes_updated_at
|
||||
BEFORE UPDATE ON recipes
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
DROP TRIGGER IF EXISTS update_shopping_lists_updated_at ON shopping_lists;
|
||||
CREATE TRIGGER update_shopping_lists_updated_at
|
||||
BEFORE UPDATE ON shopping_lists
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
71
migrations/015_professional_crm.sql
Normal file
71
migrations/015_professional_crm.sql
Normal file
@@ -0,0 +1,71 @@
|
||||
-- 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();
|
||||
31
migrations/100_rls_and_grants.sql
Normal file
31
migrations/100_rls_and_grants.sql
Normal file
@@ -0,0 +1,31 @@
|
||||
-- Grant these permissions to the database role used by the application.
|
||||
-- Replace amcs with the actual role in your deployment before applying.
|
||||
GRANT ALL ON TABLE public.thoughts TO amcs;
|
||||
GRANT ALL ON TABLE public.projects TO amcs;
|
||||
GRANT ALL ON TABLE public.thought_links TO amcs;
|
||||
GRANT ALL ON TABLE public.embeddings TO amcs;
|
||||
|
||||
-- Household Knowledge (011)
|
||||
GRANT ALL ON TABLE public.household_items TO amcs;
|
||||
GRANT ALL ON TABLE public.household_vendors TO amcs;
|
||||
|
||||
-- Home Maintenance (012)
|
||||
GRANT ALL ON TABLE public.maintenance_tasks TO amcs;
|
||||
GRANT ALL ON TABLE public.maintenance_logs TO amcs;
|
||||
|
||||
-- Family Calendar (013)
|
||||
GRANT ALL ON TABLE public.family_members TO amcs;
|
||||
GRANT ALL ON TABLE public.activities TO amcs;
|
||||
GRANT ALL ON TABLE public.important_dates TO amcs;
|
||||
|
||||
-- Meal Planning (014)
|
||||
GRANT ALL ON TABLE public.recipes TO amcs;
|
||||
GRANT ALL ON TABLE public.meal_plans TO amcs;
|
||||
GRANT ALL ON TABLE public.shopping_lists TO amcs;
|
||||
|
||||
-- Professional CRM (015)
|
||||
GRANT ALL ON TABLE public.professional_contacts TO amcs;
|
||||
GRANT ALL ON TABLE public.contact_interactions TO amcs;
|
||||
GRANT ALL ON TABLE public.opportunities TO amcs;
|
||||
|
||||
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO amcs;
|
||||
Reference in New Issue
Block a user