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:
2026-03-26 23:29:03 +02:00
parent b74d63c543
commit 0eb6ac7ee5
25 changed files with 2910 additions and 10 deletions

View File

@@ -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;

View 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();

View 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();

View 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);

View 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();

View 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();

View 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;