refactor: code quality cleanup — remove any types, add logger, rename Kine to Tabata
- Phase 0: Rename all Kine references to Tabata (types, files, imports, i18n, analytics events) - Phase 1: Add test coverage for tabataProgramStore, workoutProgramStore, and color utils (47 tests) - Phase 2: Remove all `any` types from production code with proper typed replacements - Phase 3: Replace ~60 raw console.* calls with __DEV__-gated logger utility - Phase 4: Verify .DS_Store housekeeping (already clean) 0 TypeScript errors, 583/583 tests passing.
This commit is contained in:
80
admin-web/migrations/001_reset_trainers.sql
Normal file
80
admin-web/migrations/001_reset_trainers.sql
Normal file
@@ -0,0 +1,80 @@
|
||||
-- Migration Script: Reset trainers to Félia and Félix only
|
||||
-- Run this in your Supabase SQL Editor
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
felia_id UUID;
|
||||
felix_id UUID;
|
||||
old_trainer_ids UUID[];
|
||||
BEGIN
|
||||
-- Step 1: Get IDs of trainers to be replaced
|
||||
SELECT ARRAY[
|
||||
(SELECT id FROM trainers WHERE name = 'Emma'),
|
||||
(SELECT id FROM trainers WHERE name = 'Jake'),
|
||||
(SELECT id FROM trainers WHERE name = 'Mia'),
|
||||
(SELECT id FROM trainers WHERE name = 'Alex'),
|
||||
(SELECT id FROM trainers WHERE name = 'Sofia')
|
||||
] INTO old_trainer_ids;
|
||||
|
||||
-- Step 2: Create Félia if not exists
|
||||
SELECT COALESCE(
|
||||
(SELECT id FROM trainers WHERE name = 'Félia' LIMIT 1),
|
||||
gen_random_uuid()
|
||||
) INTO felia_id;
|
||||
|
||||
INSERT INTO trainers (id, name, specialty, color, workout_count, avatar_url)
|
||||
VALUES (felia_id, 'Félia', 'Core', '#30D158', 0, NULL)
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
name = 'Félia',
|
||||
specialty = 'Core',
|
||||
color = '#30D158';
|
||||
|
||||
-- Step 3: Create Félix if not exists
|
||||
SELECT COALESCE(
|
||||
(SELECT id FROM trainers WHERE name = 'Félix' LIMIT 1),
|
||||
gen_random_uuid()
|
||||
) INTO felix_id;
|
||||
|
||||
INSERT INTO trainers (id, name, specialty, color, workout_count, avatar_url)
|
||||
VALUES (felix_id, 'Félix', 'Strength', '#FFD60A', 0, NULL)
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
name = 'Félix',
|
||||
specialty = 'Strength',
|
||||
color = '#FFD60A';
|
||||
|
||||
-- Step 4: Get the final trainer IDs
|
||||
SELECT id INTO felia_id FROM trainers WHERE name = 'Félia';
|
||||
SELECT id INTO felix_id FROM trainers WHERE name = 'Félix';
|
||||
|
||||
-- Step 5: Update workouts - female trainers → felia, male trainers → felix
|
||||
UPDATE workouts
|
||||
SET trainer_id = felia_id
|
||||
WHERE trainer_id IN (
|
||||
SELECT id FROM trainers WHERE name IN ('Emma', 'Mia', 'Sofia')
|
||||
);
|
||||
|
||||
UPDATE workouts
|
||||
SET trainer_id = felix_id
|
||||
WHERE trainer_id IN (
|
||||
SELECT id FROM trainers WHERE name IN ('Jake', 'Alex')
|
||||
);
|
||||
|
||||
-- Step 6: Update workout counts
|
||||
UPDATE trainers SET workout_count = (
|
||||
SELECT COUNT(*) FROM workouts WHERE trainer_id = felia_id
|
||||
) WHERE id = felia_id;
|
||||
|
||||
UPDATE trainers SET workout_count = (
|
||||
SELECT COUNT(*) FROM workouts WHERE trainer_id = felix_id
|
||||
) WHERE id = felix_id;
|
||||
|
||||
-- Step 7: Delete old trainers
|
||||
DELETE FROM trainers WHERE name IN ('Emma', 'Jake', 'Mia', 'Alex', 'Sofia');
|
||||
END $$;
|
||||
|
||||
-- Step 8: Verify
|
||||
SELECT 'Trainers:' AS result;
|
||||
SELECT id::text, name, specialty, color, workout_count FROM trainers ORDER BY name;
|
||||
|
||||
SELECT 'Workout trainer distribution:' AS result;
|
||||
SELECT trainer_id::text, COUNT(*) as workout_count FROM workouts GROUP BY trainer_id;
|
||||
60
admin-web/migrations/002_storage_policies.sql
Normal file
60
admin-web/migrations/002_storage_policies.sql
Normal file
@@ -0,0 +1,60 @@
|
||||
-- Storage RLS Policies for Trainer Assets
|
||||
-- Run this in your Supabase SQL Editor
|
||||
|
||||
-- Enable RLS on storage buckets (if not already enabled)
|
||||
ALTER TABLE storage.buckets ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Trainer Avatars Bucket Policies
|
||||
DROP POLICY IF EXISTS "Public read access" ON storage.objects;
|
||||
DROP POLICY IF EXISTS "Public upload access" ON storage.objects;
|
||||
DROP POLICY IF EXISTS "Public update access" ON storage.objects;
|
||||
DROP POLICY IF EXISTS "Public delete access" ON storage.objects;
|
||||
|
||||
-- Read access (anyone can view)
|
||||
CREATE POLICY "Public read access" ON storage.objects
|
||||
FOR SELECT USING (bucket_id = 'trainer-avatars');
|
||||
|
||||
-- Upload access (anyone can upload)
|
||||
CREATE POLICY "Public upload access" ON storage.objects
|
||||
FOR INSERT WITH CHECK (bucket_id = 'trainer-avatars');
|
||||
|
||||
-- Update access (anyone can update)
|
||||
CREATE POLICY "Public update access" ON storage.objects
|
||||
FOR UPDATE USING (bucket_id = 'trainer-avatars');
|
||||
|
||||
-- Delete access (anyone can delete)
|
||||
CREATE POLICY "Public delete access" ON storage.objects
|
||||
FOR DELETE USING (bucket_id = 'trainer-avatars');
|
||||
|
||||
-- Trainer Videos Bucket Policies
|
||||
DROP POLICY IF EXISTS "Public read access videos" ON storage.objects;
|
||||
DROP POLICY IF EXISTS "Public upload access videos" ON storage.objects;
|
||||
DROP POLICY IF EXISTS "Public update access videos" ON storage.objects;
|
||||
DROP POLICY IF EXISTS "Public delete access videos" ON storage.objects;
|
||||
|
||||
-- Read access (anyone can view)
|
||||
CREATE POLICY "Public read access videos" ON storage.objects
|
||||
FOR SELECT USING (bucket_id = 'trainer-videos');
|
||||
|
||||
-- Upload access (anyone can upload)
|
||||
CREATE POLICY "Public upload access videos" ON storage.objects
|
||||
FOR INSERT WITH CHECK (bucket_id = 'trainer-videos');
|
||||
|
||||
-- Update access (anyone can update)
|
||||
CREATE POLICY "Public update access videos" ON storage.objects
|
||||
FOR UPDATE USING (bucket_id = 'trainer-videos');
|
||||
|
||||
-- Delete access (anyone can delete)
|
||||
CREATE POLICY "Public delete access videos" ON storage.objects
|
||||
FOR DELETE USING (bucket_id = 'trainer-videos');
|
||||
|
||||
-- Exercise Videos Table Policies (if table exists)
|
||||
DROP POLICY IF EXISTS "Public read access exercise_videos" ON exercise_videos;
|
||||
DROP POLICY IF EXISTS "Public insert access exercise_videos" ON exercise_videos;
|
||||
DROP POLICY IF EXISTS "Public update access exercise_videos" ON exercise_videos;
|
||||
DROP POLICY IF EXISTS "Public delete access exercise_videos" ON exercise_videos;
|
||||
|
||||
CREATE POLICY "Public read access exercise_videos" ON exercise_videos FOR SELECT USING (true);
|
||||
CREATE POLICY "Public insert access exercise_videos" ON exercise_videos FOR INSERT WITH CHECK (true);
|
||||
CREATE POLICY "Public update access exercise_videos" ON exercise_videos FOR UPDATE USING (true);
|
||||
CREATE POLICY "Public delete access exercise_videos" ON exercise_videos FOR DELETE USING (true);
|
||||
24
admin-web/migrations/003_create_exercise_videos.sql
Normal file
24
admin-web/migrations/003_create_exercise_videos.sql
Normal file
@@ -0,0 +1,24 @@
|
||||
-- Create exercise_videos table
|
||||
CREATE TABLE IF NOT EXISTS exercise_videos (
|
||||
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||
workout_id UUID NOT NULL,
|
||||
trainer_id UUID NOT NULL,
|
||||
exercise_name TEXT NOT NULL,
|
||||
video_url TEXT NOT NULL,
|
||||
video_path TEXT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Enable RLS
|
||||
ALTER TABLE exercise_videos ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Public policies
|
||||
DROP POLICY IF EXISTS "Public read access exercise_videos" ON exercise_videos;
|
||||
DROP POLICY IF EXISTS "Public insert access exercise_videos" ON exercise_videos;
|
||||
DROP POLICY IF EXISTS "Public update access exercise_videos" ON exercise_videos;
|
||||
DROP POLICY IF EXISTS "Public delete access exercise_videos" ON exercise_videos;
|
||||
|
||||
CREATE POLICY "Public read access exercise_videos" ON exercise_videos FOR SELECT USING (true);
|
||||
CREATE POLICY "Public insert access exercise_videos" ON exercise_videos FOR INSERT WITH CHECK (true);
|
||||
CREATE POLICY "Public update access exercise_videos" ON exercise_videos FOR UPDATE USING (true);
|
||||
CREATE POLICY "Public delete access exercise_videos" ON exercise_videos FOR DELETE USING (true);
|
||||
11
admin-web/migrations/004_add_video_type_column.sql
Normal file
11
admin-web/migrations/004_add_video_type_column.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
-- Add video_type and duration_seconds columns to exercise_videos table
|
||||
ALTER TABLE exercise_videos
|
||||
ADD COLUMN IF NOT EXISTS video_type TEXT DEFAULT 'exercise'
|
||||
CHECK (video_type IN ('exercise', 'rest'));
|
||||
|
||||
ALTER TABLE exercise_videos
|
||||
ADD COLUMN IF NOT EXISTS duration_seconds INTEGER;
|
||||
|
||||
-- Update existing rows to have appropriate values
|
||||
UPDATE exercise_videos SET video_type = 'exercise' WHERE video_type IS NULL;
|
||||
UPDATE exercise_videos SET duration_seconds = 8 WHERE duration_seconds IS NULL;
|
||||
127
admin-web/migrations/005_kine_programs.sql
Normal file
127
admin-web/migrations/005_kine_programs.sql
Normal file
@@ -0,0 +1,127 @@
|
||||
-- Tabata Kine Programs Schema
|
||||
-- Migration 005: New kiné program system
|
||||
-- Replaces the old 3-program model with 4 kiné programs
|
||||
|
||||
-- ─── Kine Programs ──────────────────────────────────────────
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.kine_programs (
|
||||
id TEXT PRIMARY KEY, -- 'debutant', 'intermediaire', 'avance', 'bureau'
|
||||
title TEXT NOT NULL,
|
||||
title_en TEXT NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
description_en TEXT NOT NULL,
|
||||
tier TEXT NOT NULL CHECK (tier IN ('free', 'premium')),
|
||||
accent_color TEXT NOT NULL DEFAULT '#30D158',
|
||||
icon TEXT NOT NULL DEFAULT 'seedling',
|
||||
duration_weeks INTEGER NOT NULL DEFAULT 4,
|
||||
sessions_per_week INTEGER NOT NULL,
|
||||
total_sessions INTEGER NOT NULL,
|
||||
equipment JSONB NOT NULL DEFAULT '{"required": [], "optional": []}',
|
||||
focus_areas TEXT[] DEFAULT '{}',
|
||||
focus_areas_en TEXT[] DEFAULT '{}',
|
||||
principles TEXT[] DEFAULT '{}',
|
||||
principles_en TEXT[] DEFAULT '{}',
|
||||
completion_criteria TEXT[] DEFAULT '{}',
|
||||
completion_criteria_en TEXT[] DEFAULT '{}',
|
||||
next_program_id TEXT,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ─── Kine Sessions ──────────────────────────────────────────
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.kine_sessions (
|
||||
id TEXT PRIMARY KEY, -- 'deb-w1-s1', 'int-w2-s3', etc.
|
||||
program_id TEXT NOT NULL REFERENCES public.kine_programs(id) ON DELETE CASCADE,
|
||||
week_number INTEGER NOT NULL CHECK (week_number >= 1 AND week_number <= 6),
|
||||
day_number INTEGER NOT NULL CHECK (day_number >= 1 AND day_number <= 7),
|
||||
title TEXT NOT NULL,
|
||||
title_en TEXT NOT NULL,
|
||||
description TEXT,
|
||||
description_en TEXT,
|
||||
focus TEXT[] DEFAULT '{}',
|
||||
focus_en TEXT[] DEFAULT '{}',
|
||||
warmup JSONB NOT NULL, -- WarmupPhase structure
|
||||
blocks JSONB NOT NULL, -- TabataBlock[] array
|
||||
cooldown JSONB NOT NULL, -- CooldownPhase structure
|
||||
equipment TEXT[] DEFAULT '{}',
|
||||
total_rounds INTEGER NOT NULL,
|
||||
total_duration INTEGER NOT NULL, -- minutes
|
||||
calories INTEGER NOT NULL DEFAULT 0,
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ─── Kine Exercises Library ─────────────────────────────────
|
||||
-- Track individual exercises for video generation
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.kine_exercises (
|
||||
id TEXT PRIMARY KEY, -- slug: 'squat-classique', 'pont-fessier'
|
||||
name_fr TEXT NOT NULL,
|
||||
name_en TEXT NOT NULL,
|
||||
conseil_fr TEXT,
|
||||
conseil_en TEXT,
|
||||
modification TEXT,
|
||||
modification_en TEXT,
|
||||
progression TEXT,
|
||||
progression_en TEXT,
|
||||
video_url TEXT,
|
||||
video_generated BOOLEAN DEFAULT FALSE,
|
||||
video_generation_status TEXT CHECK (video_generation_status IN ('pending', 'generating', 'completed', 'failed')),
|
||||
video_generation_job_id TEXT,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ─── Kine Weeks ─────────────────────────────────────────────
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.kine_weeks (
|
||||
id TEXT PRIMARY KEY, -- 'deb-w1', 'int-w2', etc.
|
||||
program_id TEXT NOT NULL REFERENCES public.kine_programs(id) ON DELETE CASCADE,
|
||||
week_number INTEGER NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
title_en TEXT NOT NULL,
|
||||
description TEXT,
|
||||
description_en TEXT,
|
||||
focus TEXT,
|
||||
focus_en TEXT,
|
||||
is_deload BOOLEAN DEFAULT FALSE,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ─── Indexes ────────────────────────────────────────────────
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_kine_sessions_program ON public.kine_sessions(program_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_kine_sessions_week ON public.kine_sessions(program_id, week_number);
|
||||
CREATE INDEX IF NOT EXISTS idx_kine_weeks_program ON public.kine_weeks(program_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_kine_exercises_generated ON public.kine_exercises(video_generated);
|
||||
|
||||
-- ─── Row Level Security ─────────────────────────────────────
|
||||
|
||||
ALTER TABLE public.kine_programs ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.kine_sessions ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.kine_exercises ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.kine_weeks ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Public read access
|
||||
CREATE POLICY "Public read kine_programs" ON public.kine_programs FOR SELECT USING (true);
|
||||
CREATE POLICY "Public read kine_sessions" ON public.kine_sessions FOR SELECT USING (true);
|
||||
CREATE POLICY "Public read kine_exercises" ON public.kine_exercises FOR SELECT USING (true);
|
||||
CREATE POLICY "Public read kine_weeks" ON public.kine_weeks FOR SELECT USING (true);
|
||||
|
||||
-- ─── Seed: Debutant Program ─────────────────────────────────
|
||||
|
||||
INSERT INTO public.kine_programs (id, title, title_en, description, description_en, tier, accent_color, icon, duration_weeks, sessions_per_week, total_sessions) VALUES
|
||||
('debutant', 'Débutant', 'Beginner',
|
||||
'Apprendre le protocole tabata, construire les bases techniques de chaque mouvement fondamental.',
|
||||
'Learn the tabata protocol, build technical foundations for each fundamental movement.',
|
||||
'free', '#30D158', 'seedling', 4, 3, 12)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- Seed weeks
|
||||
INSERT INTO public.kine_weeks (id, program_id, week_number, title, title_en, description, description_en, focus, focus_en, is_deload) VALUES
|
||||
('deb-w1', 'debutant', 1, 'Découverte du rythme', 'Finding Your Rhythm', 'Un bloc tabata par séance (4 min) + échauffement + retour au calme.', 'One tabata block per session + warmup + cooldown.', 'Apprentissage du protocole 20/10', 'Learning the 20/10 protocol', false),
|
||||
('deb-w2', 'debutant', 2, 'Consolidation', 'Building Strength', '2 blocs tabata + 1 min récup entre les blocs.', '2 tabata blocks + 1 min recovery between blocks.', 'Consolidation des mouvements', 'Consolidating movements', false),
|
||||
('deb-w3', 'debutant', 3, 'Montée en intensité', 'Building Intensity', '3 blocs tabata + 1 min récupération entre chaque.', '3 tabata blocks + 1 min recovery between each.', 'Impacts très légers, volume augmenté', 'Very light impact, increased volume', false),
|
||||
('deb-w4', 'debutant', 4, 'Décharge & consolidation', 'Deload & Consolidation', 'Retour à 2 blocs. Volume réduit de 40%.', 'Back to 2 blocks. Volume reduced by 40%.', 'Technique parfaite, respiration', 'Perfect technique, breathing', true)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
Reference in New Issue
Block a user