🔐 ورود به حساب ابری

🌅
گزارش صبح‌گاه کادر فنی
—
🟢 آماده کامل
—
🟡 محدود
—
🔴 استراحت
—
آمادگی تیم
—
ACWR میانگین
—
بدون داده امروز
—
کل پروفایل
📋 توصیه تمرینی امروز بر اساس بار تیم
—

🔔 تنظیمات گزارش صبح‌گاه

هر روز صبح یک اعلان از وضعیت تیم دریافت کنید
: (ساعت : دقیقه)
🔔 اعلان‌ها نیاز به مجوز مرورگر دارند
📱 هنگام نصب به عنوان PWA بهترین عملکرد را دارد
💡 اگر برنامه بسته باشد، دفعه بعدی باز شدن گزارش نمایش داده می‌شود

⚙️ راه‌اندازی Supabase

مرحله ۱ — به supabase.com بروید و حساب رایگان بسازید
مرحله ۲ — پروژه جدید ایجاد کنید (New Project)
مرحله ۳ — در SQL Editor، کد زیر را کپی و اجرا کنید ↓
مرحله ۴ — از Settings → API اطلاعات را کپی کنید
📋 SQL Schema (در Supabase SQL Editor اجرا کنید):
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'));
⚠️ این کلید عمومی است و راز نیست. هر کسی که پرونده‌ی برنامه را داشته باشد آن را می‌بیند. تنها چیزی که واقعاً از داده‌ی ورزشکاران محافظت می‌کند، سیاست دسترسیِ سمت پایگاه داده (RLS) است — پس فعال‌بودنش اختیاری نیست.
توضیح
در حال ساخت PDF…
⌚ ورود داده‌ی GPS و ضربان قلب
پرونده‌ی خروجیِ Catapult، Polar، Garmin یا STATSports را بدهید. ستون‌ها خودکار تشخیص داده می‌شوند و پیش از ثبت، پیش‌نمایش می‌بینید. داده‌ی دستیِ خودتان بدون اجازه بازنویسی نمی‌شود.
⚙️ پیکربندی گزارش
بخش‌های موردنیاز گزارش و قالب خروجی را انتخاب کنید.
قالب آماده
بخش‌ها
هویت باشگاه
📅 انتخاب تاریخ
🎬 پخش ویدیو
سامانه ملی ارزیابی عملکرد ورزشکاران (سماع)
🏆 لوگو
رشته ورزشی:
💾 حالت محلی (localStorage)
پست بازیکن:
📸 عکس بازیکن
⚡ سیستم امتیازدهی
1ضعیف
2متوسط
3خوب
4خیلی خوب
⚙️ چیدمان و رنگ داشبوردها
⚙️ تنظیم باکس
هوش بازی
1
2
3
4
درک برتری عددی
درک نقصان عددی
آگاهی موقعیتی
اسکن محیط قبل از دریافت توپ
درک موقعیت‌های نزدیک
درک موقعیت‌های دور
کنترل ریتم بازی
وضعیت بدنی بر اساس موقعیت بازی
خلاقیت
بازی‌خوانی
تاکتیکی
1
2
3
4
تصمیم‌گیری در فاز تهاجمی
انتقال حمله به دفاع (۳ ثانیه)
تصمیم‌گیری در فاز تدافعی
انتقال دفاع به حمله (۳ ثانیه)
کیفیت لمس اول توپ
تمام‌کنندگی تحت فشار
دفاع یک در مقابل یک
حمله یک در مقابل یک
حفظ توپ فردی
استفاده از پای غیر تخصصی
تکنیکی
1
2
3
4
زمان اجرا
دقت اجرا
سرعت اجرا
فیزیکی
1
2
3
4
سرعت جابجایی
سرعت واکنش
چابکی
قدرت انفجاری
ظرفیت تکرار تلاش‌های با شدت بالا
کاهش شتاب
روانشناختی
1
2
3
4
شخصیت مثبت و نگرش حرفه‌ای
آموزش‌پذیری
ثبات و استمرار در عملکرد
تمرکز
اعتماد به نفس
مدیریت استرس و خشم
همکاری تیمی
نظم و انضباط
رهبری
ذهنیت رقابتی
قابلیت‌های ارتباطی
تاب‌آوری
انگیزه
مسئولیت پذیری
عکس العمل بعد از ارتکاب اشتباه
مدیریت بحران
Flying Goalkeeper
1
2
3
4
انتخاب بازیکن در توزیع توپ
استقرار در فضای مناسب
کنترل سرعت حرکت
📝 توضیحات تکمیلی

🏋️ Advanced Jump Lab

SPORT SCIENCE
اطلاعات ورزشکار
نام ورزشکار
رشته ورزشی
وزن (کیلوگرم)
قد (سانتی‌متر)
سن
جنسیت
📅 ثبت نتیجه در دوره: حداقل ۳ پرش برای ثبت
نوع تست
🏋️ Squat Jump از وضعیت نشسته
↕️ CMJ Countermovement Jump
⬇️ Drop Jump RSI واقعی
🦵 Single Leg تک پا — تقارن‌سنجی
دوربین غیرفعال — روی «فعال‌سازی دوربین» کلیک کنید
📐 راهنمای چیدمان دوربین و پروتکل آزمون — برای باز و بسته شدن کلیک کنید

🔎 شمارشگر اسکن

بازیکن:
گام ۱ از ۴ — ویدیو را بارگذاری کنید یا دوربین را روشن کنید
هنوز ویدیویی بارگذاری نشده — «بارگذاری ویدیو» یا «فعال‌سازی دوربین» را بزنید
۱٫۰× فقط نمایش — روی شمارش اثری ندارد
قفلِ ردیاب
0:00 0:00
تعداد اسکن شناسایی‌شده
۰
هنوز شمارشی شروع نشده.

🧍 سنجش وضعیت بدنی — نسخه ۳ (تشخیص چندنفره)

⚠️ این نسخه از یک مدل جدید (تشخیص هم‌زمانِ چند بازیکن) استفاده می‌کند که اولین بار است در مرورگر واقعی امتحان می‌شود — اگر «بارگذاری نشد» دیدید یا نتیجه عجیب بود، لطفاً خبر بدهید.
بازیکن:
دوربین غیرفعال
0:00 0:00
فیلم را متوقف کنید (یا با گام دقیق جلو/عقب ببرید) — وضعیت بدنیِ همه‌ی بازیکنانِ قابل‌مشاهده خودکار روی تصویر نشان داده می‌شود؛ برای ثبت در فرم، روی هرکدام کلیک کنید
⌨️ فاصله=پخش/توقف موقت · ←/→=۱ ثانیه عقب/جلو · S=توقف کامل

🧠 آگاهی موقعیتی — سنجش ترکیبی

این ابزار سه مرحله را با هم ترکیب می‌کند: نرخ اسکن (از «🔎 شمارشگر اسکن» همین بازیکن)، جایگیری فضایی، و وضعیت بدنی نسبت به هم‌تیمی+دروازه+مدافع. برای نتیجه‌ی کامل، اول یک جلسه‌ی اسکن برای این بازیکن ثبت کرده باشید.
بازیکن:
دوربین غیرفعال
0:00 0:00
فیلم را متوقف کنید تا بازیکنان خودکار تشخیص داده شوند