mirror of
https://github.com/Snouzy/workout-cool.git
synced 2026-05-19 14:40:35 +00:00
a1bd2b26dd
* feat(exercises): add new route for fetching exercises with filtering options chore(import-exercises): improve formatting and clarity in the prompt for exercise attributes * feat(shuffle): add shuffle exercise route with input validation and error handling chore(delete-program-button): simplify button formatting and update confirmation message for clarity chore(workspace): create workspace configuration for easier project management across frontend and mobile directories * feat(auth): implement signup route with validation and error handling for user registration * feat(api): add user password and profile management routes for updating and retrieving user information * chore(route.ts): remove console.log statements for cleaner code and improved performance * feat(profile): implement cookie cleaning and logging for user profile updates to enhance session management and debugging fix(package): add @better-auth/expo dependency for improved authentication support fix(better-auth): update trusted origins to include expo and workoutfast protocols for better compatibility * feat(auth): implement mobile-compatible session handling to address malformed cookies from the mobile app and improve authentication reliability chore(api): refactor user password and profile routes to utilize new mobile session utilities for better cookie management docs(api): add README for mobile authentication support detailing issues and solutions for mobile app cookie handling * feat(password): refactor password update logic to separate core functionality from action client for better reusability and clarity fix(password): improve error handling in password update route to provide more specific feedback on validation failures * refactor(mobile-auth.ts, mobile-cookie-utils.ts, mobile-safe-actions.ts): extract cleanMobileCookies function to a new utility file for better code organization and reuse across modules * refactor(password route): simplify error handling by returning the original error message instead of custom messages for better maintainability and clarity * feat(workout-sessions): add POST route for syncing workout sessions to handle mobile compatibility and session management * feat(workout-sessions): add user-specific workout session retrieval and error handling for syncing sessions fix(sync-workout-sessions): improve error handling for missing users and exercises during session sync process * feat(feedback, rating, summary): implement feedback and rating endpoints for workout sessions, add summary metrics and update database schema to support ratings and comments docs: create CLAUDE.md to provide project overview, architecture, key features, and deployment details * refactor(route.ts): simplify GET request handler by removing userId parameter check and adding rating fields to response for enhanced session details * feat(workout-sessions): implement DELETE route for deleting workout sessions with authentication checks and error handling * refactor(workout-sessions): update DELETE route to handle sessionId as a resolved promise and simplify authentication checks in delete action * feat(revenuecat): integrate RevenueCat for subscription management and user linking, add webhook handling for subscription events, and enhance premium status checks with RevenueCat data fix(premium/status): update premium status endpoint to include RevenueCat information and improve error handling chore(database): add RevenueCat fields to subscription model and create webhook event logging table in the database build(dependencies): add RevenueCat backend wrapper and crypto-js for secure webhook signature verification * chore: update .gitignore to exclude product-development and .claude directories feat(locales): add French translation for lats in locales/fr.ts fix(better-auth): update trustedOrigins to replace workoutfast with workoutcool for better authentication configuration * fix(route.ts): update import path for getExercisesAction to reflect new actions directory structure for better organization * feat(route.ts): add logging for session details to assist with debugging feat(sync-workout-sessions.action.ts): extend workout session schema to include optional rating and ratingComment fields for enhanced user feedback * feat(analytics): add premium analytics tracking endpoint and enhance subscription details with premium features * feat(revenuecat): implement RevenueCat API client for subscription management and enhance premium service with syncing capabilities * feat(statistics): implement exercise statistics feature with charts and data fetching for weight progression, one-rep max, and volume statistics, including premium access controls and UI components * feat(exercises): enhance exercise attributes handling and UI for better user experience and performance tracking * feat(ExercisesBrowser): enhance exercise selection with filters, statistics, and improved UI for better user experience * feat(exercises): enhance exercise filtering and statistics fetching with improved query parameters and error handling in the ExercisesBrowser component * refactor(statistics): replace ExerciseStatisticsTab with ExerciseCharts for improved clarity and functionality across multiple components * refactor(statistics): clean up ExerciseStatisticsTab and ExercisesBrowser components by removing unused imports, simplifying modal structure, and enhancing styling for better readability and maintainability * fix(ExercisesBrowser): add console log for selectedExercise and adjust styling for exercise image container to enhance layout and responsiveness * feat(statistics): integrate recharts for improved charting and add theme support for better UI consistency * feat(statistics): enhance exercise statistics page with new charts and localization updates, and remove unused statistics page for better organization * feat: add lodash.debounce type definitions and clean up ExercisesBrowser component markup * feat(statistics): remove exercises page and implement statistics page with localization and navigation updates * fix(statistics): update alert variant for error handling and refactor tooltip implementation for better clarity and usability * feat(statistics): enhance StatisticsPage with premium features and overlays for better user engagement and insights * feat(premium-gate): add upgrade to premium message and streamline component structure for better readability * feat(exercise): add new exercise attributes handling and refactor ExerciseSelection component to utilize updated types and methods for better attribute management * refactor(exercise): simplify muscle attribute retrieval functions and improve type consistency across components * feat: refactor workout session and exercise components for improved attribute handling and remove unused HomePage component * fix(statistics.types.ts): reorder imports to maintain consistency and clarity in module structure * feat(exercise-video-modal): implement tabbed interface for video and statistics with timeframe selection to enhance user experience * feat(statistics): enhance statistics page with dynamic translations and improved user experience through localized content and updated UI elements * feat(statistics): refactor ExercisesBrowser and ExerciseVideoModal to use TimeframeSelector component for improved code reusability and maintainability * fix(locales/fr.ts): update French translations for consistency and accuracy in exercise terminology; fix(ExercisesBrowser.tsx): enhance muscle group labels to match updated translation keys and improve code readability * feat(simple-select): add SimpleSelect component for reusable select functionality and integrate it into ExercisesBrowser for improved filtering options * fix(fr.ts): update French translations for timeframes to use lowercase for consistency * feat(statistics): enhance date formatting by integrating locale support across multiple components for improved user experience * feat(locales): add statistics and tooltip translations in multiple languages for improved user experience * feat(revenuecat): implement webhook handler for subscription updates and add advanced workout data seeding script * feat(revenuecat): deprecate user ID assignment and linking endpoints, add sync-status endpoint for automatic management of RevenueCat IDs * feat(webhook): add support for SUBSCRIPTION_PAUSED event and enhance logging for cancellation events * fix(webhooks): rename expiration_at to expiration_at_ms for consistency in RevenueCatWebhookEvent interface and update related logic in processSubscriptionEvent function * feat(revenuecat): implement new webhook handling and subscription sync logic to improve premium status management and simplify database schema * chore: update USER_ID in seed-workout-data-advanced and simplify premium status check in premium.service to improve clarity and maintainability * feat(api): add program enrollment and retrieval endpoints for better program management functionality * feat(programs): enhance program enrollment and progress routes with new functionality and improve parameter handling * refactor(api): update parameter types to use Promise for slug and sessionSlug in route handlers for better async handling and clarity * refactor(premium.service.ts): remove revenueCatEntitlement references to simplify premium access logic and improve code clarity * chore(ci): add RevenueCat environment variables for CI workflow testing * chore: remove unused RevenueCat project ID references to clean up codebase * chore: remove deprecated API routes and clean up unused code to streamline the codebase * chore: baseline migration
228 lines
8.1 KiB
TypeScript
228 lines
8.1 KiB
TypeScript
import { prisma } from "../src/shared/lib/prisma";
|
|
|
|
// Configuration
|
|
const USER_ID = "bwZuBQO4cJgBX6NiZaXgv81vKfgBQcFe";
|
|
const BENCH_PRESS_ID = "cmbw9xso904p69kv1vwuadhx6"; // Développé couché à la barre prise large
|
|
|
|
interface WorkoutPattern {
|
|
dayOfWeek: number; // 0 = Sunday, 1 = Monday, etc.
|
|
hour: number;
|
|
exercisePatterns: ExercisePattern[];
|
|
}
|
|
|
|
interface ExercisePattern {
|
|
exerciseId: string;
|
|
sets: SetPattern[];
|
|
progressionRate: number; // % increase per week
|
|
}
|
|
|
|
interface SetPattern {
|
|
repsRange: [number, number];
|
|
weightPercentage: number; // Percentage of working weight
|
|
}
|
|
|
|
// Realistic workout patterns
|
|
const workoutPatterns: WorkoutPattern[] = [
|
|
{
|
|
dayOfWeek: 1, // Monday - Chest day
|
|
hour: 10,
|
|
exercisePatterns: [
|
|
{
|
|
exerciseId: BENCH_PRESS_ID,
|
|
sets: [
|
|
{ repsRange: [12, 15], weightPercentage: 60 }, // Warm-up
|
|
{ repsRange: [10, 12], weightPercentage: 70 }, // Warm-up
|
|
{ repsRange: [8, 10], weightPercentage: 85 }, // Working set
|
|
{ repsRange: [6, 8], weightPercentage: 100 }, // Working set
|
|
{ repsRange: [6, 8], weightPercentage: 100 }, // Working set
|
|
{ repsRange: [8, 10], weightPercentage: 90 }, // Back-off set
|
|
],
|
|
progressionRate: 2.5, // 2.5% per week
|
|
},
|
|
],
|
|
},
|
|
{
|
|
dayOfWeek: 4, // Thursday - Upper body
|
|
hour: 16,
|
|
exercisePatterns: [
|
|
{
|
|
exerciseId: BENCH_PRESS_ID,
|
|
sets: [
|
|
{ repsRange: [12, 15], weightPercentage: 50 }, // Warm-up
|
|
{ repsRange: [10, 12], weightPercentage: 65 }, // Warm-up
|
|
{ repsRange: [8, 10], weightPercentage: 80 }, // Lighter day
|
|
{ repsRange: [8, 10], weightPercentage: 80 },
|
|
{ repsRange: [10, 12], weightPercentage: 75 },
|
|
],
|
|
progressionRate: 2.5,
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
async function seedAdvancedWorkoutData(weeksToGenerate: number = 12, startingWeight: number = 60) {
|
|
console.log(`Starting to seed ${weeksToGenerate} weeks of workout data...`);
|
|
console.log(`Starting bench press weight: ${startingWeight}kg`);
|
|
|
|
try {
|
|
const today = new Date();
|
|
let totalSessionsCreated = 0;
|
|
let totalSetsCreated = 0;
|
|
|
|
// Generate data week by week
|
|
for (let week = weeksToGenerate - 1; week >= 0; week--) {
|
|
const weekStart = new Date(today);
|
|
weekStart.setDate(today.getDate() - week * 7);
|
|
|
|
// Calculate current working weight with progression
|
|
const weeksCompleted = weeksToGenerate - 1 - week;
|
|
const progressionMultiplier = Math.pow(1 + workoutPatterns[0].exercisePatterns[0].progressionRate / 100, weeksCompleted);
|
|
const currentWorkingWeight = startingWeight * progressionMultiplier;
|
|
|
|
console.log(`\nWeek ${weeksCompleted + 1}: Working weight = ${currentWorkingWeight.toFixed(1)}kg`);
|
|
|
|
// Generate sessions for each pattern in the week
|
|
for (const pattern of workoutPatterns) {
|
|
// Calculate the date for this workout
|
|
const sessionDate = new Date(weekStart);
|
|
const daysUntilWorkout = (pattern.dayOfWeek - weekStart.getDay() + 7) % 7;
|
|
sessionDate.setDate(weekStart.getDate() + daysUntilWorkout);
|
|
sessionDate.setHours(pattern.hour, 0, 0, 0);
|
|
|
|
// Skip if the date is in the future
|
|
if (sessionDate > today) continue;
|
|
|
|
// Add some randomness to simulate real life (10% chance to skip a workout)
|
|
if (Math.random() < 0.1 && week > 0) {
|
|
console.log(` Skipped workout on ${sessionDate.toLocaleDateString()} (simulating missed session)`);
|
|
continue;
|
|
}
|
|
|
|
// Create workout session
|
|
const duration = 45 + Math.floor(Math.random() * 30); // 45-75 minutes
|
|
const workoutSession = await prisma.workoutSession.create({
|
|
data: {
|
|
userId: USER_ID,
|
|
startedAt: sessionDate,
|
|
endedAt: new Date(sessionDate.getTime() + duration * 60 * 1000),
|
|
duration: duration * 60,
|
|
},
|
|
});
|
|
|
|
totalSessionsCreated++;
|
|
console.log(` Created session on ${sessionDate.toLocaleDateString()} (${pattern.dayOfWeek === 1 ? "Heavy" : "Light"} day)`);
|
|
|
|
// Create exercises and sets for this session
|
|
for (let exerciseIndex = 0; exerciseIndex < pattern.exercisePatterns.length; exerciseIndex++) {
|
|
const exercisePattern = pattern.exercisePatterns[exerciseIndex];
|
|
|
|
const workoutSessionExercise = await prisma.workoutSessionExercise.create({
|
|
data: {
|
|
workoutSessionId: workoutSession.id,
|
|
exerciseId: exercisePattern.exerciseId,
|
|
order: exerciseIndex,
|
|
},
|
|
});
|
|
|
|
// Create sets according to the pattern
|
|
for (let setIndex = 0; setIndex < exercisePattern.sets.length; setIndex++) {
|
|
const setPattern = exercisePattern.sets[setIndex];
|
|
|
|
// Calculate actual weight for this set
|
|
let setWeight = (currentWorkingWeight * setPattern.weightPercentage) / 100;
|
|
|
|
// Add small random variation (±2.5%)
|
|
setWeight *= 0.975 + Math.random() * 0.05;
|
|
|
|
// Round to nearest 2.5kg
|
|
setWeight = Math.round(setWeight / 2.5) * 2.5;
|
|
setWeight = Math.max(20, setWeight); // Minimum 20kg (empty barbell)
|
|
|
|
// Calculate reps with some variation
|
|
const repsRange = setPattern.repsRange;
|
|
const reps = repsRange[0] + Math.floor(Math.random() * (repsRange[1] - repsRange[0] + 1));
|
|
|
|
// Occasionally fail a rep on heavy sets
|
|
const failedRep = setPattern.weightPercentage >= 95 && Math.random() < 0.15;
|
|
const actualReps = failedRep ? Math.max(1, reps - 1) : reps;
|
|
|
|
await prisma.workoutSet.create({
|
|
data: {
|
|
workoutSessionExerciseId: workoutSessionExercise.id,
|
|
setIndex: setIndex,
|
|
types: ["REPS", "WEIGHT"],
|
|
type: "WEIGHT",
|
|
valuesInt: [actualReps, Math.round(setWeight)],
|
|
valuesSec: [],
|
|
units: ["kg"],
|
|
completed: true,
|
|
},
|
|
});
|
|
|
|
totalSetsCreated++;
|
|
}
|
|
|
|
console.log(
|
|
` Added ${exercisePattern.sets.length} sets (${exercisePattern.sets[0].repsRange[0]}-${exercisePattern.sets[exercisePattern.sets.length - 1].repsRange[1]} reps)`,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add some recent incomplete sessions
|
|
console.log("\nAdding recent incomplete/planned sessions...");
|
|
|
|
for (let i = 0; i < 2; i++) {
|
|
const futureDate = new Date(today);
|
|
futureDate.setDate(today.getDate() + i + 1);
|
|
futureDate.setHours(18, 0, 0, 0);
|
|
|
|
const workoutSession = await prisma.workoutSession.create({
|
|
data: {
|
|
userId: USER_ID,
|
|
startedAt: futureDate,
|
|
},
|
|
});
|
|
|
|
await prisma.workoutSessionExercise.create({
|
|
data: {
|
|
workoutSessionId: workoutSession.id,
|
|
exerciseId: BENCH_PRESS_ID,
|
|
order: 0,
|
|
},
|
|
});
|
|
|
|
console.log(` Created planned session for ${futureDate.toLocaleDateString()}`);
|
|
}
|
|
|
|
console.log("\n✅ Successfully seeded workout data!");
|
|
console.log("\nSummary:");
|
|
console.log(`- Total sessions created: ${totalSessionsCreated}`);
|
|
console.log(`- Total sets created: ${totalSetsCreated}`);
|
|
console.log(`- Average sets per session: ${(totalSetsCreated / totalSessionsCreated).toFixed(1)}`);
|
|
console.log(`- Final working weight: ${(startingWeight * Math.pow(1.025, weeksToGenerate - 1)).toFixed(1)}kg`);
|
|
} catch (error) {
|
|
console.error("Error seeding data:", error);
|
|
throw error;
|
|
} finally {
|
|
await prisma.$disconnect();
|
|
}
|
|
}
|
|
|
|
// Parse command line arguments
|
|
const args = process.argv.slice(2);
|
|
const weeks = args[0] ? parseInt(args[0]) : 12;
|
|
const startWeight = args[1] ? parseInt(args[1]) : 60;
|
|
|
|
if (isNaN(weeks) || isNaN(startWeight)) {
|
|
console.error("Usage: tsx seed-workout-data-advanced.ts [weeks] [startingWeight]");
|
|
console.error("Example: tsx seed-workout-data-advanced.ts 12 60");
|
|
process.exit(1);
|
|
}
|
|
|
|
// Run the seed script
|
|
seedAdvancedWorkoutData(weeks, startWeight).catch((error) => {
|
|
console.error("Fatal error:", error);
|
|
process.exit(1);
|
|
});
|