-- Yaltopia FIFA Tournament System - Initial Schema -- Extensions CREATE EXTENSION IF NOT EXISTS "pgcrypto"; -- Enums CREATE TYPE player_status AS ENUM ('active', 'inactive'); CREATE TYPE team_member_role AS ENUM ('manager', 'captain'); CREATE TYPE tournament_mode AS ENUM ('league', 'cup'); CREATE TYPE competition_status AS ENUM ('draft', 'active', 'playoffs', 'completed'); CREATE TYPE match_status AS ENUM ( 'scheduled', 'schedule_pending', 'schedule_confirmed', 'awaiting_results', 'result_submitted', 'pending_approval', 'disputed', 'completed', 'cancelled' ); CREATE TYPE result_status AS ENUM ( 'awaiting_results', 'result_submitted', 'pending_approval', 'disputed', 'completed' ); CREATE TYPE roster_event_type AS ENUM ('add', 'remove'); CREATE TYPE transfer_status AS ENUM ('pending', 'completed', 'voided'); CREATE TYPE signature_type AS ENUM ('schedule'); -- Profiles CREATE TABLE profiles ( id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE, display_name TEXT, avatar_url TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- Leagues CREATE TABLE leagues ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL, slug TEXT NOT NULL UNIQUE, description TEXT, created_by UUID REFERENCES auth.users(id) ON DELETE SET NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- Versioned league rules CREATE TABLE league_rules ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), league_id UUID NOT NULL REFERENCES leagues(id) ON DELETE CASCADE, version INT NOT NULL, rules JSONB NOT NULL DEFAULT '{}', created_by UUID REFERENCES auth.users(id) ON DELETE SET NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE (league_id, version) ); -- Competitions (seasons / cups) CREATE TABLE competitions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), league_id UUID NOT NULL REFERENCES leagues(id) ON DELETE CASCADE, name TEXT NOT NULL, tournament_mode tournament_mode NOT NULL DEFAULT 'league', status competition_status NOT NULL DEFAULT 'draft', timezone TEXT NOT NULL DEFAULT 'UTC', rules_snapshot JSONB, rule_set_version INT, created_by UUID REFERENCES auth.users(id) ON DELETE SET NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- Global player registry CREATE TABLE players ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), display_name TEXT NOT NULL, external_id TEXT, status player_status NOT NULL DEFAULT 'active', created_by UUID REFERENCES auth.users(id) ON DELETE SET NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- Teams CREATE TABLE teams ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), competition_id UUID NOT NULL REFERENCES competitions(id) ON DELETE CASCADE, name TEXT NOT NULL, logo_path TEXT, home_stadium_name TEXT, cup_seed INT, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- Team members (manager / captain) CREATE TABLE team_members ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE, user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE, role team_member_role NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE (team_id, user_id) ); -- Team availability windows CREATE TABLE team_availability ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE, day_of_week INT NOT NULL CHECK (day_of_week >= 0 AND day_of_week <= 6), start_time TIME, end_time TIME, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- Roster CREATE TABLE team_roster ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE, player_id UUID NOT NULL REFERENCES players(id) ON DELETE RESTRICT, competition_id UUID NOT NULL REFERENCES competitions(id) ON DELETE CASCADE, joined_at TIMESTAMPTZ NOT NULL DEFAULT now(), left_at TIMESTAMPTZ ); -- Roster events CREATE TABLE roster_events ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE, player_id UUID NOT NULL REFERENCES players(id) ON DELETE RESTRICT, competition_id UUID NOT NULL REFERENCES competitions(id) ON DELETE CASCADE, event_type roster_event_type NOT NULL, registered_by UUID NOT NULL REFERENCES auth.users(id), created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- Transfers CREATE TABLE transfers ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), competition_id UUID NOT NULL REFERENCES competitions(id) ON DELETE CASCADE, player_id UUID NOT NULL REFERENCES players(id) ON DELETE RESTRICT, from_team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE, to_team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE, status transfer_status NOT NULL DEFAULT 'completed', effective_at TIMESTAMPTZ NOT NULL DEFAULT now(), registered_by UUID NOT NULL REFERENCES auth.users(id), created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- Competition league managers CREATE TABLE competition_league_managers ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), competition_id UUID NOT NULL REFERENCES competitions(id) ON DELETE CASCADE, user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE (competition_id, user_id) ); -- Matches CREATE TABLE matches ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), competition_id UUID NOT NULL REFERENCES competitions(id) ON DELETE CASCADE, home_team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE, away_team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE, round INT, matchday INT, leg INT DEFAULT 1, bracket_round INT, bracket_slot INT, next_match_id UUID REFERENCES matches(id) ON DELETE SET NULL, status match_status NOT NULL DEFAULT 'scheduled', result_status result_status DEFAULT 'awaiting_results', scheduled_at TIMESTAMPTZ, proposed_scheduled_at TIMESTAMPTZ, venue TEXT, home_score INT, away_score INT, approved_by UUID REFERENCES auth.users(id), approved_at TIMESTAMPTZ, resolution_note TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- Schedule signatures CREATE TABLE match_signatures ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), match_id UUID NOT NULL REFERENCES matches(id) ON DELETE CASCADE, team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE, user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE, signature_type signature_type NOT NULL DEFAULT 'schedule', signed_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE (match_id, team_id, signature_type) ); -- Result submissions (per team) CREATE TABLE match_result_submissions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), match_id UUID NOT NULL REFERENCES matches(id) ON DELETE CASCADE, team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE, home_score INT NOT NULL, away_score INT NOT NULL, submitted_by UUID NOT NULL REFERENCES auth.users(id), submitted_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE (match_id, team_id) ); -- Player match stats CREATE TABLE match_player_stats ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), match_id UUID NOT NULL REFERENCES matches(id) ON DELETE CASCADE, player_id UUID NOT NULL REFERENCES players(id) ON DELETE RESTRICT, team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE, goals INT NOT NULL DEFAULT 0 CHECK (goals >= 0), assists INT NOT NULL DEFAULT 0 CHECK (assists >= 0), created_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE (match_id, player_id) ); -- Indexes CREATE INDEX idx_competitions_league ON competitions(league_id); CREATE INDEX idx_teams_competition ON teams(competition_id); CREATE INDEX idx_matches_competition ON matches(competition_id); CREATE INDEX idx_matches_status ON matches(competition_id, status); CREATE INDEX idx_team_roster_active ON team_roster(competition_id, player_id) WHERE left_at IS NULL; CREATE INDEX idx_team_members_user ON team_members(user_id); -- One manager per team CREATE UNIQUE INDEX idx_one_manager_per_team ON team_members(team_id) WHERE role = 'manager'; -- One active roster slot per player per competition CREATE UNIQUE INDEX idx_active_roster_per_player ON team_roster(competition_id, player_id) WHERE left_at IS NULL; -- Updated_at trigger CREATE OR REPLACE FUNCTION update_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = now(); RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER profiles_updated_at BEFORE UPDATE ON profiles FOR EACH ROW EXECUTE FUNCTION update_updated_at(); CREATE TRIGGER leagues_updated_at BEFORE UPDATE ON leagues FOR EACH ROW EXECUTE FUNCTION update_updated_at(); CREATE TRIGGER competitions_updated_at BEFORE UPDATE ON competitions FOR EACH ROW EXECUTE FUNCTION update_updated_at(); CREATE TRIGGER teams_updated_at BEFORE UPDATE ON teams FOR EACH ROW EXECUTE FUNCTION update_updated_at(); CREATE TRIGGER players_updated_at BEFORE UPDATE ON players FOR EACH ROW EXECUTE FUNCTION update_updated_at(); CREATE TRIGGER matches_updated_at BEFORE UPDATE ON matches FOR EACH ROW EXECUTE FUNCTION update_updated_at(); -- Auto-create profile on signup CREATE OR REPLACE FUNCTION handle_new_user() RETURNS TRIGGER AS $$ BEGIN INSERT INTO public.profiles (id, display_name) VALUES (NEW.id, COALESCE(NEW.raw_user_meta_data->>'display_name', NEW.email)); RETURN NEW; END; $$ LANGUAGE plpgsql SECURITY DEFINER; CREATE TRIGGER on_auth_user_created AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION handle_new_user();