Yaltopia-FIFA/supabase/migrations/20250524000006_portal_roles.sql
Kirubel-Kibru-Yaltopia 89440985f1
Some checks failed
Deploy to Cloudflare Workers / deploy (push) Has been cancelled
x
2026-05-24 21:46:10 +03:00

196 lines
6.5 KiB
PL/PgSQL

-- Portal roles, league masters, team extras, support issues
CREATE TYPE portal_role AS ENUM ('league_master', 'manager');
ALTER TABLE profiles
ADD COLUMN IF NOT EXISTS portal_role portal_role NOT NULL DEFAULT 'manager';
ALTER TABLE teams
ADD COLUMN IF NOT EXISTS nickname TEXT,
ADD COLUMN IF NOT EXISTS icon TEXT NOT NULL DEFAULT 'shield';
CREATE TABLE IF NOT EXISTS league_masters (
league_id UUID NOT NULL REFERENCES leagues(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
assigned_by UUID REFERENCES auth.users(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (league_id, user_id)
);
CREATE TABLE IF NOT EXISTS support_issues (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
league_id UUID NOT NULL REFERENCES leagues(id) ON DELETE CASCADE,
competition_id UUID REFERENCES competitions(id) ON DELETE SET NULL,
submitted_by UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
subject TEXT NOT NULL,
body TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'in_progress', 'resolved')),
master_reply TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TRIGGER support_issues_updated_at
BEFORE UPDATE ON support_issues
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
-- Helpers
CREATE OR REPLACE FUNCTION is_global_league_master()
RETURNS BOOLEAN AS $$
SELECT EXISTS (
SELECT 1 FROM profiles WHERE id = auth.uid() AND portal_role = 'league_master'
);
$$ LANGUAGE sql SECURITY DEFINER STABLE;
CREATE OR REPLACE FUNCTION is_league_master(p_league_id UUID)
RETURNS BOOLEAN AS $$
SELECT is_global_league_master()
OR EXISTS (SELECT 1 FROM leagues WHERE id = p_league_id AND created_by = auth.uid())
OR EXISTS (
SELECT 1 FROM league_masters WHERE league_id = p_league_id AND user_id = auth.uid()
);
$$ LANGUAGE sql SECURITY DEFINER STABLE;
-- Extend organizer check to include assigned masters
CREATE OR REPLACE FUNCTION is_league_organizer(p_league_id UUID)
RETURNS BOOLEAN AS $$
SELECT is_league_master(p_league_id);
$$ LANGUAGE sql SECURITY DEFINER STABLE;
-- Profile trigger: portal role from signup metadata
CREATE OR REPLACE FUNCTION handle_new_user()
RETURNS TRIGGER AS $$
DECLARE
v_role portal_role;
BEGIN
BEGIN
v_role := COALESCE(
(NEW.raw_user_meta_data->>'portal_role')::portal_role,
'manager'::portal_role
);
EXCEPTION WHEN OTHERS THEN
v_role := 'manager';
END;
INSERT INTO public.profiles (id, display_name, portal_role)
VALUES (
NEW.id,
COALESCE(NEW.raw_user_meta_data->>'display_name', split_part(NEW.email, '@', 1)),
v_role
)
ON CONFLICT (id) DO UPDATE SET
display_name = COALESCE(EXCLUDED.display_name, profiles.display_name),
portal_role = COALESCE(EXCLUDED.portal_role, profiles.portal_role);
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- RLS new tables
ALTER TABLE league_masters ENABLE ROW LEVEL SECURITY;
ALTER TABLE support_issues ENABLE ROW LEVEL SECURITY;
CREATE POLICY league_masters_select ON league_masters FOR SELECT TO authenticated
USING (is_league_master(league_id) OR user_id = auth.uid());
CREATE POLICY league_masters_insert ON league_masters FOR INSERT TO authenticated
WITH CHECK (is_league_master(league_id));
CREATE POLICY league_masters_delete ON league_masters FOR DELETE TO authenticated
USING (is_league_master(league_id));
CREATE POLICY support_issues_select ON support_issues FOR SELECT TO authenticated
USING (
submitted_by = auth.uid()
OR is_league_master(league_id)
);
CREATE POLICY support_issues_insert ON support_issues FOR INSERT TO authenticated
WITH CHECK (submitted_by = auth.uid());
CREATE POLICY support_issues_update ON support_issues FOR UPDATE TO authenticated
USING (is_league_master(league_id));
-- League delete (masters only)
CREATE POLICY leagues_delete ON leagues FOR DELETE TO authenticated
USING (is_league_master(id));
-- Team delete in draft competitions
CREATE POLICY teams_delete ON teams FOR DELETE TO authenticated
USING (
EXISTS (
SELECT 1 FROM competitions c
WHERE c.id = competition_id
AND c.status = 'draft'
AND is_league_master(c.league_id)
)
);
-- Recreate standings view with nickname/icon (must DROP first: REPLACE cannot reorder columns)
DROP VIEW IF EXISTS competition_standings;
CREATE VIEW competition_standings AS
SELECT
t.competition_id,
t.id AS team_id,
t.name AS team_name,
t.nickname AS team_nickname,
t.icon AS team_icon,
t.logo_path,
COUNT(m.id) FILTER (WHERE m.status = 'completed') AS played,
COUNT(m.id) FILTER (
WHERE m.status = 'completed'
AND (
(m.home_team_id = t.id AND m.home_score > m.away_score)
OR (m.away_team_id = t.id AND m.away_score > m.home_score)
)
) AS won,
COUNT(m.id) FILTER (
WHERE m.status = 'completed' AND m.home_score = m.away_score
) AS drawn,
COUNT(m.id) FILTER (
WHERE m.status = 'completed'
AND (
(m.home_team_id = t.id AND m.home_score < m.away_score)
OR (m.away_team_id = t.id AND m.away_score < m.home_score)
)
) AS lost,
COALESCE(SUM(
CASE
WHEN m.status = 'completed' AND m.home_team_id = t.id THEN m.home_score
WHEN m.status = 'completed' AND m.away_team_id = t.id THEN m.away_score
ELSE 0
END
), 0)::INT AS goals_for,
COALESCE(SUM(
CASE
WHEN m.status = 'completed' AND m.home_team_id = t.id THEN m.away_score
WHEN m.status = 'completed' AND m.away_team_id = t.id THEN m.home_score
ELSE 0
END
), 0)::INT AS goals_against,
COALESCE(SUM(
CASE
WHEN m.status = 'completed' AND m.home_team_id = t.id THEN m.home_score - m.away_score
WHEN m.status = 'completed' AND m.away_team_id = t.id THEN m.away_score - m.home_score
ELSE 0
END
), 0)::INT AS goal_difference,
COALESCE(SUM(
CASE
WHEN m.status <> 'completed' THEN 0
WHEN m.home_score = m.away_score THEN 1
WHEN m.home_team_id = t.id AND m.home_score > m.away_score THEN 3
WHEN m.away_team_id = t.id AND m.away_score > m.home_score THEN 3
ELSE 0
END
), 0)::INT AS points
FROM teams t
LEFT JOIN matches m ON m.competition_id = t.competition_id
AND (m.home_team_id = t.id OR m.away_team_id = t.id)
GROUP BY t.competition_id, t.id, t.name, t.nickname, t.icon, t.logo_path;
CREATE OR REPLACE FUNCTION get_user_id_by_email(p_email TEXT)
RETURNS UUID AS $$
SELECT id FROM auth.users WHERE email = lower(trim(p_email)) LIMIT 1;
$$ LANGUAGE sql SECURITY DEFINER STABLE;