feat: Apple Watch app + Paywall + Privacy Policy + rebranding

## Major Features
- Apple Watch companion app (6 phases complete)
  - WatchConnectivity iPhone ↔ Watch
  - HealthKit integration (HR, calories)
  - SwiftUI premium UI
  - 9 complication types
  - Always-On Display support

- Paywall screen with RevenueCat integration
- Privacy Policy screen
- App rebranding: tabatago → TabataFit
- Bundle ID: com.millianlmx.tabatafit

## Changes
- New: ios/TabataFit Watch App/ (complete Watch app)
- New: app/paywall.tsx (subscription UI)
- New: app/privacy.tsx (privacy policy)
- New: src/features/watch/ (Watch sync hooks)
- New: admin-web/ (admin dashboard)
- Updated: app.json, package.json (branding)
- Updated: profile.tsx (paywall + privacy links)
- Updated: i18n translations (EN/FR/DE/ES)
- New: app icon 1024x1024

## Watch App Files
- TabataFitWatchApp.swift (entry point)
- ContentView.swift (premium UI)
- HealthKitManager.swift (HR + calories)
- WatchSessionManager.swift (communication)
- Complications/ (WidgetKit)
- UserDefaults+Shared.swift (data sharing)
This commit is contained in:
Millian Lamiaux
2026-03-11 09:43:53 +01:00
parent f80798069b
commit 2ad7ae3a34
86 changed files with 19648 additions and 365 deletions

130
admin-web/lib/supabase.ts Normal file
View File

@@ -0,0 +1,130 @@
import { createClient } from '@supabase/supabase-js'
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL === 'your_supabase_project_url'
? 'http://localhost:54321'
: (process.env.NEXT_PUBLIC_SUPABASE_URL || 'http://localhost:54321')
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY === 'your_supabase_anon_key'
? 'placeholder-key'
: (process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || 'placeholder-key')
export const supabase = createClient(supabaseUrl, supabaseKey)
export const isSupabaseConfigured = () => {
const url = process.env.NEXT_PUBLIC_SUPABASE_URL
return url !== 'your_supabase_project_url' && url !== 'http://localhost:54321' && !!url
}
export type Json =
| string
| number
| boolean
| null
| { [key: string]: Json | undefined }
| Json[]
export interface Database {
public: {
Tables: {
workouts: {
Row: {
id: string
title: string
trainer_id: string
category: 'full-body' | 'core' | 'upper-body' | 'lower-body' | 'cardio'
level: 'Beginner' | 'Intermediate' | 'Advanced'
duration: number
calories: number
rounds: number
prep_time: number
work_time: number
rest_time: number
equipment: string[]
music_vibe: 'electronic' | 'hip-hop' | 'pop' | 'rock' | 'chill'
exercises: { name: string; duration: number }[]
thumbnail_url: string | null
video_url: string | null
is_featured: boolean
created_at: string
updated_at: string
}
Insert: {
id?: string
title: string
trainer_id: string
category: 'full-body' | 'core' | 'upper-body' | 'lower-body' | 'cardio'
level: 'Beginner' | 'Intermediate' | 'Advanced'
duration: number
calories: number
rounds: number
prep_time: number
work_time: number
rest_time: number
equipment?: string[]
music_vibe: 'electronic' | 'hip-hop' | 'pop' | 'rock' | 'chill'
exercises: { name: string; duration: number }[]
thumbnail_url?: string | null
video_url?: string | null
is_featured?: boolean
}
Update: Partial<Database['public']['Tables']['workouts']['Insert']>
}
trainers: {
Row: {
id: string
name: string
specialty: string
color: string
avatar_url: string | null
workout_count: number
created_at: string
updated_at: string
}
Insert: {
id?: string
name: string
specialty: string
color: string
avatar_url?: string | null
workout_count?: number
}
Update: Partial<Omit<Database['public']['Tables']['trainers']['Insert'], 'id'>>
}
collections: {
Row: {
id: string
title: string
description: string
icon: string
gradient: string[] | null
created_at: string
updated_at: string
}
Insert: {
id?: string
title: string
description: string
icon: string
gradient?: string[] | null
}
Update: Partial<Omit<Database['public']['Tables']['collections']['Insert'], 'id'>>
}
collection_workouts: {
Row: {
id: string
collection_id: string
workout_id: string
sort_order: number
}
}
admin_users: {
Row: {
id: string
email: string
role: 'admin' | 'editor'
created_at: string
last_login: string | null
}
}
}
}
}

6
admin-web/lib/utils.ts Normal file
View File

@@ -0,0 +1,6 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}