در ایمیل بازیابی، لینک تغییر رمز را بدون باز کردن کپی کنید و اینجا بچسبانید. لینک یا رمز را در گفتگو نفرستید.
CREATE TABLE IF NOT EXISTS public.player_profiles (
id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
user_id uuid REFERENCES auth.users(id) ON DELETE SET NULL,
profile_name text NOT NULL,
data jsonb NOT NULL DEFAULT '{}',
updated_at timestamptz NOT NULL DEFAULT now(),
created_at timestamptz NOT NULL DEFAULT now(),
CONSTRAINT uq_player_profiles UNIQUE (profile_name)
);
CREATE INDEX IF NOT EXISTS idx_pp_name ON public.player_profiles(profile_name);
CREATE OR REPLACE FUNCTION public.set_updated_at()
RETURNS TRIGGER LANGUAGE plpgsql AS $$
BEGIN NEW.updated_at = now(); RETURN NEW; END; $$;
CREATE TRIGGER trg_pp_updated_at
BEFORE UPDATE ON public.player_profiles
FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
-- === جدول نقشها (برای کنترل دسترسی) ===
CREATE TABLE IF NOT EXISTS public.user_roles (
user_id uuid PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
role text NOT NULL DEFAULT 'player'
CHECK (role IN ('admin','coach','sc','medical','td','viewer','player')),
email text,
linked_profile text,
updated_at timestamptz DEFAULT now()
);
-- تابع کمکی: نقش کاربر جاری را بدون بازگشت بینهایت (recursive RLS) برمیگرداند
CREATE OR REPLACE FUNCTION public.my_role() RETURNS text
LANGUAGE sql SECURITY DEFINER STABLE SET search_path = public AS $$
SELECT role FROM public.user_roles WHERE user_id = auth.uid()
$$;
ALTER TABLE public.player_profiles ENABLE ROW LEVEL SECURITY;
-- کل کادر فنی (هر نقشی جز player/viewer) به همهی پروفایلهای این استقرار دسترسی کامل دارند
CREATE POLICY "staff_full_access" ON public.player_profiles FOR ALL
USING (public.my_role() IN ('admin','coach','sc','medical','td'))
WITH CHECK (public.my_role() IN ('admin','coach','sc','medical','td'));
-- نقش ناظر (رئیس فدراسیون / رئیس کمیته فنی و مشابه): فقط مشاهده، بدون امکان ویرایش یا حذف
CREATE POLICY "viewer_readonly_access" ON public.player_profiles FOR SELECT
USING (public.my_role() = 'viewer');
-- بازیکن فقط پروفایل لینکشدهی خودش را میبیند
CREATE POLICY "player_reads_own_linked_profile" ON public.player_profiles FOR SELECT
USING (public.my_role() = 'player' AND profile_name =
(SELECT linked_profile FROM public.user_roles WHERE user_id = auth.uid()));
ALTER TABLE public.user_roles ENABLE ROW LEVEL SECURITY;
CREATE POLICY "read_own_role_row" ON public.user_roles
FOR SELECT USING (auth.uid() = user_id);
CREATE POLICY "admin_reads_all" ON public.user_roles
FOR SELECT USING (public.my_role() = 'admin');
-- ⚠️ امنیتی — این سیاست باید مقدار ستون role را هم مقید کند، نه فقط مالکیت سطر.
-- بدون شرط role='player'، هر کاربری که ثبتنام کند میتواند مستقیماً سطری با
-- نقش 'admin' برای خودش درج کند و به پروندهی همهی ورزشکاران (از جمله آزمایش
-- خون) دسترسی کامل بگیرد. ستون linked_profile هم باید خالی بماند تا کاربر
-- نتواند خودش را به پروفایل دلخواه وصل کند.
CREATE POLICY "user_creates_own_row" ON public.user_roles
FOR INSERT WITH CHECK (
auth.uid() = user_id
AND role = 'player'
AND linked_profile IS NULL
);
CREATE POLICY "admin_updates_any_role" ON public.user_roles
FOR UPDATE USING (public.my_role() = 'admin')
WITH CHECK (public.my_role() = 'admin');
-- دفاع لایهی دوم: سطر نقش را سمت سرور بسازید تا اصلاً به درجِ سمت مرورگر
-- تکیه نشود. با این ماشه، حتی اگر سیاست بالا هم روزی سست شود، نقش پیشفرض
-- همیشه 'player' است.
CREATE OR REPLACE FUNCTION public.handle_new_user() RETURNS TRIGGER
LANGUAGE plpgsql SECURITY DEFINER SET search_path = public AS $$
BEGIN
INSERT INTO public.user_roles(user_id, role, email)
VALUES (NEW.id, 'player', NEW.email)
ON CONFLICT (user_id) DO NOTHING;
RETURN NEW;
END; $$;
DROP TRIGGER IF EXISTS trg_on_auth_user_created ON auth.users;
CREATE TRIGGER trg_on_auth_user_created
AFTER INSERT ON auth.users
FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();
-- ⚠️ همچنین در پنل Supabase → Authentication → Providers → Email،
-- گزینهی «Allow new users to sign up» را خاموش کنید و کاربران را با
-- دعوتنامه اضافه کنید. تا وقتی ثبتنام آزاد باز است، هر کسی روی اینترنت
-- میتواند یک حساب بسازد؛ سیاست بالا فقط جلوی ارتقای نقش را میگیرد.
-- اگر از نسخهی پیشین مهاجرت میکنید، اینها را هم یکبار اجرا کنید:
-- DROP POLICY IF EXISTS "user_creates_own_row" ON public.user_roles;
-- (سپس نسخهی جدید بالا را اجرا کنید)
-- و سطرهای نقشی را که خودِ کاربر ساخته بازبینی کنید:
-- SELECT user_id, email, role FROM public.user_roles WHERE role <> 'player';
-- اولین ادمین را اینجا تنظیم کنید (user_id از auth.users):
-- INSERT INTO public.user_roles(user_id,role,email) VALUES('YOUR-UUID','admin','admin@email.com') ON CONFLICT(user_id) DO UPDATE SET role='admin';
-- اگر از نسخه قبلی مهاجرت میکنید، پیش از اجرای بالا اینها را هم اجرا کنید:
-- ALTER TABLE public.user_roles DROP CONSTRAINT IF EXISTS user_roles_role_check;
-- ALTER TABLE public.user_roles ADD CONSTRAINT user_roles_role_check CHECK (role IN ('admin','coach','sc','medical','td','viewer','player'));
-- DROP POLICY IF EXISTS "users_manage_own" ON public.player_profiles;
-- DROP POLICY IF EXISTS "own_role_rw" ON public.user_roles;
-- DROP POLICY IF EXISTS "admin_rw_all" ON public.user_roles;
-- ALTER TABLE public.player_profiles DROP CONSTRAINT IF EXISTS uq_player_profiles;
-- ALTER TABLE public.player_profiles ALTER COLUMN user_id DROP NOT NULL;
-- ALTER TABLE public.player_profiles ADD CONSTRAINT uq_player_profiles UNIQUE (profile_name);
-- =================================================================
-- اختیاری — فقط اگر میخواهید از مدل رابطهای برای بار/آمادگی/زیستنشانگر
-- بهجای بلوک JSONB استفاده کنید (نگاه کنید به Load_Readiness_Domain_Model.md).
-- اجرای این بخش هیچ دادهی موجود را تغییر نمیدهد و هیچ کدی هنوز از آن
-- نمیخواند/نمینویسد — صرفاً جدول را برای گام بعدی آماده میکند.
-- =================================================================
CREATE TABLE IF NOT EXISTS public.monitoring_sessions (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
player_profile_id UUID NOT NULL REFERENCES public.player_profiles(id) ON DELETE CASCADE,
session_date DATE NOT NULL,
source TEXT NOT NULL DEFAULT 'manual' CHECK (source IN ('manual','csv_import')),
is_game_day BOOLEAN DEFAULT false,
training_load_au NUMERIC, duration_min NUMERIC, rpe NUMERIC,
distance_m NUMERIC, max_speed_kmh NUMERIC,
avg_hr_bpm NUMERIC, resting_hr_bpm NUMERIC, hrv_ms NUMERIC,
sleep_hours NUMERIC, sleep_quality SMALLINT,
fatigue_score SMALLINT, soreness_score SMALLINT,
cmj_height_cm NUMERIC, vo2max NUMERIC, readiness_score NUMERIC,
note TEXT, created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(player_profile_id, session_date, source)
);
CREATE TABLE IF NOT EXISTS public.pain_reports (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
session_id UUID NOT NULL REFERENCES public.monitoring_sessions(id) ON DELETE CASCADE,
body_area TEXT NOT NULL CHECK (body_area IN
('knee_r','knee_l','ankle_r','ankle_l','back','groin','hamstring','shoulder','neck'))
);
CREATE TABLE IF NOT EXISTS public.biomarker_panels (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
player_profile_id UUID NOT NULL REFERENCES public.player_profiles(id) ON DELETE CASCADE,
test_date DATE NOT NULL,
hemoglobin NUMERIC, ferritin NUMERIC, vitamin_d NUMERIC,
transferrin NUMERIC, testosterone NUMERIC, cortisol NUMERIC,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(player_profile_id, test_date)
);
ALTER TABLE public.monitoring_sessions ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.pain_reports ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.biomarker_panels ENABLE ROW LEVEL SECURITY;
CREATE POLICY "staff_full_access_sessions" ON public.monitoring_sessions FOR ALL
USING (public.my_role() IN ('admin','coach','sc','medical','td'))
WITH CHECK (public.my_role() IN ('admin','coach','sc','medical','td'));
-- بیومارکر محدودتر است — فقط admin/medical (تغییر سیاست نسبت به رفتار امروزی JSONB،
-- چون دادهی آزمایش خون حساستر از بار تمرینی است؛ نگاه کنید به Open Decisions سند دامنه):
CREATE POLICY "staff_medical_access_biomarkers" ON public.biomarker_panels FOR ALL
USING (public.my_role() IN ('admin','medical'))
WITH CHECK (public.my_role() IN ('admin','medical'));
1 2 3 4 | |
درک برتری عددی | |
درک نقصان عددی | |
آگاهی موقعیتی | |
اسکن محیط قبل از دریافت توپ | |
درک موقعیتهای نزدیک | |
درک موقعیتهای دور | |
کنترل ریتم بازی | |
وضعیت بدنی بر اساس موقعیت بازی | |
خلاقیت | |
بازیخوانی |
1 2 3 4 | |
تصمیمگیری در فاز تهاجمی | |
انتقال حمله به دفاع (۳ ثانیه) | |
تصمیمگیری در فاز تدافعی | |
انتقال دفاع به حمله (۳ ثانیه) | |
کیفیت لمس اول توپ | |
تمامکنندگی تحت فشار | |
دفاع یک در مقابل یک | |
حمله یک در مقابل یک | |
حفظ توپ فردی | |
استفاده از پای غیر تخصصی |
1 2 3 4 | |
زمان اجرا | |
دقت اجرا | |
سرعت اجرا |
1 2 3 4 | |
سرعت جابجایی | |
سرعت واکنش | |
چابکی | |
قدرت انفجاری | |
ظرفیت تکرار تلاشهای با شدت بالا | |
کاهش شتاب |
1 2 3 4 | |
شخصیت مثبت و نگرش حرفهای | |
آموزشپذیری | |
ثبات و استمرار در عملکرد | |
تمرکز | |
اعتماد به نفس | |
مدیریت استرس و خشم | |
همکاری تیمی | |
نظم و انضباط | |
رهبری | |
ذهنیت رقابتی | |
قابلیتهای ارتباطی | |
تابآوری | |
انگیزه | |
مسئولیت پذیری | |
عکس العمل بعد از ارتکاب اشتباه | |
مدیریت بحران |
1 2 3 4 | |
انتخاب بازیکن در توزیع توپ | |
استقرار در فضای مناسب | |
کنترل سرعت حرکت |