From c3f3fa376e962f68110dcb688ab3405f9c443f81 Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 11 Aug 2025 17:38:30 +0000 Subject: [PATCH 1/2] Complete schema redesign for UltraEdge - Replaced existing schema with new comprehensive design - Added support for event-specific dropbags, item tracking, and performance monitoring - Improved inventory management and consumption tracking - Added support for pacers, support vehicles, and athlete kits - Added documentation of schema changes --- supabase/SCHEMA_CHANGES.md | 78 ++ .../migrations/20250811173500_new_schema.sql | 497 +++++++++++++ supabase/new_schema.sql | 494 +++++++++++++ supabase/schema.sql | 687 +++++++++--------- 4 files changed, 1430 insertions(+), 326 deletions(-) create mode 100644 supabase/SCHEMA_CHANGES.md create mode 100644 supabase/migrations/20250811173500_new_schema.sql create mode 100644 supabase/new_schema.sql diff --git a/supabase/SCHEMA_CHANGES.md b/supabase/SCHEMA_CHANGES.md new file mode 100644 index 0000000..ca80670 --- /dev/null +++ b/supabase/SCHEMA_CHANGES.md @@ -0,0 +1,78 @@ +# UltraEdge Schema Changes + +This document outlines the major schema changes implemented in the August 2025 update. + +## Overview of Changes + +The database schema has been completely redesigned to better support the UltraEdge application's requirements for endurance event crew and supply management. The new schema provides improved tracking of items, dropbags, aid stations, and athlete performance. + +## Key Improvements + +1. **Separated Event-Specific Data**: Created `EventDropbags` to handle event-specific instances of dropbags +2. **Enhanced Item Management**: Added `UserItems` for personal inventory tracking +3. **Better Consumption Tracking**: Improved consumption tracking with location and timing +4. **Performance Analytics**: Added tables for tracking athlete performance and aid station visits +5. **Deployment Tracking**: Added `DropbagDeployments` to track physical dropbag movements +6. **Enhanced Metadata**: Added timestamps, status fields, and better organization +7. **Constraint Handling**: Structure now supports the rule that items can't be in multiple dropbags for the same event +8. **Inventory Management**: Better tracking of available vs. reserved quantities +9. **Template System**: Improved dropbag template functionality +10. **Support System**: Added comprehensive pacer, support vehicle, and athlete kit tracking +11. **Mobile Crew Support**: Support vehicle location tracking for real-time coordination + +## Major Table Changes + +### Core Entities +- Renamed `profiles` to `users` with expanded fields +- Added `athlete_profiles` for athlete-specific information +- Renamed `races` to `events` with expanded fields +- Redesigned `aid_stations` to be event-independent + +### Event-Specific Relationships +- Added `event_aid_stations` to link aid stations to events +- Renamed `crew_members` to `crew` with expanded fields +- Added `event_crew` to link crew to events +- Redesigned `aid_station_crew` to link to event_aid_stations + +### Items and Inventory Management +- Replaced `gear_items` and `nutrition_items` with unified `items` table +- Added `user_items` for personal inventory tracking +- Added `item_requirements` for tracking item dependencies + +### Dropbag System +- Renamed `drop_bag_templates` to `dropbags` with template flag +- Added `event_dropbags` for event-specific instances +- Added `dropbag_items` for tracking items in dropbags +- Added `dropbag_deployments` for tracking physical dropbag movements + +### Consumption and Performance Tracking +- Added `event_consumption` for tracking item consumption +- Added `athlete_event_performance` for tracking athlete performance +- Added `aid_station_visits` for tracking aid station visits + +### Support and Performance Tracking +- Added `pacers` for tracking pacers +- Added `support_vehicles` for tracking support vehicles +- Added `support_vehicle_crew` for tracking crew in support vehicles +- Added `athlete_kits` for tracking athlete gear +- Added `athlete_kit_items` for tracking items in athlete kits +- Added `support_vehicle_inventory` for tracking items in support vehicles + +## Migration Notes + +This schema change is a complete replacement of the previous schema. All existing data will need to be migrated to the new schema. The migration process should: + +1. Back up all existing data +2. Drop all existing tables +3. Create the new tables +4. Migrate data from the backup to the new tables + +## Business Rules Enforcement + +- The `UserItems.availableQuantity` and `reservedQuantity` fields help enforce finite item quantities +- Event-specific dropbag instances prevent the same physical item from being in multiple dropbags for one event +- Consumption tracking updates available quantities for future events +- Item requirements are tracked for validation during dropbag packing +- Pacer assignments track both athlete and pacer with specific aid station segments +- Support vehicle crew can have multiple members with different roles +- Athlete kits separate mandatory gear from carried nutrition/hydration for better organization \ No newline at end of file diff --git a/supabase/migrations/20250811173500_new_schema.sql b/supabase/migrations/20250811173500_new_schema.sql new file mode 100644 index 0000000..06e975b --- /dev/null +++ b/supabase/migrations/20250811173500_new_schema.sql @@ -0,0 +1,497 @@ +-- Migration: Complete schema replacement +-- Description: Replacing the entire schema with a new structure based on updated requirements + +-- Drop existing tables (in reverse order of dependencies) +DROP TABLE IF EXISTS course_notes; +DROP TABLE IF EXISTS nutrition_plan_items; +DROP TABLE IF EXISTS nutrition_plans; +DROP TABLE IF EXISTS nutrition_items; +DROP TABLE IF EXISTS pacer_gear; +DROP TABLE IF EXISTS race_gear; +DROP TABLE IF EXISTS gear_items; +DROP TABLE IF EXISTS race_drop_bags; +DROP TABLE IF EXISTS drop_bag_templates; +DROP TABLE IF EXISTS aid_station_crew; +DROP TABLE IF EXISTS race_crew; +DROP TABLE IF EXISTS crew_members; +DROP TABLE IF EXISTS aid_station_checkins; +DROP TABLE IF EXISTS aid_stations; +DROP TABLE IF EXISTS races; +DROP TABLE IF EXISTS race_backups; +DROP TABLE IF EXISTS profiles; + +-- Create new schema based on the provided requirements + +-- Core Entities + +-- Users table +CREATE TABLE users ( + id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE, + name VARCHAR(255), + email VARCHAR(255) UNIQUE, + phone VARCHAR(50), + age INTEGER, + date_of_birth DATE, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- AthleteProfiles table +CREATE TABLE athlete_profiles ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, + location VARCHAR(255), + preferred_activity_type VARCHAR(100), + hourly_calorie_consumption INTEGER, + carry_limit INTEGER, -- weight limit in grams + preferred_aid_station_rest_duration INTEGER, -- minutes + emergency_contact_name VARCHAR(255), + emergency_contact_phone VARCHAR(50), + medical_notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Events table +CREATE TABLE events ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + name VARCHAR(255) NOT NULL, + description TEXT, + type VARCHAR(50) CHECK (type IN ('marathon', 'ultramarathon', 'triathlon', 'adventure_race', 'other')), + start_date_time TIMESTAMP WITH TIME ZONE, + end_date_time TIMESTAMP WITH TIME ZONE, + total_distance INTEGER, -- meters + elevation_gain INTEGER, -- meters + location VARCHAR(255), + latitude DECIMAL(10, 7), + longitude DECIMAL(10, 7), + registration_url VARCHAR(255), + mandatory_gear_list JSONB, + race_director_contact VARCHAR(255), + status VARCHAR(50) CHECK (status IN ('planning', 'active', 'completed', 'cancelled')), + created_by UUID REFERENCES users(id) ON DELETE SET NULL, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- AidStations table +CREATE TABLE aid_stations ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + name VARCHAR(255) NOT NULL, + description TEXT, + station_type VARCHAR(50) CHECK (station_type IN ('minor', 'major', 'marshall', 'start', 'finish', 'medical')), + latitude DECIMAL(10, 7), + longitude DECIMAL(10, 7), + elevation INTEGER, -- meters + facilities JSONB, -- {washrooms: boolean, medical: boolean, water: boolean, food: boolean} + accessibility_notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Event-Specific Relationships + +-- EventAidStations table +CREATE TABLE event_aid_stations ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_id UUID REFERENCES events(id) ON DELETE CASCADE NOT NULL, + aid_station_id UUID REFERENCES aid_stations(id) ON DELETE CASCADE NOT NULL, + distance_from_start INTEGER, -- meters + cutoff_date_time TIMESTAMP WITH TIME ZONE, + cutoff_duration_minutes INTEGER, + max_rest_duration_minutes INTEGER, + sequence_order INTEGER, -- for ordering aid stations along course + is_active BOOLEAN DEFAULT TRUE, + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Crew table +CREATE TABLE crew ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + name VARCHAR(255) NOT NULL, + phone VARCHAR(50), + email VARCHAR(255), + skills JSONB, -- JSON array: ["medical", "nutrition", "logistics", etc.] + availability TEXT, + emergency_contact VARCHAR(255), + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- EventCrew table +CREATE TABLE event_crew ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_id UUID REFERENCES events(id) ON DELETE CASCADE NOT NULL, + crew_id UUID REFERENCES crew(id) ON DELETE CASCADE NOT NULL, + role VARCHAR(50) CHECK (role IN ('crew_chief', 'medical', 'nutrition', 'logistics', 'general')), + start_date_time TIMESTAMP WITH TIME ZONE, + end_date_time TIMESTAMP WITH TIME ZONE, + is_active BOOLEAN DEFAULT TRUE, + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- AidStationCrew table +CREATE TABLE aid_station_crew ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_aid_station_id UUID REFERENCES event_aid_stations(id) ON DELETE CASCADE NOT NULL, + event_crew_id UUID REFERENCES event_crew(id) ON DELETE CASCADE NOT NULL, + start_date_time TIMESTAMP WITH TIME ZONE, + end_date_time TIMESTAMP WITH TIME ZONE, + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Items and Inventory Management + +-- Items table +CREATE TABLE items ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + name VARCHAR(255) NOT NULL, + description TEXT, + category VARCHAR(50) CHECK (category IN ('gear', 'nutrition', 'hydration', 'medical', 'safety')), + subcategory VARCHAR(100), -- headlamp, energy_gel, electrolyte, etc. + weight INTEGER, -- grams + volume INTEGER, -- ml - for liquids + is_consumable BOOLEAN DEFAULT FALSE, + runtime_minutes INTEGER, -- for items with finite use time + unit_of_measure VARCHAR(50) CHECK (unit_of_measure IN ('piece', 'ml', 'gram', 'serving')), + storage_instructions TEXT, + expiration_tracking BOOLEAN DEFAULT FALSE, + cost DECIMAL(10, 2), -- for inventory valuation + supplier VARCHAR(255), + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- UserItems table (User's Personal Inventory) +CREATE TABLE user_items ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, + item_id UUID REFERENCES items(id) ON DELETE CASCADE NOT NULL, + total_quantity INTEGER NOT NULL, + available_quantity INTEGER NOT NULL, + reserved_quantity INTEGER NOT NULL, -- allocated to events but not consumed + unit_cost DECIMAL(10, 2), + purchase_date DATE, + expiration_date DATE, + condition VARCHAR(50) CHECK (condition IN ('new', 'good', 'fair', 'poor')), + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- ItemRequirements table +CREATE TABLE item_requirements ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + primary_item_id UUID REFERENCES items(id) ON DELETE CASCADE NOT NULL, + required_item_id UUID REFERENCES items(id) ON DELETE CASCADE NOT NULL, + required_quantity INTEGER NOT NULL, + required_unit_of_measure VARCHAR(50), + relationship_type VARCHAR(50) CHECK (relationship_type IN ('requires', 'pairs_with', 'enhances')), + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Dropbag System + +-- Dropbags table +CREATE TABLE dropbags ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, + name VARCHAR(255) NOT NULL, + description TEXT, + is_template BOOLEAN DEFAULT FALSE, + template_category VARCHAR(100), -- for organizing templates + max_weight INTEGER, -- grams + color VARCHAR(50), -- for physical identification + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- EventDropbags table (Event-specific dropbag instances) +CREATE TABLE event_dropbags ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_id UUID REFERENCES events(id) ON DELETE CASCADE NOT NULL, + dropbag_id UUID REFERENCES dropbags(id) ON DELETE CASCADE NOT NULL, + user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, + status VARCHAR(50) CHECK (status IN ('planned', 'packed', 'deployed', 'retrieved')), + actual_weight INTEGER, -- grams + packing_notes TEXT, + deployment_notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- DropbagItems table +CREATE TABLE dropbag_items ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_dropbag_id UUID REFERENCES event_dropbags(id) ON DELETE CASCADE NOT NULL, + item_id UUID REFERENCES items(id) ON DELETE CASCADE NOT NULL, + planned_quantity INTEGER NOT NULL, + actual_quantity INTEGER, -- what was actually packed + is_mandatory BOOLEAN DEFAULT FALSE, + consumed_quantity INTEGER DEFAULT 0, -- tracked during event + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- DropbagDeployments table +CREATE TABLE dropbag_deployments ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_dropbag_id UUID REFERENCES event_dropbags(id) ON DELETE CASCADE NOT NULL, + event_aid_station_id UUID REFERENCES event_aid_stations(id) ON DELETE CASCADE NOT NULL, + deployment_date_time TIMESTAMP WITH TIME ZONE, + retrieval_date_time TIMESTAMP WITH TIME ZONE, + deployed_by UUID REFERENCES event_crew(id) ON DELETE SET NULL, + retrieved_by UUID REFERENCES event_crew(id) ON DELETE SET NULL, + status VARCHAR(50) CHECK (status IN ('deployed', 'accessed', 'retrieved', 'lost')), + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Consumption and Performance Tracking + +-- EventConsumption table +CREATE TABLE event_consumption ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_id UUID REFERENCES events(id) ON DELETE CASCADE NOT NULL, + user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, + item_id UUID REFERENCES items(id) ON DELETE CASCADE NOT NULL, + event_aid_station_id UUID REFERENCES event_aid_stations(id) ON DELETE SET NULL, + event_dropbag_id UUID REFERENCES event_dropbags(id) ON DELETE SET NULL, + quantity INTEGER NOT NULL, + consumption_date_time TIMESTAMP WITH TIME ZONE, + consumption_type VARCHAR(50) CHECK (consumption_type IN ('planned', 'actual')), + effectiveness VARCHAR(50) CHECK (effectiveness IN ('very_helpful', 'helpful', 'neutral', 'unhelpful')), + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- AthleteEventPerformance table +CREATE TABLE athlete_event_performance ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_id UUID REFERENCES events(id) ON DELETE CASCADE NOT NULL, + user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, + status VARCHAR(50) CHECK (status IN ('registered', 'started', 'finished', 'dnf', 'dsq')), + start_date_time TIMESTAMP WITH TIME ZONE, + finish_date_time TIMESTAMP WITH TIME ZONE, + total_time_minutes INTEGER, + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- AidStationVisits table +CREATE TABLE aid_station_visits ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + athlete_event_performance_id UUID REFERENCES athlete_event_performance(id) ON DELETE CASCADE NOT NULL, + event_aid_station_id UUID REFERENCES event_aid_stations(id) ON DELETE CASCADE NOT NULL, + arrival_date_time TIMESTAMP WITH TIME ZONE, + departure_date_time TIMESTAMP WITH TIME ZONE, + rest_duration_minutes INTEGER, + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Support and Performance Tracking + +-- Pacers table +CREATE TABLE pacers ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_id UUID REFERENCES events(id) ON DELETE CASCADE NOT NULL, + athlete_user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, -- the athlete being paced + pacer_user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, -- the person doing the pacing + start_aid_station_id UUID REFERENCES event_aid_stations(id) ON DELETE CASCADE NOT NULL, -- where pacing begins + end_aid_station_id UUID REFERENCES event_aid_stations(id) ON DELETE SET NULL, -- where pacing ends (nullable for full race) + status VARCHAR(50) CHECK (status IN ('registered', 'active', 'completed', 'dnf', 'withdrawn')), + start_date_time TIMESTAMP WITH TIME ZONE, + end_date_time TIMESTAMP WITH TIME ZONE, + total_time_minutes INTEGER, + pacing_notes TEXT, + emergency_contact_info TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- SupportVehicles table +CREATE TABLE support_vehicles ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_id UUID REFERENCES events(id) ON DELETE CASCADE NOT NULL, + athlete_user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, -- athlete being supported + vehicle_type VARCHAR(50) CHECK (vehicle_type IN ('car', 'van', 'bike', 'atv', 'other')), + license_plate VARCHAR(50), + status VARCHAR(50) CHECK (status IN ('registered', 'active', 'completed', 'withdrawn')), + start_location VARCHAR(255), + current_latitude DECIMAL(10, 7), + current_longitude DECIMAL(10, 7), + last_location_update TIMESTAMP WITH TIME ZONE, + driver_event_crew_id UUID REFERENCES event_crew(id) ON DELETE SET NULL, + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- SupportVehicleCrew table +CREATE TABLE support_vehicle_crew ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + support_vehicle_id UUID REFERENCES support_vehicles(id) ON DELETE CASCADE NOT NULL, + event_crew_id UUID REFERENCES event_crew(id) ON DELETE CASCADE NOT NULL, + role VARCHAR(50) CHECK (role IN ('driver', 'navigator', 'crew_chief', 'support')), + start_date_time TIMESTAMP WITH TIME ZONE, + end_date_time TIMESTAMP WITH TIME ZONE, + is_active BOOLEAN DEFAULT TRUE, + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- AthleteKits table (Starting/Carrying Gear) +CREATE TABLE athlete_kits ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_id UUID REFERENCES events(id) ON DELETE CASCADE NOT NULL, + user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, + kit_type VARCHAR(50) CHECK (kit_type IN ('starting_gear', 'mandatory_gear', 'carried_nutrition', 'carried_hydration')), + total_weight INTEGER, -- grams + status VARCHAR(50) CHECK (status IN ('planned', 'packed', 'race_ready', 'post_race')), + packing_date_time TIMESTAMP WITH TIME ZONE, + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- AthleteKitItems table +CREATE TABLE athlete_kit_items ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + athlete_kit_id UUID REFERENCES athlete_kits(id) ON DELETE CASCADE NOT NULL, + item_id UUID REFERENCES items(id) ON DELETE CASCADE NOT NULL, + quantity INTEGER NOT NULL, + is_mandatory BOOLEAN DEFAULT FALSE, + item_condition VARCHAR(50) CHECK (item_condition IN ('new', 'good', 'fair', 'poor')), + consumed_quantity INTEGER DEFAULT 0, -- for nutrition/hydration tracking + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- SupportVehicleInventory table +CREATE TABLE support_vehicle_inventory ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + support_vehicle_id UUID REFERENCES support_vehicles(id) ON DELETE CASCADE NOT NULL, + item_id UUID REFERENCES items(id) ON DELETE CASCADE NOT NULL, + quantity INTEGER NOT NULL, + consumed_quantity INTEGER DEFAULT 0, + restock_location VARCHAR(255), -- where item was added to vehicle + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Create indexes for performance +CREATE INDEX idx_athlete_profiles_user_id ON athlete_profiles(user_id); +CREATE INDEX idx_events_created_by ON events(created_by); +CREATE INDEX idx_event_aid_stations_event_id ON event_aid_stations(event_id); +CREATE INDEX idx_event_aid_stations_aid_station_id ON event_aid_stations(aid_station_id); +CREATE INDEX idx_event_crew_event_id ON event_crew(event_id); +CREATE INDEX idx_event_crew_crew_id ON event_crew(crew_id); +CREATE INDEX idx_aid_station_crew_event_aid_station_id ON aid_station_crew(event_aid_station_id); +CREATE INDEX idx_aid_station_crew_event_crew_id ON aid_station_crew(event_crew_id); +CREATE INDEX idx_user_items_user_id ON user_items(user_id); +CREATE INDEX idx_user_items_item_id ON user_items(item_id); +CREATE INDEX idx_item_requirements_primary_item_id ON item_requirements(primary_item_id); +CREATE INDEX idx_item_requirements_required_item_id ON item_requirements(required_item_id); +CREATE INDEX idx_dropbags_user_id ON dropbags(user_id); +CREATE INDEX idx_event_dropbags_event_id ON event_dropbags(event_id); +CREATE INDEX idx_event_dropbags_dropbag_id ON event_dropbags(dropbag_id); +CREATE INDEX idx_event_dropbags_user_id ON event_dropbags(user_id); +CREATE INDEX idx_dropbag_items_event_dropbag_id ON dropbag_items(event_dropbag_id); +CREATE INDEX idx_dropbag_items_item_id ON dropbag_items(item_id); +CREATE INDEX idx_dropbag_deployments_event_dropbag_id ON dropbag_deployments(event_dropbag_id); +CREATE INDEX idx_dropbag_deployments_event_aid_station_id ON dropbag_deployments(event_aid_station_id); +CREATE INDEX idx_event_consumption_event_id ON event_consumption(event_id); +CREATE INDEX idx_event_consumption_user_id ON event_consumption(user_id); +CREATE INDEX idx_event_consumption_item_id ON event_consumption(item_id); +CREATE INDEX idx_athlete_event_performance_event_id ON athlete_event_performance(event_id); +CREATE INDEX idx_athlete_event_performance_user_id ON athlete_event_performance(user_id); +CREATE INDEX idx_aid_station_visits_athlete_event_performance_id ON aid_station_visits(athlete_event_performance_id); +CREATE INDEX idx_aid_station_visits_event_aid_station_id ON aid_station_visits(event_aid_station_id); +CREATE INDEX idx_pacers_event_id ON pacers(event_id); +CREATE INDEX idx_pacers_athlete_user_id ON pacers(athlete_user_id); +CREATE INDEX idx_pacers_pacer_user_id ON pacers(pacer_user_id); +CREATE INDEX idx_support_vehicles_event_id ON support_vehicles(event_id); +CREATE INDEX idx_support_vehicles_athlete_user_id ON support_vehicles(athlete_user_id); +CREATE INDEX idx_support_vehicle_crew_support_vehicle_id ON support_vehicle_crew(support_vehicle_id); +CREATE INDEX idx_support_vehicle_crew_event_crew_id ON support_vehicle_crew(event_crew_id); +CREATE INDEX idx_athlete_kits_event_id ON athlete_kits(event_id); +CREATE INDEX idx_athlete_kits_user_id ON athlete_kits(user_id); +CREATE INDEX idx_athlete_kit_items_athlete_kit_id ON athlete_kit_items(athlete_kit_id); +CREATE INDEX idx_athlete_kit_items_item_id ON athlete_kit_items(item_id); +CREATE INDEX idx_support_vehicle_inventory_support_vehicle_id ON support_vehicle_inventory(support_vehicle_id); +CREATE INDEX idx_support_vehicle_inventory_item_id ON support_vehicle_inventory(item_id); + +-- Set up Row Level Security (RLS) +ALTER TABLE users ENABLE ROW LEVEL SECURITY; +ALTER TABLE athlete_profiles ENABLE ROW LEVEL SECURITY; +ALTER TABLE events ENABLE ROW LEVEL SECURITY; +ALTER TABLE aid_stations ENABLE ROW LEVEL SECURITY; +ALTER TABLE event_aid_stations ENABLE ROW LEVEL SECURITY; +ALTER TABLE crew ENABLE ROW LEVEL SECURITY; +ALTER TABLE event_crew ENABLE ROW LEVEL SECURITY; +ALTER TABLE aid_station_crew ENABLE ROW LEVEL SECURITY; +ALTER TABLE items ENABLE ROW LEVEL SECURITY; +ALTER TABLE user_items ENABLE ROW LEVEL SECURITY; +ALTER TABLE item_requirements ENABLE ROW LEVEL SECURITY; +ALTER TABLE dropbags ENABLE ROW LEVEL SECURITY; +ALTER TABLE event_dropbags ENABLE ROW LEVEL SECURITY; +ALTER TABLE dropbag_items ENABLE ROW LEVEL SECURITY; +ALTER TABLE dropbag_deployments ENABLE ROW LEVEL SECURITY; +ALTER TABLE event_consumption ENABLE ROW LEVEL SECURITY; +ALTER TABLE athlete_event_performance ENABLE ROW LEVEL SECURITY; +ALTER TABLE aid_station_visits ENABLE ROW LEVEL SECURITY; +ALTER TABLE pacers ENABLE ROW LEVEL SECURITY; +ALTER TABLE support_vehicles ENABLE ROW LEVEL SECURITY; +ALTER TABLE support_vehicle_crew ENABLE ROW LEVEL SECURITY; +ALTER TABLE athlete_kits ENABLE ROW LEVEL SECURITY; +ALTER TABLE athlete_kit_items ENABLE ROW LEVEL SECURITY; +ALTER TABLE support_vehicle_inventory ENABLE ROW LEVEL SECURITY; + +-- Create policies for users +CREATE POLICY "Users can view their own profile" + ON users FOR SELECT + USING (auth.uid() = id); + +CREATE POLICY "Users can update their own profile" + ON users FOR UPDATE + USING (auth.uid() = id); + +-- Function to handle user creation +CREATE OR REPLACE FUNCTION public.handle_new_user() +RETURNS TRIGGER AS $$ +BEGIN + INSERT INTO public.users ( + id, + name, + email + ) + VALUES ( + NEW.id, + COALESCE(NEW.raw_user_meta_data->>'name', NEW.email), + NEW.email + ); + RETURN NEW; +END; +$$ LANGUAGE plpgsql SECURITY DEFINER; + +-- Trigger to automatically create user on signup +CREATE TRIGGER on_auth_user_created + AFTER INSERT ON auth.users + FOR EACH ROW EXECUTE PROCEDURE public.handle_new_user(); \ No newline at end of file diff --git a/supabase/new_schema.sql b/supabase/new_schema.sql new file mode 100644 index 0000000..09d501b --- /dev/null +++ b/supabase/new_schema.sql @@ -0,0 +1,494 @@ +-- Drop existing tables (in reverse order of dependencies) +DROP TABLE IF EXISTS course_notes; +DROP TABLE IF EXISTS nutrition_plan_items; +DROP TABLE IF EXISTS nutrition_plans; +DROP TABLE IF EXISTS nutrition_items; +DROP TABLE IF EXISTS pacer_gear; +DROP TABLE IF EXISTS race_gear; +DROP TABLE IF EXISTS gear_items; +DROP TABLE IF EXISTS race_drop_bags; +DROP TABLE IF EXISTS drop_bag_templates; +DROP TABLE IF EXISTS aid_station_crew; +DROP TABLE IF EXISTS race_crew; +DROP TABLE IF EXISTS crew_members; +DROP TABLE IF EXISTS aid_station_checkins; +DROP TABLE IF EXISTS aid_stations; +DROP TABLE IF EXISTS races; +DROP TABLE IF EXISTS race_backups; +DROP TABLE IF EXISTS profiles; + +-- Create new schema based on the provided requirements + +-- Core Entities + +-- Users table +CREATE TABLE users ( + id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE, + name VARCHAR(255), + email VARCHAR(255) UNIQUE, + phone VARCHAR(50), + age INTEGER, + date_of_birth DATE, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- AthleteProfiles table +CREATE TABLE athlete_profiles ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, + location VARCHAR(255), + preferred_activity_type VARCHAR(100), + hourly_calorie_consumption INTEGER, + carry_limit INTEGER, -- weight limit in grams + preferred_aid_station_rest_duration INTEGER, -- minutes + emergency_contact_name VARCHAR(255), + emergency_contact_phone VARCHAR(50), + medical_notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Events table +CREATE TABLE events ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + name VARCHAR(255) NOT NULL, + description TEXT, + type VARCHAR(50) CHECK (type IN ('marathon', 'ultramarathon', 'triathlon', 'adventure_race', 'other')), + start_date_time TIMESTAMP WITH TIME ZONE, + end_date_time TIMESTAMP WITH TIME ZONE, + total_distance INTEGER, -- meters + elevation_gain INTEGER, -- meters + location VARCHAR(255), + latitude DECIMAL(10, 7), + longitude DECIMAL(10, 7), + registration_url VARCHAR(255), + mandatory_gear_list JSONB, + race_director_contact VARCHAR(255), + status VARCHAR(50) CHECK (status IN ('planning', 'active', 'completed', 'cancelled')), + created_by UUID REFERENCES users(id) ON DELETE SET NULL, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- AidStations table +CREATE TABLE aid_stations ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + name VARCHAR(255) NOT NULL, + description TEXT, + station_type VARCHAR(50) CHECK (station_type IN ('minor', 'major', 'marshall', 'start', 'finish', 'medical')), + latitude DECIMAL(10, 7), + longitude DECIMAL(10, 7), + elevation INTEGER, -- meters + facilities JSONB, -- {washrooms: boolean, medical: boolean, water: boolean, food: boolean} + accessibility_notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Event-Specific Relationships + +-- EventAidStations table +CREATE TABLE event_aid_stations ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_id UUID REFERENCES events(id) ON DELETE CASCADE NOT NULL, + aid_station_id UUID REFERENCES aid_stations(id) ON DELETE CASCADE NOT NULL, + distance_from_start INTEGER, -- meters + cutoff_date_time TIMESTAMP WITH TIME ZONE, + cutoff_duration_minutes INTEGER, + max_rest_duration_minutes INTEGER, + sequence_order INTEGER, -- for ordering aid stations along course + is_active BOOLEAN DEFAULT TRUE, + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Crew table +CREATE TABLE crew ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + name VARCHAR(255) NOT NULL, + phone VARCHAR(50), + email VARCHAR(255), + skills JSONB, -- JSON array: ["medical", "nutrition", "logistics", etc.] + availability TEXT, + emergency_contact VARCHAR(255), + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- EventCrew table +CREATE TABLE event_crew ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_id UUID REFERENCES events(id) ON DELETE CASCADE NOT NULL, + crew_id UUID REFERENCES crew(id) ON DELETE CASCADE NOT NULL, + role VARCHAR(50) CHECK (role IN ('crew_chief', 'medical', 'nutrition', 'logistics', 'general')), + start_date_time TIMESTAMP WITH TIME ZONE, + end_date_time TIMESTAMP WITH TIME ZONE, + is_active BOOLEAN DEFAULT TRUE, + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- AidStationCrew table +CREATE TABLE aid_station_crew ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_aid_station_id UUID REFERENCES event_aid_stations(id) ON DELETE CASCADE NOT NULL, + event_crew_id UUID REFERENCES event_crew(id) ON DELETE CASCADE NOT NULL, + start_date_time TIMESTAMP WITH TIME ZONE, + end_date_time TIMESTAMP WITH TIME ZONE, + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Items and Inventory Management + +-- Items table +CREATE TABLE items ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + name VARCHAR(255) NOT NULL, + description TEXT, + category VARCHAR(50) CHECK (category IN ('gear', 'nutrition', 'hydration', 'medical', 'safety')), + subcategory VARCHAR(100), -- headlamp, energy_gel, electrolyte, etc. + weight INTEGER, -- grams + volume INTEGER, -- ml - for liquids + is_consumable BOOLEAN DEFAULT FALSE, + runtime_minutes INTEGER, -- for items with finite use time + unit_of_measure VARCHAR(50) CHECK (unit_of_measure IN ('piece', 'ml', 'gram', 'serving')), + storage_instructions TEXT, + expiration_tracking BOOLEAN DEFAULT FALSE, + cost DECIMAL(10, 2), -- for inventory valuation + supplier VARCHAR(255), + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- UserItems table (User's Personal Inventory) +CREATE TABLE user_items ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, + item_id UUID REFERENCES items(id) ON DELETE CASCADE NOT NULL, + total_quantity INTEGER NOT NULL, + available_quantity INTEGER NOT NULL, + reserved_quantity INTEGER NOT NULL, -- allocated to events but not consumed + unit_cost DECIMAL(10, 2), + purchase_date DATE, + expiration_date DATE, + condition VARCHAR(50) CHECK (condition IN ('new', 'good', 'fair', 'poor')), + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- ItemRequirements table +CREATE TABLE item_requirements ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + primary_item_id UUID REFERENCES items(id) ON DELETE CASCADE NOT NULL, + required_item_id UUID REFERENCES items(id) ON DELETE CASCADE NOT NULL, + required_quantity INTEGER NOT NULL, + required_unit_of_measure VARCHAR(50), + relationship_type VARCHAR(50) CHECK (relationship_type IN ('requires', 'pairs_with', 'enhances')), + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Dropbag System + +-- Dropbags table +CREATE TABLE dropbags ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, + name VARCHAR(255) NOT NULL, + description TEXT, + is_template BOOLEAN DEFAULT FALSE, + template_category VARCHAR(100), -- for organizing templates + max_weight INTEGER, -- grams + color VARCHAR(50), -- for physical identification + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- EventDropbags table (Event-specific dropbag instances) +CREATE TABLE event_dropbags ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_id UUID REFERENCES events(id) ON DELETE CASCADE NOT NULL, + dropbag_id UUID REFERENCES dropbags(id) ON DELETE CASCADE NOT NULL, + user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, + status VARCHAR(50) CHECK (status IN ('planned', 'packed', 'deployed', 'retrieved')), + actual_weight INTEGER, -- grams + packing_notes TEXT, + deployment_notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- DropbagItems table +CREATE TABLE dropbag_items ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_dropbag_id UUID REFERENCES event_dropbags(id) ON DELETE CASCADE NOT NULL, + item_id UUID REFERENCES items(id) ON DELETE CASCADE NOT NULL, + planned_quantity INTEGER NOT NULL, + actual_quantity INTEGER, -- what was actually packed + is_mandatory BOOLEAN DEFAULT FALSE, + consumed_quantity INTEGER DEFAULT 0, -- tracked during event + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- DropbagDeployments table +CREATE TABLE dropbag_deployments ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_dropbag_id UUID REFERENCES event_dropbags(id) ON DELETE CASCADE NOT NULL, + event_aid_station_id UUID REFERENCES event_aid_stations(id) ON DELETE CASCADE NOT NULL, + deployment_date_time TIMESTAMP WITH TIME ZONE, + retrieval_date_time TIMESTAMP WITH TIME ZONE, + deployed_by UUID REFERENCES event_crew(id) ON DELETE SET NULL, + retrieved_by UUID REFERENCES event_crew(id) ON DELETE SET NULL, + status VARCHAR(50) CHECK (status IN ('deployed', 'accessed', 'retrieved', 'lost')), + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Consumption and Performance Tracking + +-- EventConsumption table +CREATE TABLE event_consumption ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_id UUID REFERENCES events(id) ON DELETE CASCADE NOT NULL, + user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, + item_id UUID REFERENCES items(id) ON DELETE CASCADE NOT NULL, + event_aid_station_id UUID REFERENCES event_aid_stations(id) ON DELETE SET NULL, + event_dropbag_id UUID REFERENCES event_dropbags(id) ON DELETE SET NULL, + quantity INTEGER NOT NULL, + consumption_date_time TIMESTAMP WITH TIME ZONE, + consumption_type VARCHAR(50) CHECK (consumption_type IN ('planned', 'actual')), + effectiveness VARCHAR(50) CHECK (effectiveness IN ('very_helpful', 'helpful', 'neutral', 'unhelpful')), + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- AthleteEventPerformance table +CREATE TABLE athlete_event_performance ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_id UUID REFERENCES events(id) ON DELETE CASCADE NOT NULL, + user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, + status VARCHAR(50) CHECK (status IN ('registered', 'started', 'finished', 'dnf', 'dsq')), + start_date_time TIMESTAMP WITH TIME ZONE, + finish_date_time TIMESTAMP WITH TIME ZONE, + total_time_minutes INTEGER, + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- AidStationVisits table +CREATE TABLE aid_station_visits ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + athlete_event_performance_id UUID REFERENCES athlete_event_performance(id) ON DELETE CASCADE NOT NULL, + event_aid_station_id UUID REFERENCES event_aid_stations(id) ON DELETE CASCADE NOT NULL, + arrival_date_time TIMESTAMP WITH TIME ZONE, + departure_date_time TIMESTAMP WITH TIME ZONE, + rest_duration_minutes INTEGER, + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Support and Performance Tracking + +-- Pacers table +CREATE TABLE pacers ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_id UUID REFERENCES events(id) ON DELETE CASCADE NOT NULL, + athlete_user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, -- the athlete being paced + pacer_user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, -- the person doing the pacing + start_aid_station_id UUID REFERENCES event_aid_stations(id) ON DELETE CASCADE NOT NULL, -- where pacing begins + end_aid_station_id UUID REFERENCES event_aid_stations(id) ON DELETE SET NULL, -- where pacing ends (nullable for full race) + status VARCHAR(50) CHECK (status IN ('registered', 'active', 'completed', 'dnf', 'withdrawn')), + start_date_time TIMESTAMP WITH TIME ZONE, + end_date_time TIMESTAMP WITH TIME ZONE, + total_time_minutes INTEGER, + pacing_notes TEXT, + emergency_contact_info TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- SupportVehicles table +CREATE TABLE support_vehicles ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_id UUID REFERENCES events(id) ON DELETE CASCADE NOT NULL, + athlete_user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, -- athlete being supported + vehicle_type VARCHAR(50) CHECK (vehicle_type IN ('car', 'van', 'bike', 'atv', 'other')), + license_plate VARCHAR(50), + status VARCHAR(50) CHECK (status IN ('registered', 'active', 'completed', 'withdrawn')), + start_location VARCHAR(255), + current_latitude DECIMAL(10, 7), + current_longitude DECIMAL(10, 7), + last_location_update TIMESTAMP WITH TIME ZONE, + driver_event_crew_id UUID REFERENCES event_crew(id) ON DELETE SET NULL, + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- SupportVehicleCrew table +CREATE TABLE support_vehicle_crew ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + support_vehicle_id UUID REFERENCES support_vehicles(id) ON DELETE CASCADE NOT NULL, + event_crew_id UUID REFERENCES event_crew(id) ON DELETE CASCADE NOT NULL, + role VARCHAR(50) CHECK (role IN ('driver', 'navigator', 'crew_chief', 'support')), + start_date_time TIMESTAMP WITH TIME ZONE, + end_date_time TIMESTAMP WITH TIME ZONE, + is_active BOOLEAN DEFAULT TRUE, + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- AthleteKits table (Starting/Carrying Gear) +CREATE TABLE athlete_kits ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_id UUID REFERENCES events(id) ON DELETE CASCADE NOT NULL, + user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, + kit_type VARCHAR(50) CHECK (kit_type IN ('starting_gear', 'mandatory_gear', 'carried_nutrition', 'carried_hydration')), + total_weight INTEGER, -- grams + status VARCHAR(50) CHECK (status IN ('planned', 'packed', 'race_ready', 'post_race')), + packing_date_time TIMESTAMP WITH TIME ZONE, + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- AthleteKitItems table +CREATE TABLE athlete_kit_items ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + athlete_kit_id UUID REFERENCES athlete_kits(id) ON DELETE CASCADE NOT NULL, + item_id UUID REFERENCES items(id) ON DELETE CASCADE NOT NULL, + quantity INTEGER NOT NULL, + is_mandatory BOOLEAN DEFAULT FALSE, + item_condition VARCHAR(50) CHECK (item_condition IN ('new', 'good', 'fair', 'poor')), + consumed_quantity INTEGER DEFAULT 0, -- for nutrition/hydration tracking + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- SupportVehicleInventory table +CREATE TABLE support_vehicle_inventory ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + support_vehicle_id UUID REFERENCES support_vehicles(id) ON DELETE CASCADE NOT NULL, + item_id UUID REFERENCES items(id) ON DELETE CASCADE NOT NULL, + quantity INTEGER NOT NULL, + consumed_quantity INTEGER DEFAULT 0, + restock_location VARCHAR(255), -- where item was added to vehicle + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Create indexes for performance +CREATE INDEX idx_athlete_profiles_user_id ON athlete_profiles(user_id); +CREATE INDEX idx_events_created_by ON events(created_by); +CREATE INDEX idx_event_aid_stations_event_id ON event_aid_stations(event_id); +CREATE INDEX idx_event_aid_stations_aid_station_id ON event_aid_stations(aid_station_id); +CREATE INDEX idx_event_crew_event_id ON event_crew(event_id); +CREATE INDEX idx_event_crew_crew_id ON event_crew(crew_id); +CREATE INDEX idx_aid_station_crew_event_aid_station_id ON aid_station_crew(event_aid_station_id); +CREATE INDEX idx_aid_station_crew_event_crew_id ON aid_station_crew(event_crew_id); +CREATE INDEX idx_user_items_user_id ON user_items(user_id); +CREATE INDEX idx_user_items_item_id ON user_items(item_id); +CREATE INDEX idx_item_requirements_primary_item_id ON item_requirements(primary_item_id); +CREATE INDEX idx_item_requirements_required_item_id ON item_requirements(required_item_id); +CREATE INDEX idx_dropbags_user_id ON dropbags(user_id); +CREATE INDEX idx_event_dropbags_event_id ON event_dropbags(event_id); +CREATE INDEX idx_event_dropbags_dropbag_id ON event_dropbags(dropbag_id); +CREATE INDEX idx_event_dropbags_user_id ON event_dropbags(user_id); +CREATE INDEX idx_dropbag_items_event_dropbag_id ON dropbag_items(event_dropbag_id); +CREATE INDEX idx_dropbag_items_item_id ON dropbag_items(item_id); +CREATE INDEX idx_dropbag_deployments_event_dropbag_id ON dropbag_deployments(event_dropbag_id); +CREATE INDEX idx_dropbag_deployments_event_aid_station_id ON dropbag_deployments(event_aid_station_id); +CREATE INDEX idx_event_consumption_event_id ON event_consumption(event_id); +CREATE INDEX idx_event_consumption_user_id ON event_consumption(user_id); +CREATE INDEX idx_event_consumption_item_id ON event_consumption(item_id); +CREATE INDEX idx_athlete_event_performance_event_id ON athlete_event_performance(event_id); +CREATE INDEX idx_athlete_event_performance_user_id ON athlete_event_performance(user_id); +CREATE INDEX idx_aid_station_visits_athlete_event_performance_id ON aid_station_visits(athlete_event_performance_id); +CREATE INDEX idx_aid_station_visits_event_aid_station_id ON aid_station_visits(event_aid_station_id); +CREATE INDEX idx_pacers_event_id ON pacers(event_id); +CREATE INDEX idx_pacers_athlete_user_id ON pacers(athlete_user_id); +CREATE INDEX idx_pacers_pacer_user_id ON pacers(pacer_user_id); +CREATE INDEX idx_support_vehicles_event_id ON support_vehicles(event_id); +CREATE INDEX idx_support_vehicles_athlete_user_id ON support_vehicles(athlete_user_id); +CREATE INDEX idx_support_vehicle_crew_support_vehicle_id ON support_vehicle_crew(support_vehicle_id); +CREATE INDEX idx_support_vehicle_crew_event_crew_id ON support_vehicle_crew(event_crew_id); +CREATE INDEX idx_athlete_kits_event_id ON athlete_kits(event_id); +CREATE INDEX idx_athlete_kits_user_id ON athlete_kits(user_id); +CREATE INDEX idx_athlete_kit_items_athlete_kit_id ON athlete_kit_items(athlete_kit_id); +CREATE INDEX idx_athlete_kit_items_item_id ON athlete_kit_items(item_id); +CREATE INDEX idx_support_vehicle_inventory_support_vehicle_id ON support_vehicle_inventory(support_vehicle_id); +CREATE INDEX idx_support_vehicle_inventory_item_id ON support_vehicle_inventory(item_id); + +-- Set up Row Level Security (RLS) +ALTER TABLE users ENABLE ROW LEVEL SECURITY; +ALTER TABLE athlete_profiles ENABLE ROW LEVEL SECURITY; +ALTER TABLE events ENABLE ROW LEVEL SECURITY; +ALTER TABLE aid_stations ENABLE ROW LEVEL SECURITY; +ALTER TABLE event_aid_stations ENABLE ROW LEVEL SECURITY; +ALTER TABLE crew ENABLE ROW LEVEL SECURITY; +ALTER TABLE event_crew ENABLE ROW LEVEL SECURITY; +ALTER TABLE aid_station_crew ENABLE ROW LEVEL SECURITY; +ALTER TABLE items ENABLE ROW LEVEL SECURITY; +ALTER TABLE user_items ENABLE ROW LEVEL SECURITY; +ALTER TABLE item_requirements ENABLE ROW LEVEL SECURITY; +ALTER TABLE dropbags ENABLE ROW LEVEL SECURITY; +ALTER TABLE event_dropbags ENABLE ROW LEVEL SECURITY; +ALTER TABLE dropbag_items ENABLE ROW LEVEL SECURITY; +ALTER TABLE dropbag_deployments ENABLE ROW LEVEL SECURITY; +ALTER TABLE event_consumption ENABLE ROW LEVEL SECURITY; +ALTER TABLE athlete_event_performance ENABLE ROW LEVEL SECURITY; +ALTER TABLE aid_station_visits ENABLE ROW LEVEL SECURITY; +ALTER TABLE pacers ENABLE ROW LEVEL SECURITY; +ALTER TABLE support_vehicles ENABLE ROW LEVEL SECURITY; +ALTER TABLE support_vehicle_crew ENABLE ROW LEVEL SECURITY; +ALTER TABLE athlete_kits ENABLE ROW LEVEL SECURITY; +ALTER TABLE athlete_kit_items ENABLE ROW LEVEL SECURITY; +ALTER TABLE support_vehicle_inventory ENABLE ROW LEVEL SECURITY; + +-- Create policies for users +CREATE POLICY "Users can view their own profile" + ON users FOR SELECT + USING (auth.uid() = id); + +CREATE POLICY "Users can update their own profile" + ON users FOR UPDATE + USING (auth.uid() = id); + +-- Function to handle user creation +CREATE OR REPLACE FUNCTION public.handle_new_user() +RETURNS TRIGGER AS $$ +BEGIN + INSERT INTO public.users ( + id, + name, + email + ) + VALUES ( + NEW.id, + COALESCE(NEW.raw_user_meta_data->>'name', NEW.email), + NEW.email + ); + RETURN NEW; +END; +$$ LANGUAGE plpgsql SECURITY DEFINER; + +-- Trigger to automatically create user on signup +CREATE TRIGGER on_auth_user_created + AFTER INSERT ON auth.users + FOR EACH ROW EXECUTE PROCEDURE public.handle_new_user(); \ No newline at end of file diff --git a/supabase/schema.sql b/supabase/schema.sql index 93201e0..3fcae28 100644 --- a/supabase/schema.sql +++ b/supabase/schema.sql @@ -1,438 +1,473 @@ --- Create profiles table -CREATE TABLE profiles ( +-- Core Entities + +-- Users table +CREATE TABLE users ( id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE, - name TEXT, - email TEXT UNIQUE, - is_premium BOOLEAN DEFAULT FALSE, - profile_image TEXT, - location TEXT, - bio TEXT, - preferences JSONB, - stats JSONB, - achievements JSONB, + name VARCHAR(255), + email VARCHAR(255) UNIQUE, + phone VARCHAR(50), + age INTEGER, + date_of_birth DATE, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); --- Create race_backups table (legacy - will be deprecated after migration) -CREATE TABLE race_backups ( +-- AthleteProfiles table +CREATE TABLE athlete_profiles ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL, - races_data JSONB NOT NULL, - backup_date TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() + user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, + location VARCHAR(255), + preferred_activity_type VARCHAR(100), + hourly_calorie_consumption INTEGER, + carry_limit INTEGER, -- weight limit in grams + preferred_aid_station_rest_duration INTEGER, -- minutes + emergency_contact_name VARCHAR(255), + emergency_contact_phone VARCHAR(50), + medical_notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); --- Create races table to store race data properly instead of using JSONB -CREATE TABLE races ( +-- Events table +CREATE TABLE events ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL, - name TEXT NOT NULL, - distance NUMERIC NOT NULL, - elevation NUMERIC NOT NULL, - date DATE NOT NULL, - start_time TIME, - gear_pickup_time TIMESTAMP WITH TIME ZONE, - briefing_time TIMESTAMP WITH TIME ZONE, - cutoff_time INTERVAL, - goal_time INTERVAL, - hiking_poles_allowed BOOLEAN DEFAULT TRUE, - pacer_allowed BOOLEAN DEFAULT FALSE, - pacer_start_point TEXT, - race_status TEXT DEFAULT 'planned', -- planned, completed, DNF - result_time INTERVAL, - result_notes TEXT, - course_notes TEXT, + name VARCHAR(255) NOT NULL, + description TEXT, + type VARCHAR(50) CHECK (type IN ('marathon', 'ultramarathon', 'triathlon', 'adventure_race', 'other')), + start_date_time TIMESTAMP WITH TIME ZONE, + end_date_time TIMESTAMP WITH TIME ZONE, + total_distance INTEGER, -- meters + elevation_gain INTEGER, -- meters + location VARCHAR(255), + latitude DECIMAL(10, 7), + longitude DECIMAL(10, 7), + registration_url VARCHAR(255), + mandatory_gear_list JSONB, + race_director_contact VARCHAR(255), + status VARCHAR(50) CHECK (status IN ('planning', 'active', 'completed', 'cancelled')), + created_by UUID REFERENCES users(id) ON DELETE SET NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); --- Create aid_stations table +-- AidStations table CREATE TABLE aid_stations ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - race_id UUID REFERENCES races(id) ON DELETE CASCADE NOT NULL, - name TEXT NOT NULL, - distance NUMERIC NOT NULL, - cutoff_time TIME, - eta_time TIME, - is_eta_manual BOOLEAN DEFAULT FALSE, - water_available BOOLEAN DEFAULT TRUE, - sports_drink_available BOOLEAN DEFAULT TRUE, - soda_available BOOLEAN DEFAULT FALSE, - fruit_available BOOLEAN DEFAULT TRUE, - sandwiches_available BOOLEAN DEFAULT FALSE, - soup_available BOOLEAN DEFAULT FALSE, - medical_available BOOLEAN DEFAULT TRUE, - other_nutrition TEXT, - washroom_available BOOLEAN DEFAULT FALSE, - drop_bag_allowed BOOLEAN DEFAULT FALSE, - crew_allowed BOOLEAN DEFAULT FALSE, + name VARCHAR(255) NOT NULL, + description TEXT, + station_type VARCHAR(50) CHECK (station_type IN ('minor', 'major', 'marshall', 'start', 'finish', 'medical')), + latitude DECIMAL(10, 7), + longitude DECIMAL(10, 7), + elevation INTEGER, -- meters + facilities JSONB, -- {washrooms: boolean, medical: boolean, water: boolean, food: boolean} + accessibility_notes TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); --- Create aid_station_checkins table to track time spent at aid stations -CREATE TABLE aid_station_checkins ( +-- Event-Specific Relationships + +-- EventAidStations table +CREATE TABLE event_aid_stations ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_id UUID REFERENCES events(id) ON DELETE CASCADE NOT NULL, aid_station_id UUID REFERENCES aid_stations(id) ON DELETE CASCADE NOT NULL, - check_in_time TIMESTAMP WITH TIME ZONE, - check_out_time TIMESTAMP WITH TIME ZONE, + distance_from_start INTEGER, -- meters + cutoff_date_time TIMESTAMP WITH TIME ZONE, + cutoff_duration_minutes INTEGER, + max_rest_duration_minutes INTEGER, + sequence_order INTEGER, -- for ordering aid stations along course + is_active BOOLEAN DEFAULT TRUE, notes TEXT, - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); --- Create crew_members table -CREATE TABLE crew_members ( +-- Crew table +CREATE TABLE crew ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL, - name TEXT NOT NULL, - email TEXT, - phone TEXT, - role TEXT, - responsibilities TEXT, - notes TEXT, + name VARCHAR(255) NOT NULL, + phone VARCHAR(50), + email VARCHAR(255), + skills JSONB, -- JSON array: ["medical", "nutrition", "logistics", etc.] + availability TEXT, + emergency_contact VARCHAR(255), created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); --- Create race_crew table (junction table for many-to-many relationship) -CREATE TABLE race_crew ( +-- EventCrew table +CREATE TABLE event_crew ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - race_id UUID REFERENCES races(id) ON DELETE CASCADE NOT NULL, - crew_member_id UUID REFERENCES crew_members(id) ON DELETE CASCADE NOT NULL, + event_id UUID REFERENCES events(id) ON DELETE CASCADE NOT NULL, + crew_id UUID REFERENCES crew(id) ON DELETE CASCADE NOT NULL, + role VARCHAR(50) CHECK (role IN ('crew_chief', 'medical', 'nutrition', 'logistics', 'general')), + start_date_time TIMESTAMP WITH TIME ZONE, + end_date_time TIMESTAMP WITH TIME ZONE, + is_active BOOLEAN DEFAULT TRUE, + notes TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - UNIQUE(race_id, crew_member_id) + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); --- Create aid_station_crew table (junction table for many-to-many relationship) +-- AidStationCrew table CREATE TABLE aid_station_crew ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - aid_station_id UUID REFERENCES aid_stations(id) ON DELETE CASCADE NOT NULL, - crew_member_id UUID REFERENCES crew_members(id) ON DELETE CASCADE NOT NULL, + event_aid_station_id UUID REFERENCES event_aid_stations(id) ON DELETE CASCADE NOT NULL, + event_crew_id UUID REFERENCES event_crew(id) ON DELETE CASCADE NOT NULL, + start_date_time TIMESTAMP WITH TIME ZONE, + end_date_time TIMESTAMP WITH TIME ZONE, + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Items and Inventory Management + +-- Items table +CREATE TABLE items ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + name VARCHAR(255) NOT NULL, + description TEXT, + category VARCHAR(50) CHECK (category IN ('gear', 'nutrition', 'hydration', 'medical', 'safety')), + subcategory VARCHAR(100), -- headlamp, energy_gel, electrolyte, etc. + weight INTEGER, -- grams + volume INTEGER, -- ml - for liquids + is_consumable BOOLEAN DEFAULT FALSE, + runtime_minutes INTEGER, -- for items with finite use time + unit_of_measure VARCHAR(50) CHECK (unit_of_measure IN ('piece', 'ml', 'gram', 'serving')), + storage_instructions TEXT, + expiration_tracking BOOLEAN DEFAULT FALSE, + cost DECIMAL(10, 2), -- for inventory valuation + supplier VARCHAR(255), created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - UNIQUE(aid_station_id, crew_member_id) + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); --- Create drop_bags table -CREATE TABLE drop_bag_templates ( +-- UserItems table (User's Personal Inventory) +CREATE TABLE user_items ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL, - name TEXT NOT NULL, - items JSONB NOT NULL, + user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, + item_id UUID REFERENCES items(id) ON DELETE CASCADE NOT NULL, + total_quantity INTEGER NOT NULL, + available_quantity INTEGER NOT NULL, + reserved_quantity INTEGER NOT NULL, -- allocated to events but not consumed + unit_cost DECIMAL(10, 2), + purchase_date DATE, + expiration_date DATE, + condition VARCHAR(50) CHECK (condition IN ('new', 'good', 'fair', 'poor')), + notes TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); --- Create race_drop_bags table -CREATE TABLE race_drop_bags ( +-- ItemRequirements table +CREATE TABLE item_requirements ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - race_id UUID REFERENCES races(id) ON DELETE CASCADE NOT NULL, - aid_station_id UUID REFERENCES aid_stations(id) ON DELETE CASCADE, - template_id UUID REFERENCES drop_bag_templates(id) ON DELETE SET NULL, - name TEXT NOT NULL, - items JSONB NOT NULL, + primary_item_id UUID REFERENCES items(id) ON DELETE CASCADE NOT NULL, + required_item_id UUID REFERENCES items(id) ON DELETE CASCADE NOT NULL, + required_quantity INTEGER NOT NULL, + required_unit_of_measure VARCHAR(50), + relationship_type VARCHAR(50) CHECK (relationship_type IN ('requires', 'pairs_with', 'enhances')), + notes TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); --- Create gear_items table -CREATE TABLE gear_items ( +-- Dropbag System + +-- Dropbags table +CREATE TABLE dropbags ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL, - name TEXT NOT NULL, - category TEXT NOT NULL, - weight NUMERIC, + user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, + name VARCHAR(255) NOT NULL, description TEXT, - quantity INTEGER DEFAULT 1, - retired BOOLEAN DEFAULT FALSE, + is_template BOOLEAN DEFAULT FALSE, + template_category VARCHAR(100), -- for organizing templates + max_weight INTEGER, -- grams + color VARCHAR(50), -- for physical identification + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- EventDropbags table (Event-specific dropbag instances) +CREATE TABLE event_dropbags ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_id UUID REFERENCES events(id) ON DELETE CASCADE NOT NULL, + dropbag_id UUID REFERENCES dropbags(id) ON DELETE CASCADE NOT NULL, + user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, + status VARCHAR(50) CHECK (status IN ('planned', 'packed', 'deployed', 'retrieved')), + actual_weight INTEGER, -- grams + packing_notes TEXT, + deployment_notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- DropbagItems table +CREATE TABLE dropbag_items ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_dropbag_id UUID REFERENCES event_dropbags(id) ON DELETE CASCADE NOT NULL, + item_id UUID REFERENCES items(id) ON DELETE CASCADE NOT NULL, + planned_quantity INTEGER NOT NULL, + actual_quantity INTEGER, -- what was actually packed + is_mandatory BOOLEAN DEFAULT FALSE, + consumed_quantity INTEGER DEFAULT 0, -- tracked during event + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- DropbagDeployments table +CREATE TABLE dropbag_deployments ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_dropbag_id UUID REFERENCES event_dropbags(id) ON DELETE CASCADE NOT NULL, + event_aid_station_id UUID REFERENCES event_aid_stations(id) ON DELETE CASCADE NOT NULL, + deployment_date_time TIMESTAMP WITH TIME ZONE, + retrieval_date_time TIMESTAMP WITH TIME ZONE, + deployed_by UUID REFERENCES event_crew(id) ON DELETE SET NULL, + retrieved_by UUID REFERENCES event_crew(id) ON DELETE SET NULL, + status VARCHAR(50) CHECK (status IN ('deployed', 'accessed', 'retrieved', 'lost')), + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Consumption and Performance Tracking + +-- EventConsumption table +CREATE TABLE event_consumption ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_id UUID REFERENCES events(id) ON DELETE CASCADE NOT NULL, + user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, + item_id UUID REFERENCES items(id) ON DELETE CASCADE NOT NULL, + event_aid_station_id UUID REFERENCES event_aid_stations(id) ON DELETE SET NULL, + event_dropbag_id UUID REFERENCES event_dropbags(id) ON DELETE SET NULL, + quantity INTEGER NOT NULL, + consumption_date_time TIMESTAMP WITH TIME ZONE, + consumption_type VARCHAR(50) CHECK (consumption_type IN ('planned', 'actual')), + effectiveness VARCHAR(50) CHECK (effectiveness IN ('very_helpful', 'helpful', 'neutral', 'unhelpful')), + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- AthleteEventPerformance table +CREATE TABLE athlete_event_performance ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_id UUID REFERENCES events(id) ON DELETE CASCADE NOT NULL, + user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, + status VARCHAR(50) CHECK (status IN ('registered', 'started', 'finished', 'dnf', 'dsq')), + start_date_time TIMESTAMP WITH TIME ZONE, + finish_date_time TIMESTAMP WITH TIME ZONE, + total_time_minutes INTEGER, + notes TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); --- Create race_gear table (junction table for many-to-many relationship) -CREATE TABLE race_gear ( +-- AidStationVisits table +CREATE TABLE aid_station_visits ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - race_id UUID REFERENCES races(id) ON DELETE CASCADE NOT NULL, - gear_item_id UUID REFERENCES gear_items(id) ON DELETE CASCADE NOT NULL, - is_packed BOOLEAN DEFAULT FALSE, + athlete_event_performance_id UUID REFERENCES athlete_event_performance(id) ON DELETE CASCADE NOT NULL, + event_aid_station_id UUID REFERENCES event_aid_stations(id) ON DELETE CASCADE NOT NULL, + arrival_date_time TIMESTAMP WITH TIME ZONE, + departure_date_time TIMESTAMP WITH TIME ZONE, + rest_duration_minutes INTEGER, + notes TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - UNIQUE(race_id, gear_item_id) + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); --- Create pacer_gear table -CREATE TABLE pacer_gear ( +-- Support and Performance Tracking + +-- Pacers table +CREATE TABLE pacers ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - race_id UUID REFERENCES races(id) ON DELETE CASCADE NOT NULL, - name TEXT NOT NULL, - is_mandatory BOOLEAN DEFAULT TRUE, + event_id UUID REFERENCES events(id) ON DELETE CASCADE NOT NULL, + athlete_user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, -- the athlete being paced + pacer_user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, -- the person doing the pacing + start_aid_station_id UUID REFERENCES event_aid_stations(id) ON DELETE CASCADE NOT NULL, -- where pacing begins + end_aid_station_id UUID REFERENCES event_aid_stations(id) ON DELETE SET NULL, -- where pacing ends (nullable for full race) + status VARCHAR(50) CHECK (status IN ('registered', 'active', 'completed', 'dnf', 'withdrawn')), + start_date_time TIMESTAMP WITH TIME ZONE, + end_date_time TIMESTAMP WITH TIME ZONE, + total_time_minutes INTEGER, + pacing_notes TEXT, + emergency_contact_info TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); --- Create nutrition_items table -CREATE TABLE nutrition_items ( +-- SupportVehicles table +CREATE TABLE support_vehicles ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL, - name TEXT NOT NULL, - calories INTEGER, - weight NUMERIC, - category TEXT, + event_id UUID REFERENCES events(id) ON DELETE CASCADE NOT NULL, + athlete_user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, -- athlete being supported + vehicle_type VARCHAR(50) CHECK (vehicle_type IN ('car', 'van', 'bike', 'atv', 'other')), + license_plate VARCHAR(50), + status VARCHAR(50) CHECK (status IN ('registered', 'active', 'completed', 'withdrawn')), + start_location VARCHAR(255), + current_latitude DECIMAL(10, 7), + current_longitude DECIMAL(10, 7), + last_location_update TIMESTAMP WITH TIME ZONE, + driver_event_crew_id UUID REFERENCES event_crew(id) ON DELETE SET NULL, + notes TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); --- Create nutrition_plans table -CREATE TABLE nutrition_plans ( +-- SupportVehicleCrew table +CREATE TABLE support_vehicle_crew ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - race_id UUID REFERENCES races(id) ON DELETE CASCADE NOT NULL, - calorie_goal INTEGER, - calorie_interval INTEGER, -- in minutes - hydration_goal INTEGER, -- in ml - hydration_interval INTEGER, -- in minutes - dietary_preferences TEXT, + support_vehicle_id UUID REFERENCES support_vehicles(id) ON DELETE CASCADE NOT NULL, + event_crew_id UUID REFERENCES event_crew(id) ON DELETE CASCADE NOT NULL, + role VARCHAR(50) CHECK (role IN ('driver', 'navigator', 'crew_chief', 'support')), + start_date_time TIMESTAMP WITH TIME ZONE, + end_date_time TIMESTAMP WITH TIME ZONE, + is_active BOOLEAN DEFAULT TRUE, + notes TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); --- Create nutrition_plan_items table (junction table) -CREATE TABLE nutrition_plan_items ( +-- AthleteKits table (Starting/Carrying Gear) +CREATE TABLE athlete_kits ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - nutrition_plan_id UUID REFERENCES nutrition_plans(id) ON DELETE CASCADE NOT NULL, - nutrition_item_id UUID REFERENCES nutrition_items(id) ON DELETE CASCADE NOT NULL, - quantity INTEGER DEFAULT 1, - timing TEXT, + event_id UUID REFERENCES events(id) ON DELETE CASCADE NOT NULL, + user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, + kit_type VARCHAR(50) CHECK (kit_type IN ('starting_gear', 'mandatory_gear', 'carried_nutrition', 'carried_hydration')), + total_weight INTEGER, -- grams + status VARCHAR(50) CHECK (status IN ('planned', 'packed', 'race_ready', 'post_race')), + packing_date_time TIMESTAMP WITH TIME ZONE, + notes TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); --- Create course_notes table -CREATE TABLE course_notes ( +-- AthleteKitItems table +CREATE TABLE athlete_kit_items ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - race_id UUID REFERENCES races(id) ON DELETE CASCADE NOT NULL, - distance_point NUMERIC, - note TEXT NOT NULL, + athlete_kit_id UUID REFERENCES athlete_kits(id) ON DELETE CASCADE NOT NULL, + item_id UUID REFERENCES items(id) ON DELETE CASCADE NOT NULL, + quantity INTEGER NOT NULL, + is_mandatory BOOLEAN DEFAULT FALSE, + item_condition VARCHAR(50) CHECK (item_condition IN ('new', 'good', 'fair', 'poor')), + consumed_quantity INTEGER DEFAULT 0, -- for nutrition/hydration tracking + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- SupportVehicleInventory table +CREATE TABLE support_vehicle_inventory ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + support_vehicle_id UUID REFERENCES support_vehicles(id) ON DELETE CASCADE NOT NULL, + item_id UUID REFERENCES items(id) ON DELETE CASCADE NOT NULL, + quantity INTEGER NOT NULL, + consumed_quantity INTEGER DEFAULT 0, + restock_location VARCHAR(255), -- where item was added to vehicle + notes TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- Create indexes for performance -CREATE INDEX race_backups_user_id_idx ON race_backups(user_id); -CREATE INDEX races_user_id_idx ON races(user_id); -CREATE INDEX aid_stations_race_id_idx ON aid_stations(race_id); -CREATE INDEX crew_members_user_id_idx ON crew_members(user_id); -CREATE INDEX race_crew_race_id_idx ON race_crew(race_id); -CREATE INDEX race_crew_crew_member_id_idx ON race_crew(crew_member_id); -CREATE INDEX aid_station_crew_aid_station_id_idx ON aid_station_crew(aid_station_id); -CREATE INDEX aid_station_crew_crew_member_id_idx ON aid_station_crew(crew_member_id); -CREATE INDEX drop_bag_templates_user_id_idx ON drop_bag_templates(user_id); -CREATE INDEX race_drop_bags_race_id_idx ON race_drop_bags(race_id); -CREATE INDEX race_drop_bags_aid_station_id_idx ON race_drop_bags(aid_station_id); -CREATE INDEX gear_items_user_id_idx ON gear_items(user_id); -CREATE INDEX race_gear_race_id_idx ON race_gear(race_id); -CREATE INDEX race_gear_gear_item_id_idx ON race_gear(gear_item_id); -CREATE INDEX pacer_gear_race_id_idx ON pacer_gear(race_id); -CREATE INDEX nutrition_items_user_id_idx ON nutrition_items(user_id); -CREATE INDEX nutrition_plans_race_id_idx ON nutrition_plans(race_id); -CREATE INDEX nutrition_plan_items_nutrition_plan_id_idx ON nutrition_plan_items(nutrition_plan_id); -CREATE INDEX nutrition_plan_items_nutrition_item_id_idx ON nutrition_plan_items(nutrition_item_id); -CREATE INDEX course_notes_race_id_idx ON course_notes(race_id); -CREATE INDEX course_notes_distance_point_idx ON course_notes(distance_point); +CREATE INDEX idx_athlete_profiles_user_id ON athlete_profiles(user_id); +CREATE INDEX idx_events_created_by ON events(created_by); +CREATE INDEX idx_event_aid_stations_event_id ON event_aid_stations(event_id); +CREATE INDEX idx_event_aid_stations_aid_station_id ON event_aid_stations(aid_station_id); +CREATE INDEX idx_event_crew_event_id ON event_crew(event_id); +CREATE INDEX idx_event_crew_crew_id ON event_crew(crew_id); +CREATE INDEX idx_aid_station_crew_event_aid_station_id ON aid_station_crew(event_aid_station_id); +CREATE INDEX idx_aid_station_crew_event_crew_id ON aid_station_crew(event_crew_id); +CREATE INDEX idx_user_items_user_id ON user_items(user_id); +CREATE INDEX idx_user_items_item_id ON user_items(item_id); +CREATE INDEX idx_item_requirements_primary_item_id ON item_requirements(primary_item_id); +CREATE INDEX idx_item_requirements_required_item_id ON item_requirements(required_item_id); +CREATE INDEX idx_dropbags_user_id ON dropbags(user_id); +CREATE INDEX idx_event_dropbags_event_id ON event_dropbags(event_id); +CREATE INDEX idx_event_dropbags_dropbag_id ON event_dropbags(dropbag_id); +CREATE INDEX idx_event_dropbags_user_id ON event_dropbags(user_id); +CREATE INDEX idx_dropbag_items_event_dropbag_id ON dropbag_items(event_dropbag_id); +CREATE INDEX idx_dropbag_items_item_id ON dropbag_items(item_id); +CREATE INDEX idx_dropbag_deployments_event_dropbag_id ON dropbag_deployments(event_dropbag_id); +CREATE INDEX idx_dropbag_deployments_event_aid_station_id ON dropbag_deployments(event_aid_station_id); +CREATE INDEX idx_event_consumption_event_id ON event_consumption(event_id); +CREATE INDEX idx_event_consumption_user_id ON event_consumption(user_id); +CREATE INDEX idx_event_consumption_item_id ON event_consumption(item_id); +CREATE INDEX idx_athlete_event_performance_event_id ON athlete_event_performance(event_id); +CREATE INDEX idx_athlete_event_performance_user_id ON athlete_event_performance(user_id); +CREATE INDEX idx_aid_station_visits_athlete_event_performance_id ON aid_station_visits(athlete_event_performance_id); +CREATE INDEX idx_aid_station_visits_event_aid_station_id ON aid_station_visits(event_aid_station_id); +CREATE INDEX idx_pacers_event_id ON pacers(event_id); +CREATE INDEX idx_pacers_athlete_user_id ON pacers(athlete_user_id); +CREATE INDEX idx_pacers_pacer_user_id ON pacers(pacer_user_id); +CREATE INDEX idx_support_vehicles_event_id ON support_vehicles(event_id); +CREATE INDEX idx_support_vehicles_athlete_user_id ON support_vehicles(athlete_user_id); +CREATE INDEX idx_support_vehicle_crew_support_vehicle_id ON support_vehicle_crew(support_vehicle_id); +CREATE INDEX idx_support_vehicle_crew_event_crew_id ON support_vehicle_crew(event_crew_id); +CREATE INDEX idx_athlete_kits_event_id ON athlete_kits(event_id); +CREATE INDEX idx_athlete_kits_user_id ON athlete_kits(user_id); +CREATE INDEX idx_athlete_kit_items_athlete_kit_id ON athlete_kit_items(athlete_kit_id); +CREATE INDEX idx_athlete_kit_items_item_id ON athlete_kit_items(item_id); +CREATE INDEX idx_support_vehicle_inventory_support_vehicle_id ON support_vehicle_inventory(support_vehicle_id); +CREATE INDEX idx_support_vehicle_inventory_item_id ON support_vehicle_inventory(item_id); -- Set up Row Level Security (RLS) -ALTER TABLE profiles ENABLE ROW LEVEL SECURITY; -ALTER TABLE race_backups ENABLE ROW LEVEL SECURITY; -ALTER TABLE races ENABLE ROW LEVEL SECURITY; +ALTER TABLE users ENABLE ROW LEVEL SECURITY; +ALTER TABLE athlete_profiles ENABLE ROW LEVEL SECURITY; +ALTER TABLE events ENABLE ROW LEVEL SECURITY; ALTER TABLE aid_stations ENABLE ROW LEVEL SECURITY; -ALTER TABLE aid_station_checkins ENABLE ROW LEVEL SECURITY; -ALTER TABLE crew_members ENABLE ROW LEVEL SECURITY; -ALTER TABLE race_crew ENABLE ROW LEVEL SECURITY; +ALTER TABLE event_aid_stations ENABLE ROW LEVEL SECURITY; +ALTER TABLE crew ENABLE ROW LEVEL SECURITY; +ALTER TABLE event_crew ENABLE ROW LEVEL SECURITY; ALTER TABLE aid_station_crew ENABLE ROW LEVEL SECURITY; -ALTER TABLE drop_bag_templates ENABLE ROW LEVEL SECURITY; -ALTER TABLE race_drop_bags ENABLE ROW LEVEL SECURITY; -ALTER TABLE gear_items ENABLE ROW LEVEL SECURITY; -ALTER TABLE race_gear ENABLE ROW LEVEL SECURITY; -ALTER TABLE pacer_gear ENABLE ROW LEVEL SECURITY; -ALTER TABLE nutrition_items ENABLE ROW LEVEL SECURITY; -ALTER TABLE nutrition_plans ENABLE ROW LEVEL SECURITY; -ALTER TABLE nutrition_plan_items ENABLE ROW LEVEL SECURITY; -ALTER TABLE course_notes ENABLE ROW LEVEL SECURITY; - --- Create policies for profiles +ALTER TABLE items ENABLE ROW LEVEL SECURITY; +ALTER TABLE user_items ENABLE ROW LEVEL SECURITY; +ALTER TABLE item_requirements ENABLE ROW LEVEL SECURITY; +ALTER TABLE dropbags ENABLE ROW LEVEL SECURITY; +ALTER TABLE event_dropbags ENABLE ROW LEVEL SECURITY; +ALTER TABLE dropbag_items ENABLE ROW LEVEL SECURITY; +ALTER TABLE dropbag_deployments ENABLE ROW LEVEL SECURITY; +ALTER TABLE event_consumption ENABLE ROW LEVEL SECURITY; +ALTER TABLE athlete_event_performance ENABLE ROW LEVEL SECURITY; +ALTER TABLE aid_station_visits ENABLE ROW LEVEL SECURITY; +ALTER TABLE pacers ENABLE ROW LEVEL SECURITY; +ALTER TABLE support_vehicles ENABLE ROW LEVEL SECURITY; +ALTER TABLE support_vehicle_crew ENABLE ROW LEVEL SECURITY; +ALTER TABLE athlete_kits ENABLE ROW LEVEL SECURITY; +ALTER TABLE athlete_kit_items ENABLE ROW LEVEL SECURITY; +ALTER TABLE support_vehicle_inventory ENABLE ROW LEVEL SECURITY; + +-- Create policies for users CREATE POLICY "Users can view their own profile" - ON profiles FOR SELECT + ON users FOR SELECT USING (auth.uid() = id); CREATE POLICY "Users can update their own profile" - ON profiles FOR UPDATE + ON users FOR UPDATE USING (auth.uid() = id); --- Create policies for race_backups -CREATE POLICY "Users can view their own backups" - ON race_backups FOR SELECT - USING (auth.uid() = user_id); - -CREATE POLICY "Users can insert their own backups" - ON race_backups FOR INSERT - WITH CHECK (auth.uid() = user_id); - -CREATE POLICY "Users can update their own backups" - ON race_backups FOR UPDATE - USING (auth.uid() = user_id); - -CREATE POLICY "Users can delete their own backups" - ON race_backups FOR DELETE - USING (auth.uid() = user_id); - --- Create policies for races -CREATE POLICY "Users can view their own races" - ON races FOR SELECT - USING (auth.uid() = user_id); - -CREATE POLICY "Users can insert their own races" - ON races FOR INSERT - WITH CHECK (auth.uid() = user_id); - -CREATE POLICY "Users can update their own races" - ON races FOR UPDATE - USING (auth.uid() = user_id); - -CREATE POLICY "Users can delete their own races" - ON races FOR DELETE - USING (auth.uid() = user_id); - --- Create policies for aid_stations -CREATE POLICY "Users can view their own aid stations" - ON aid_stations FOR SELECT - USING (EXISTS ( - SELECT 1 FROM races - WHERE races.id = aid_stations.race_id - AND races.user_id = auth.uid() - )); - -CREATE POLICY "Users can insert their own aid stations" - ON aid_stations FOR INSERT - WITH CHECK (EXISTS ( - SELECT 1 FROM races - WHERE races.id = aid_stations.race_id - AND races.user_id = auth.uid() - )); - -CREATE POLICY "Users can update their own aid stations" - ON aid_stations FOR UPDATE - USING (EXISTS ( - SELECT 1 FROM races - WHERE races.id = aid_stations.race_id - AND races.user_id = auth.uid() - )); - -CREATE POLICY "Users can delete their own aid stations" - ON aid_stations FOR DELETE - USING (EXISTS ( - SELECT 1 FROM races - WHERE races.id = aid_stations.race_id - AND races.user_id = auth.uid() - )); - --- Create policies for crew_members -CREATE POLICY "Users can view their own crew members" - ON crew_members FOR SELECT - USING (auth.uid() = user_id); - -CREATE POLICY "Users can insert their own crew members" - ON crew_members FOR INSERT - WITH CHECK (auth.uid() = user_id); - -CREATE POLICY "Users can update their own crew members" - ON crew_members FOR UPDATE - USING (auth.uid() = user_id); - -CREATE POLICY "Users can delete their own crew members" - ON crew_members FOR DELETE - USING (auth.uid() = user_id); - --- Create policies for drop_bag_templates -CREATE POLICY "Users can view their own drop bag templates" - ON drop_bag_templates FOR SELECT - USING (auth.uid() = user_id); - -CREATE POLICY "Users can insert their own drop bag templates" - ON drop_bag_templates FOR INSERT - WITH CHECK (auth.uid() = user_id); - -CREATE POLICY "Users can update their own drop bag templates" - ON drop_bag_templates FOR UPDATE - USING (auth.uid() = user_id); - -CREATE POLICY "Users can delete their own drop bag templates" - ON drop_bag_templates FOR DELETE - USING (auth.uid() = user_id); - --- Create policies for gear_items -CREATE POLICY "Users can view their own gear items" - ON gear_items FOR SELECT - USING (auth.uid() = user_id); - -CREATE POLICY "Users can insert their own gear items" - ON gear_items FOR INSERT - WITH CHECK (auth.uid() = user_id); - -CREATE POLICY "Users can update their own gear items" - ON gear_items FOR UPDATE - USING (auth.uid() = user_id); - -CREATE POLICY "Users can delete their own gear items" - ON gear_items FOR DELETE - USING (auth.uid() = user_id); - --- Create policies for nutrition_items -CREATE POLICY "Users can view their own nutrition items" - ON nutrition_items FOR SELECT - USING (auth.uid() = user_id); - -CREATE POLICY "Users can insert their own nutrition items" - ON nutrition_items FOR INSERT - WITH CHECK (auth.uid() = user_id); - -CREATE POLICY "Users can update their own nutrition items" - ON nutrition_items FOR UPDATE - USING (auth.uid() = user_id); - -CREATE POLICY "Users can delete their own nutrition items" - ON nutrition_items FOR DELETE - USING (auth.uid() = user_id); - -- Function to handle user creation CREATE OR REPLACE FUNCTION public.handle_new_user() RETURNS TRIGGER AS $$ BEGIN - INSERT INTO public.profiles ( + INSERT INTO public.users ( id, name, - email, - profile_image, - location, - bio, - preferences, - stats, - achievements + email ) VALUES ( NEW.id, COALESCE(NEW.raw_user_meta_data->>'name', NEW.email), - NEW.email, - NULL, - '', - '', - '{"distanceUnit": "miles", "elevationUnit": "ft", "notifications": true, "darkMode": false}'::jsonb, - '{"racesPlanned": 0, "racesCompleted": 0, "totalDistance": 0, "appUsage": 0, "longestRace": 0}'::jsonb, - '[]'::jsonb + NEW.email ); RETURN NEW; END; $$ LANGUAGE plpgsql SECURITY DEFINER; --- Trigger to automatically create profile on signup +-- Trigger to automatically create user on signup CREATE TRIGGER on_auth_user_created AFTER INSERT ON auth.users FOR EACH ROW EXECUTE PROCEDURE public.handle_new_user(); From f035a185969a583905c69fea6bfba88f0a85e36c Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 11 Aug 2025 17:38:48 +0000 Subject: [PATCH 2/2] Add instructions for applying schema changes to Supabase --- supabase/APPLY_SCHEMA.md | 93 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 supabase/APPLY_SCHEMA.md diff --git a/supabase/APPLY_SCHEMA.md b/supabase/APPLY_SCHEMA.md new file mode 100644 index 0000000..5a57a01 --- /dev/null +++ b/supabase/APPLY_SCHEMA.md @@ -0,0 +1,93 @@ +# Applying the New Schema to Supabase + +This document provides instructions for applying the new schema to your Supabase project. + +## Prerequisites + +- Supabase CLI installed (if using CLI method) +- Access to the Supabase project dashboard +- Database backup (recommended before making changes) + +## Option 1: Using the Supabase Dashboard + +1. **Backup Your Data** + - Go to the Supabase Dashboard + - Navigate to your project + - Go to Database > Backups + - Create a new backup + +2. **Apply the Schema** + - Go to the SQL Editor + - Open a new query + - Copy the contents of `migrations/20250811173500_new_schema.sql` + - Paste into the SQL Editor + - Run the query + +3. **Verify the Changes** + - Go to the Table Editor + - Verify that all tables have been created correctly + - Check that indexes and constraints are in place + +## Option 2: Using the Supabase CLI + +1. **Install the Supabase CLI** (if not already installed) + ```bash + npm install -g supabase + ``` + +2. **Login to Supabase** + ```bash + supabase login + ``` + +3. **Link Your Project** + ```bash + supabase link --project-ref + ``` + +4. **Push the Migration** + ```bash + supabase db push + ``` + +5. **Verify the Changes** + - Go to the Supabase Dashboard + - Navigate to your project + - Go to the Table Editor + - Verify that all tables have been created correctly + +## Data Migration Considerations + +Since this is a complete schema redesign, you'll need to migrate your data from the old schema to the new one. Here are some general steps: + +1. **Export Existing Data** + - Use the Supabase Dashboard to export data from existing tables + - Alternatively, use the Supabase CLI or direct PostgreSQL commands + +2. **Transform Data** + - Create scripts to transform data from the old format to the new format + - Map fields from old tables to new tables + +3. **Import Data** + - Use the Supabase Dashboard to import data into new tables + - Alternatively, use the Supabase CLI or direct PostgreSQL commands + +4. **Verify Data Integrity** + - Check that all data has been migrated correctly + - Verify relationships between tables + +## Troubleshooting + +If you encounter issues during the schema application: + +1. **Check Error Messages** + - Look for specific error messages in the SQL Editor or CLI output + - Address each error individually + +2. **Rollback if Necessary** + - If major issues occur, restore from your backup + - Try applying the schema changes in smaller batches + +3. **Seek Help** + - Consult the Supabase documentation + - Reach out to the development team for assistance \ No newline at end of file