Full Code of Snouzy/workout-cool for AI

main dd60a7bbf5d6 cached
664 files
2.6 MB
732.1k tokens
1182 symbols
1 requests
Download .txt
Showing preview only (2,908K chars total). Download the full file or copy to clipboard to get everything.
Repository: Snouzy/workout-cool
Branch: main
Commit: dd60a7bbf5d6
Files: 664
Total size: 2.6 MB

Directory structure:
gitextract_ajccn8ug/

├── .cursorrules
├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── config.yml
│   │   └── feature_request.md
│   ├── pull_request_template.md
│   └── workflows/
│       ├── ci.yml
│       ├── notify-discord-issues.yml
│       ├── notify-discord-pr.yml
│       ├── notify-discord.yml
│       └── publish-ghcr-image.yml
├── .gitignore
├── .npmrc
├── .prettierrc
├── .vscode/
│   └── settings.json
├── AGENTS.md
├── CLAUDE.md
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── app/
│   ├── [locale]/
│   │   ├── (admin)/
│   │   │   └── admin/
│   │   │       ├── [...catchAll]/
│   │   │       │   ├── not-found.tsx
│   │   │       │   └── page.tsx
│   │   │       ├── dashboard/
│   │   │       │   └── page.tsx
│   │   │       ├── layout.tsx
│   │   │       ├── not-found.tsx
│   │   │       ├── programs/
│   │   │       │   ├── [id]/
│   │   │       │   │   └── edit/
│   │   │       │   │       └── page.tsx
│   │   │       │   └── page.tsx
│   │   │       ├── settings/
│   │   │       │   └── page.tsx
│   │   │       └── users/
│   │   │           └── page.tsx
│   │   ├── (app)/
│   │   │   ├── (legal-and-payment)/
│   │   │   │   ├── layout.tsx
│   │   │   │   └── legal/
│   │   │   │       ├── privacy/
│   │   │   │       │   └── page.tsx
│   │   │   │       ├── sales-terms/
│   │   │   │       │   └── page.tsx
│   │   │   │       └── terms/
│   │   │   │           └── page.tsx
│   │   │   ├── [slug]/
│   │   │   │   └── layout.tsx
│   │   │   ├── about/
│   │   │   │   └── page.tsx
│   │   │   ├── auth/
│   │   │   │   ├── (auth-layout)/
│   │   │   │   │   ├── forgot-password/
│   │   │   │   │   │   └── page.tsx
│   │   │   │   │   ├── layout.tsx
│   │   │   │   │   ├── reset-password/
│   │   │   │   │   │   └── page.tsx
│   │   │   │   │   ├── signin/
│   │   │   │   │   │   └── page.tsx
│   │   │   │   │   └── signup/
│   │   │   │   │       └── page.tsx
│   │   │   │   ├── error/
│   │   │   │   │   └── page.tsx
│   │   │   │   ├── error.tsx
│   │   │   │   ├── layout.tsx
│   │   │   │   ├── signout/
│   │   │   │   │   └── page.tsx
│   │   │   │   ├── verify-email/
│   │   │   │   │   ├── layout.tsx
│   │   │   │   │   └── page.tsx
│   │   │   │   └── verify-request/
│   │   │   │       └── page.tsx
│   │   │   ├── layout.tsx
│   │   │   ├── leaderboard/
│   │   │   │   └── page.tsx
│   │   │   ├── onboarding/
│   │   │   │   ├── layout.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── page.tsx
│   │   │   ├── premium/
│   │   │   │   └── page.tsx
│   │   │   ├── profile/
│   │   │   │   └── page.tsx
│   │   │   ├── programs/
│   │   │   │   ├── [slug]/
│   │   │   │   │   ├── page.tsx
│   │   │   │   │   └── session/
│   │   │   │   │       └── [sessionSlug]/
│   │   │   │   │           ├── ProgramSessionClient.tsx
│   │   │   │   │           └── page.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── statistics/
│   │   │   │   └── page.tsx
│   │   │   └── tools/
│   │   │       ├── bmi-calculator/
│   │   │       │   ├── bmi-calculator.utils.ts
│   │   │       │   ├── page.tsx
│   │   │       │   └── shared/
│   │   │       │       ├── BmiCalculatorClient.tsx
│   │   │       │       └── components/
│   │   │       │           ├── BmiEducationalContent.tsx
│   │   │       │           ├── BmiHeightInput.tsx
│   │   │       │           ├── BmiResultsDisplay.tsx
│   │   │       │           ├── BmiUnitSelector.tsx
│   │   │       │           ├── BmiWeightInput.tsx
│   │   │       │           └── MathEquation.tsx
│   │   │       ├── calorie-calculator/
│   │   │       │   ├── CalorieCalculatorHub.tsx
│   │   │       │   ├── calorie-calculator-comparison/
│   │   │       │   │   ├── CalorieCalculatorComparison.tsx
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── calorie-calculator.utils.ts
│   │   │       │   ├── cunningham-calculator/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── harris-benedict-calculator/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── katch-mcardle-calculator/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── mifflin-st-jeor-calculator/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── oxford-calculator/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── page.tsx
│   │   │       │   ├── shared/
│   │   │       │   │   ├── CalorieCalculatorClient.tsx
│   │   │       │   │   ├── calculator-configs.ts
│   │   │       │   │   ├── calorie-formulas.utils.ts
│   │   │       │   │   ├── components/
│   │   │       │   │   │   ├── ActivityLevelSelector.tsx
│   │   │       │   │   │   ├── AgeInput.tsx
│   │   │       │   │   │   ├── BodyFatInput.tsx
│   │   │       │   │   │   ├── FAQSection.tsx
│   │   │       │   │   │   ├── GenderSelector.tsx
│   │   │       │   │   │   ├── GoalSelector.tsx
│   │   │       │   │   │   ├── HeightInput.tsx
│   │   │       │   │   │   ├── InfoButton.tsx
│   │   │       │   │   │   ├── InfoModal.tsx
│   │   │       │   │   │   ├── ResultsDisplay.tsx
│   │   │       │   │   │   ├── UnitSelector.tsx
│   │   │       │   │   │   ├── WeightInput.tsx
│   │   │       │   │   │   └── index.ts
│   │   │       │   │   └── types/
│   │   │       │   │       └── index.ts
│   │   │       │   └── styles.css
│   │   │       ├── heart-rate-zones/
│   │   │       │   ├── lib/
│   │   │       │   │   └── utils.ts
│   │   │       │   ├── page.tsx
│   │   │       │   ├── seo/
│   │   │       │   │   ├── config.ts
│   │   │       │   │   └── page-content.ts
│   │   │       │   └── ui/
│   │   │       │       ├── HeartRateZonesCalculatorClient.tsx
│   │   │       │       ├── components/
│   │   │       │       │   ├── EducationalContent.tsx
│   │   │       │       │   ├── EducationalContentServer.tsx
│   │   │       │       │   ├── FAQAccordion.tsx
│   │   │       │       │   ├── SEOOptimizedContentServer.tsx
│   │   │       │       │   └── ScrollToTopButton.tsx
│   │   │       │       └── styles.css
│   │   │       └── page.tsx
│   │   ├── @modal/
│   │   │   └── (.)auth/
│   │   │       └── login/
│   │   │           └── page.tsx
│   │   ├── layout.tsx
│   │   ├── manifest.json/
│   │   │   └── route.ts
│   │   ├── not-found.tsx
│   │   └── providers.tsx
│   ├── ads.txt/
│   │   └── route.ts
│   ├── api/
│   │   ├── analytics/
│   │   │   └── premium/
│   │   │       └── route.ts
│   │   ├── auth/
│   │   │   ├── [...all]/
│   │   │   │   └── route.ts
│   │   │   └── signup/
│   │   │       └── route.ts
│   │   ├── billing/
│   │   │   └── status/
│   │   │       └── route.ts
│   │   ├── exercises/
│   │   │   ├── [exerciseId]/
│   │   │   │   └── statistics/
│   │   │   │       ├── one-rep-max/
│   │   │   │       │   └── route.ts
│   │   │   │       ├── route.ts
│   │   │   │       ├── volume/
│   │   │   │       │   └── route.ts
│   │   │   │       └── weight-progression/
│   │   │   │           └── route.ts
│   │   │   ├── all/
│   │   │   │   └── route.ts
│   │   │   ├── route.ts
│   │   │   └── shuffle/
│   │   │       └── route.ts
│   │   ├── premium/
│   │   │   ├── billing-portal/
│   │   │   │   └── route.ts
│   │   │   ├── checkout/
│   │   │   │   └── route.ts
│   │   │   ├── plans/
│   │   │   │   └── route.ts
│   │   │   └── status/
│   │   │       └── route.ts
│   │   ├── programs/
│   │   │   ├── [slug]/
│   │   │   │   ├── enroll/
│   │   │   │   │   └── route.ts
│   │   │   │   ├── progress/
│   │   │   │   │   └── route.ts
│   │   │   │   ├── route.ts
│   │   │   │   └── sessions/
│   │   │   │       └── [sessionSlug]/
│   │   │   │           └── route.ts
│   │   │   ├── route.ts
│   │   │   └── session-progress/
│   │   │       ├── [progressId]/
│   │   │       │   └── complete/
│   │   │       │       └── route.ts
│   │   │       └── start/
│   │   │           └── route.ts
│   │   ├── revenuecat/
│   │   │   ├── link-user/
│   │   │   │   └── route.ts
│   │   │   ├── sync-status/
│   │   │   │   └── route.ts
│   │   │   └── webhook/
│   │   │       └── route.ts
│   │   ├── user/
│   │   │   ├── password/
│   │   │   │   └── route.ts
│   │   │   └── profile/
│   │   │       └── route.ts
│   │   ├── webhooks/
│   │   │   ├── revenuecat/
│   │   │   │   └── route.ts
│   │   │   └── stripe/
│   │   │       └── route.ts
│   │   └── workout-sessions/
│   │       ├── [sessionId]/
│   │       │   ├── feedback/
│   │       │   │   └── route.ts
│   │       │   ├── rating/
│   │       │   │   └── route.ts
│   │       │   ├── route.ts
│   │       │   └── summary/
│   │       │       └── route.ts
│   │       ├── sync/
│   │       │   └── route.ts
│   │       └── user/
│   │           └── [userId]/
│   │               └── route.ts
│   ├── robots.txt
│   └── sitemap.ts
├── components.json
├── content/
│   ├── about/
│   │   ├── en.mdx
│   │   ├── es.mdx
│   │   ├── fr.mdx
│   │   ├── pt.mdx
│   │   ├── ru.mdx
│   │   └── zh-CN.mdx
│   ├── privacy-policy/
│   │   ├── en.mdx
│   │   ├── es.mdx
│   │   ├── fr.mdx
│   │   ├── pt.mdx
│   │   ├── ru.mdx
│   │   └── zh-CN.mdx
│   ├── sales-terms/
│   │   ├── en.mdx
│   │   ├── es.mdx
│   │   ├── fr.mdx
│   │   ├── pt.mdx
│   │   ├── ru.mdx
│   │   └── zh-CN.mdx
│   └── terms/
│       ├── en.mdx
│       ├── es.mdx
│       ├── fr.mdx
│       ├── pt.mdx
│       ├── ru.mdx
│       └── zh-CN.mdx
├── data/
│   └── sample-exercises.csv
├── docker-compose.yml
├── docs/
│   └── SELF-HOSTING.md
├── emails/
│   ├── ContactSupportEmail.tsx
│   ├── DeleteAccountEmail.tsx
│   ├── ResetPasswordEmail.tsx
│   ├── VerifyEmail.tsx
│   └── utils/
│       └── BaseEmailLayout.tsx
├── eslint.config.mjs
├── locales/
│   ├── client.ts
│   ├── en.ts
│   ├── es.ts
│   ├── fr.ts
│   ├── heart-rate-zones-translations.ts
│   ├── pt.ts
│   ├── ru.ts
│   ├── server.ts
│   ├── types.ts
│   └── zh-CN.ts
├── middleware.ts
├── next.config.ts
├── nextauth.d.ts
├── package.json
├── postcss.config.mjs
├── prisma/
│   ├── migrations/
│   │   └── 0_init/
│   │       └── migration.sql
│   ├── migrations_backup/
│   │   ├── 20240726_simplify_subscription_model/
│   │   │   └── migration.sql
│   │   ├── 20250101000000_baseline/
│   │   │   └── migration.sql
│   │   ├── 20250117000000_add_statistics_indexes/
│   │   │   └── migration.sql
│   │   ├── 20250414120436_init/
│   │   │   └── migration.sql
│   │   ├── 20250414170807_add_feedbacks/
│   │   │   └── migration.sql
│   │   ├── 20250414174246_rename_feedbacks/
│   │   │   └── migration.sql
│   │   ├── 20250414232816_add_first_name_and_last_name/
│   │   │   └── migration.sql
│   │   ├── 20250416160303_add_plans/
│   │   │   └── migration.sql
│   │   ├── 20250416160502_map/
│   │   │   └── migration.sql
│   │   ├── 20250505114841_add_user_role/
│   │   │   └── migration.sql
│   │   ├── 20250505191954_admin_and_user_lowercase/
│   │   │   └── migration.sql
│   │   ├── 20250610182024_add_exercises_and_attributes/
│   │   │   └── migration.sql
│   │   ├── 20250610182815_add_exercise_enums/
│   │   │   └── migration.sql
│   │   ├── 20250610184725_simplified_exercises/
│   │   │   └── migration.sql
│   │   ├── 20250611190228_convert_text_to_enums/
│   │   │   └── migration.sql
│   │   ├── 20250611210106_add_enum_values/
│   │   │   └── migration.sql
│   │   ├── 20250612213546_workout_session_sets/
│   │   │   └── migration.sql
│   │   ├── 20250613095031_add_multi_column_support/
│   │   │   └── migration.sql
│   │   ├── 20250614125347_add_table_maps/
│   │   │   └── migration.sql
│   │   ├── 20250614153656_remove_value_int_value_sec_unit_from_workoutset/
│   │   │   └── migration.sql
│   │   ├── 20250615160343_add_muscle_to_a_workout_session/
│   │   │   └── migration.sql
│   │   ├── 20250615170916_add_cascade_delete_workout_sessions/
│   │   │   └── migration.sql
│   │   ├── 20250623142458_add_billing_and_subscriptions/
│   │   │   └── migration.sql
│   │   ├── 20250623143952_remove_webhook_events/
│   │   │   └── migration.sql
│   │   ├── 20250623144324_add_webhook_events/
│   │   │   └── migration.sql
│   │   ├── 20250625155932_add_admin/
│   │   │   └── migration.sql
│   │   ├── 20250625195907_add_program_visibility/
│   │   │   └── migration.sql
│   │   ├── 20250626102058_add_i18n_slugs_on_program/
│   │   │   └── migration.sql
│   │   ├── 20250626134345_remove_emoji_on_program/
│   │   │   └── migration.sql
│   │   ├── 20250626182857_cleanup_billing_system/
│   │   │   └── migration.sql
│   │   ├── 20250626204136_remove_payment_table/
│   │   │   └── migration.sql
│   │   ├── 20250626205121_remove_legacy_premium_fields/
│   │   │   └── migration.sql
│   │   ├── 20250626205904_remove_payment_table_keep_ispremium/
│   │   │   └── migration.sql
│   │   ├── 20250707114920_add_user_favorite_exercises/
│   │   │   └── migration.sql
│   │   ├── 20250708214116_add_rating_to_wkt_sessions/
│   │   │   └── migration.sql
│   │   ├── 20250709_add_revenuecat_fields/
│   │   │   └── migration.sql
│   │   └── migration_lock.toml
│   └── schema.prisma
├── public/
│   ├── _ads.txt
│   ├── manifest.json
│   └── sw.js
├── scripts/
│   ├── check-pricing-config.ts
│   ├── import-exercises-with-attributes.prompt.md
│   ├── import-exercises-with-attributes.ts
│   ├── seed-leaderboard-data.ts
│   ├── seed-multi-region-plans.ts
│   ├── seed-subscription-plans-simple.ts
│   ├── seed-workout-data-advanced.ts
│   └── setup.sh
├── src/
│   ├── components/
│   │   ├── ads/
│   │   │   ├── AdBlockerForPremium.tsx
│   │   │   ├── AdPlaceholder.tsx
│   │   │   ├── AdSenseAutoAds.tsx
│   │   │   ├── AdWrapper.tsx
│   │   │   ├── EzoicAd.tsx
│   │   │   ├── GoogleAdSense.tsx
│   │   │   ├── HorizontalAdBanner.tsx
│   │   │   ├── HorizontalBottomBanner.tsx
│   │   │   ├── HorizontalTopBanner.tsx
│   │   │   ├── InArticle.tsx
│   │   │   ├── ResponsiveAdBanner.tsx
│   │   │   ├── VerticalAdBanner.tsx
│   │   │   ├── VerticalLeftBanner.tsx
│   │   │   ├── VerticalRightBanner.tsx
│   │   │   ├── index.ts
│   │   │   └── nutripure-affiliate-banner.tsx
│   │   ├── premium/
│   │   │   └── RemoveAdsText.tsx
│   │   ├── pwa/
│   │   │   └── ServiceWorkerRegistration.tsx
│   │   ├── seo/
│   │   │   ├── SEOHead.tsx
│   │   │   ├── breadcrumbs.tsx
│   │   │   ├── duration-badge.tsx
│   │   │   ├── rich-snippet-rating.tsx
│   │   │   └── session-rich-snippets.tsx
│   │   ├── svg/
│   │   │   ├── BrokenLink.tsx
│   │   │   ├── Calendly.tsx
│   │   │   ├── CircleSvg.tsx
│   │   │   ├── DiscordSvg.tsx
│   │   │   ├── DotPattern.tsx
│   │   │   ├── GoogleSvg.tsx
│   │   │   ├── IconCheckboxCheck.tsx
│   │   │   ├── LogoSvg.tsx
│   │   │   ├── UnderlineSvg.tsx
│   │   │   ├── VerifiedBadge.tsx
│   │   │   └── Youtube.tsx
│   │   ├── ui/
│   │   │   ├── 404-page-not-found.tsx
│   │   │   ├── Bento.tsx
│   │   │   ├── ToastSonner.tsx
│   │   │   ├── accordion.tsx
│   │   │   ├── alert-dialog.tsx
│   │   │   ├── alert.tsx
│   │   │   ├── animated-button/
│   │   │   │   └── ShinyButton.tsx
│   │   │   ├── aspect-ratio.tsx
│   │   │   ├── avatar.tsx
│   │   │   ├── badge.tsx
│   │   │   ├── bottom-sheet-vaul.tsx
│   │   │   ├── bottom-sheet.tsx
│   │   │   ├── button.tsx
│   │   │   ├── card-styled.tsx
│   │   │   ├── card.tsx
│   │   │   ├── collapsible.tsx
│   │   │   ├── dialog-stack.tsx
│   │   │   ├── dialog.tsx
│   │   │   ├── divider.tsx
│   │   │   ├── donation-alert.tsx
│   │   │   ├── dropdown-menu.tsx
│   │   │   ├── form.tsx
│   │   │   ├── hover-card.tsx
│   │   │   ├── input-password-strength.tsx
│   │   │   ├── input.tsx
│   │   │   ├── iphone-mockup.tsx
│   │   │   ├── label.tsx
│   │   │   ├── link.tsx
│   │   │   ├── loader.tsx
│   │   │   ├── local-alert.tsx
│   │   │   ├── moving-border.tsx
│   │   │   ├── navigation-menu.tsx
│   │   │   ├── next-top-loader.tsx
│   │   │   ├── pagination.tsx
│   │   │   ├── phone-frame-preview.tsx
│   │   │   ├── popover.tsx
│   │   │   ├── premium-gate.tsx
│   │   │   ├── premium-upsell-alert.tsx
│   │   │   ├── radio-group.tsx
│   │   │   ├── scroll-area.tsx
│   │   │   ├── select.tsx
│   │   │   ├── separator.tsx
│   │   │   ├── sheet.tsx
│   │   │   ├── shine-border.tsx
│   │   │   ├── simple-select.tsx
│   │   │   ├── skeleton.tsx
│   │   │   ├── slider.tsx
│   │   │   ├── sonner.tsx
│   │   │   ├── star-button.tsx
│   │   │   ├── switch.tsx
│   │   │   ├── table.tsx
│   │   │   ├── tabs.tsx
│   │   │   ├── textarea.tsx
│   │   │   ├── theme-provider.tsx
│   │   │   ├── timer.tsx
│   │   │   ├── title-with-dot.tsx
│   │   │   ├── toast.tsx
│   │   │   ├── toaster.tsx
│   │   │   ├── tooltip.tsx
│   │   │   ├── typography.tsx
│   │   │   ├── use-toast.ts
│   │   │   └── workout-lol.tsx
│   │   ├── utils/
│   │   │   ├── ErrorBoundaries.tsx
│   │   │   └── TailwindIndicator.tsx
│   │   └── version.tsx
│   ├── entities/
│   │   ├── exercise/
│   │   │   ├── shared/
│   │   │   │   └── muscles.tsx
│   │   │   └── types/
│   │   │       └── exercise.types.ts
│   │   ├── program/
│   │   │   └── types/
│   │   │       └── program.types.ts
│   │   ├── program-session/
│   │   │   └── types/
│   │   │       └── program-session.types.ts
│   │   └── user/
│   │       ├── lib/
│   │       │   └── display-name.ts
│   │       ├── model/
│   │       │   ├── get-server-session-user.ts
│   │       │   ├── get-users.actions.ts
│   │       │   ├── update-user-locale.ts
│   │       │   ├── update-user.action.ts
│   │       │   ├── use-auto-locale.ts
│   │       │   ├── useCurrentSession.ts
│   │       │   └── useCurrentUser.ts
│   │       ├── schemas/
│   │       │   ├── get-user.schema.ts
│   │       │   └── update-user.schema.ts
│   │       └── types/
│   │           └── session-user.ts
│   ├── env.ts
│   ├── features/
│   │   ├── admin/
│   │   │   ├── layout/
│   │   │   │   ├── admin-header.tsx
│   │   │   │   └── admin-sidebar/
│   │   │   │       └── ui/
│   │   │   │           ├── admin-header.tsx
│   │   │   │           └── admin-sidebar.tsx
│   │   │   ├── programs/
│   │   │   │   ├── actions/
│   │   │   │   │   ├── add-exercise.action.ts
│   │   │   │   │   ├── add-session.action.ts
│   │   │   │   │   ├── add-week.action.ts
│   │   │   │   │   ├── create-program.action.ts
│   │   │   │   │   ├── delete-program.action.ts
│   │   │   │   │   ├── get-programs.action.ts
│   │   │   │   │   ├── update-exercise-sets.action.ts
│   │   │   │   │   ├── update-program-visibility.action.ts
│   │   │   │   │   ├── update-program.action.ts
│   │   │   │   │   ├── update-session.action.ts
│   │   │   │   │   └── update-week.action.ts
│   │   │   │   ├── types/
│   │   │   │   │   └── program.types.ts
│   │   │   │   └── ui/
│   │   │   │       ├── add-exercise-modal.tsx
│   │   │   │       ├── add-session-modal.tsx
│   │   │   │       ├── add-week-modal.tsx
│   │   │   │       ├── create-program-button.tsx
│   │   │   │       ├── create-program-form.tsx
│   │   │   │       ├── create-program-modal.tsx
│   │   │   │       ├── delete-program-button.tsx
│   │   │   │       ├── edit-program-modal.tsx
│   │   │   │       ├── edit-session-modal.tsx
│   │   │   │       ├── edit-sets-modal.tsx
│   │   │   │       ├── edit-week-modal.tsx
│   │   │   │       ├── program-builder.tsx
│   │   │   │       ├── programs-list.tsx
│   │   │   │       ├── session-card.tsx
│   │   │   │       ├── visibility-badge.tsx
│   │   │   │       └── week-card.tsx
│   │   │   └── users/
│   │   │       └── list/
│   │   │           └── ui/
│   │   │               └── users-table.tsx
│   │   ├── ads/
│   │   │   └── hooks/
│   │   │       └── useUserSubscription.ts
│   │   ├── auth/
│   │   │   ├── forgot-password/
│   │   │   │   ├── forgot-password.schema.ts
│   │   │   │   ├── model/
│   │   │   │   │   └── useForgotPassword.tsx
│   │   │   │   └── ui/
│   │   │   │       └── forgot-password-form.tsx
│   │   │   ├── lib/
│   │   │   │   ├── auth-client.ts
│   │   │   │   └── better-auth.ts
│   │   │   ├── model/
│   │   │   │   └── useLogout.ts
│   │   │   ├── reset-password/
│   │   │   │   ├── model/
│   │   │   │   │   └── useResetPassword.ts
│   │   │   │   ├── schema/
│   │   │   │   │   └── reset-password.schema.ts
│   │   │   │   └── ui/
│   │   │   │       └── reset-password-form.tsx
│   │   │   ├── signin/
│   │   │   │   ├── model/
│   │   │   │   │   └── useSignIn.ts
│   │   │   │   ├── schema/
│   │   │   │   │   └── signin.schema.ts
│   │   │   │   └── ui/
│   │   │   │       └── CredentialsLoginForm.tsx
│   │   │   ├── signup/
│   │   │   │   ├── model/
│   │   │   │   │   ├── signup.action.ts
│   │   │   │   │   └── useSignUp.ts
│   │   │   │   ├── schema/
│   │   │   │   │   └── signup.schema.ts
│   │   │   │   └── ui/
│   │   │   │       └── signup-form.tsx
│   │   │   ├── ui/
│   │   │   │   ├── AuthButtonServer.tsx
│   │   │   │   ├── LoggedInButton.tsx
│   │   │   │   ├── ProviderButton.tsx
│   │   │   │   ├── SignInButton.tsx
│   │   │   │   └── SignUpButton.tsx
│   │   │   └── verify-email/
│   │   │       ├── constants.ts
│   │   │       ├── model/
│   │   │       │   └── useResendEmail.ts
│   │   │       └── ui/
│   │   │           └── verify-email-page.tsx
│   │   ├── consent-banner/
│   │   │   ├── model/
│   │   │   │   └── tracking-consent.action.ts
│   │   │   ├── schema/
│   │   │   │   └── tracking-consent.schema.ts
│   │   │   └── ui/
│   │   │       └── consent-banner.tsx
│   │   ├── contact/
│   │   │   └── support/
│   │   │       ├── ContactSupportDialog.tsx
│   │   │       ├── contact-support.action.ts
│   │   │       └── contact-support.schema.ts
│   │   ├── contact-feedback/
│   │   │   ├── model/
│   │   │   │   ├── contact-feedback.action.ts
│   │   │   │   └── contact-feedback.schema.ts
│   │   │   └── ui/
│   │   │       ├── ReviewInput.tsx
│   │   │       └── contact-feedback-popover.tsx
│   │   ├── dialogs-provider/
│   │   │   ├── DialogProvider.tsx
│   │   │   └── DialogProviderDialog.tsx
│   │   ├── email/
│   │   │   ├── EmailForm.tsx
│   │   │   ├── email.action.ts
│   │   │   └── email.schema.ts
│   │   ├── form/
│   │   │   └── SubmitButton.tsx
│   │   ├── layout/
│   │   │   ├── BottomNavigation.tsx
│   │   │   ├── Footer.tsx
│   │   │   ├── Header.tsx
│   │   │   ├── model/
│   │   │   │   └── use-sidebar.store.tsx
│   │   │   ├── nav-link.tsx
│   │   │   ├── page-heading.tsx
│   │   │   ├── useSidebarToggle.ts
│   │   │   └── workout-streak-header.tsx
│   │   ├── leaderboard/
│   │   │   ├── actions/
│   │   │   │   ├── get-top-workout-users.action.ts
│   │   │   │   └── get-user-position.action.ts
│   │   │   ├── hooks/
│   │   │   │   ├── use-top-workout-users.ts
│   │   │   │   └── use-user-position.ts
│   │   │   ├── lib/
│   │   │   │   └── utils.ts
│   │   │   ├── models/
│   │   │   │   └── types.ts
│   │   │   └── ui/
│   │   │       ├── leaderboard-item.tsx
│   │   │       ├── leaderboard-page.tsx
│   │   │       ├── leaderboard-skeleton.tsx
│   │   │       └── user-leaderboard-position.tsx
│   │   ├── page/
│   │   │   └── layout.tsx
│   │   ├── premium/
│   │   │   └── ui/
│   │   │       ├── README.md
│   │   │       ├── conversion-flow-notification.tsx
│   │   │       ├── feature-comparison-table.tsx
│   │   │       ├── index.ts
│   │   │       ├── premium-upgrade-card.tsx
│   │   │       ├── pricing-faq.tsx
│   │   │       ├── pricing-hero-section.tsx
│   │   │       └── pricing-testimonials.tsx
│   │   ├── programs/
│   │   │   ├── actions/
│   │   │   │   ├── complete-program-session.action.ts
│   │   │   │   ├── enroll-program.action.ts
│   │   │   │   ├── get-program-by-slug.action.ts
│   │   │   │   ├── get-program-progress-by-slug.action.ts
│   │   │   │   ├── get-program-progress.action.ts
│   │   │   │   ├── get-public-programs.action.ts
│   │   │   │   ├── get-session-by-slug.action.ts
│   │   │   │   ├── get-sitemap-data.action.ts
│   │   │   │   └── start-program-session.action.ts
│   │   │   ├── hooks/
│   │   │   │   └── use-program-share.ts
│   │   │   ├── lib/
│   │   │   │   ├── program-metadata.ts
│   │   │   │   ├── session-metadata.ts
│   │   │   │   ├── suggested-sets-helpers.ts
│   │   │   │   └── translations-mapper.ts
│   │   │   └── ui/
│   │   │       ├── program-card.tsx
│   │   │       ├── program-detail-page.tsx
│   │   │       ├── program-progress.tsx
│   │   │       ├── programs-page.tsx
│   │   │       ├── session-access-guard.tsx
│   │   │       ├── share-button.tsx
│   │   │       └── welcome-modal.tsx
│   │   ├── release-notes/
│   │   │   ├── hooks/
│   │   │   │   ├── index.ts
│   │   │   │   └── use-changelog-notification.ts
│   │   │   ├── index.ts
│   │   │   ├── lib/
│   │   │   │   └── date-utils.ts
│   │   │   ├── model/
│   │   │   │   ├── changelog-notification.local.ts
│   │   │   │   └── notes.ts
│   │   │   ├── types/
│   │   │   │   └── notification.ts
│   │   │   └── ui/
│   │   │       ├── changelog-notification-badge.tsx
│   │   │       ├── index.ts
│   │   │       └── release-notes-dialog.tsx
│   │   ├── statistics/
│   │   │   ├── components/
│   │   │   │   ├── ExerciseSelection.tsx
│   │   │   │   ├── ExerciseStatisticsTab.tsx
│   │   │   │   ├── ExercisesBrowser.tsx
│   │   │   │   ├── OneRepMaxChart.tsx
│   │   │   │   ├── StatisticsPreviewOverlay.tsx
│   │   │   │   ├── TimeframeSelector.tsx
│   │   │   │   ├── VolumeChart.tsx
│   │   │   │   ├── WeightProgressionChart.tsx
│   │   │   │   └── index.ts
│   │   │   ├── hooks/
│   │   │   │   ├── use-chart-theme.ts
│   │   │   │   └── use-exercise-statistics.ts
│   │   │   └── types/
│   │   │       └── index.ts
│   │   ├── theme/
│   │   │   ├── ThemeProviders.tsx
│   │   │   ├── ThemeToggle.tsx
│   │   │   └── ui/
│   │   │       └── ThemeSynchronizer.tsx
│   │   ├── update-password/
│   │   │   ├── lib/
│   │   │   │   ├── hash.ts
│   │   │   │   └── validate-password.ts
│   │   │   ├── model/
│   │   │   │   ├── update-password.action.ts
│   │   │   │   └── update-password.schema.ts
│   │   │   └── ui/
│   │   │       └── password-form.tsx
│   │   ├── user/
│   │   │   └── ui/
│   │   │       └── UserDropdown.tsx
│   │   ├── workout-builder/
│   │   │   ├── actions/
│   │   │   │   ├── get-exercises-by-muscle.action.ts
│   │   │   │   ├── get-exercises.action.ts
│   │   │   │   ├── get-favorite-exercises.action.ts
│   │   │   │   ├── pick-exercise.action.ts
│   │   │   │   ├── shuffle-exercise.action.ts
│   │   │   │   └── sync-favorite-exercises.action.ts
│   │   │   ├── hooks/
│   │   │   │   ├── use-exercises.ts
│   │   │   │   ├── use-favorite-exercises.service.ts
│   │   │   │   ├── use-favorites-modal.ts
│   │   │   │   ├── use-sync-favorite-exercises.ts
│   │   │   │   ├── use-workout-session.ts
│   │   │   │   └── use-workout-stepper.ts
│   │   │   ├── index.ts
│   │   │   ├── model/
│   │   │   │   ├── equipment-config.ts
│   │   │   │   ├── favorite-exercises-synchronizer.tsx
│   │   │   │   ├── favorite-exercises.local.ts
│   │   │   │   └── workout-builder.store.ts
│   │   │   ├── schema/
│   │   │   │   └── get-exercises.schema.ts
│   │   │   ├── types/
│   │   │   │   └── index.ts
│   │   │   └── ui/
│   │   │       ├── add-exercise-modal.tsx
│   │   │       ├── equipment-selection.tsx
│   │   │       ├── exercise-card.tsx
│   │   │       ├── exercise-list-item.tsx
│   │   │       ├── exercise-pick-modal.tsx
│   │   │       ├── exercise-video-modal.tsx
│   │   │       ├── exercises-selection.tsx
│   │   │       ├── favorite-button.tsx
│   │   │       ├── favorite-exercise-button.tsx
│   │   │       ├── muscle-selection.tsx
│   │   │       ├── muscles/
│   │   │       │   ├── abdominals-group.tsx
│   │   │       │   ├── back-group.tsx
│   │   │       │   ├── biceps-group.tsx
│   │   │       │   ├── calves-group.tsx
│   │   │       │   ├── chest-group.tsx
│   │   │       │   ├── forearms-group.tsx
│   │   │       │   ├── glutes-group.tsx
│   │   │       │   ├── hamstrings-group.tsx
│   │   │       │   ├── obliques-group.tsx
│   │   │       │   ├── quadriceps-group.tsx
│   │   │       │   ├── shoulders-group.tsx
│   │   │       │   ├── traps-group.tsx
│   │   │       │   └── triceps-group.tsx
│   │   │       ├── muscles.module.css
│   │   │       ├── quit-workout-dialog.tsx
│   │   │       ├── stepper-header.tsx
│   │   │       ├── workout-stepper-footer.tsx
│   │   │       └── workout-stepper.tsx
│   │   └── workout-session/
│   │       ├── actions/
│   │       │   ├── delete-workout-session.action.ts
│   │       │   ├── get-workout-sessions.action.ts
│   │       │   └── sync-workout-sessions.action.ts
│   │       ├── hooks/
│   │       │   └── use-donation-modal.ts
│   │       ├── lib/
│   │       │   └── workout-set-labels.ts
│   │       ├── model/
│   │       │   ├── use-sync-workout-sessions.ts
│   │       │   ├── use-workout-session.ts
│   │       │   ├── use-workout-sessions.ts
│   │       │   └── workout-session.store.ts
│   │       ├── types/
│   │       │   └── workout-set.ts
│   │       └── ui/
│   │           ├── donation-modal.tsx
│   │           ├── workout-session-header.tsx
│   │           ├── workout-session-heatmap.tsx
│   │           ├── workout-session-list.tsx
│   │           ├── workout-session-set.tsx
│   │           ├── workout-session-sets.tsx
│   │           ├── workout-session-timer.tsx
│   │           └── workout-sessions-synchronizer.tsx
│   ├── index.d.ts
│   ├── shared/
│   │   ├── api/
│   │   │   ├── README.md
│   │   │   ├── createHandler.ts
│   │   │   ├── handlers.ts
│   │   │   ├── mobile-auth.ts
│   │   │   ├── mobile-cookie-utils.ts
│   │   │   ├── mobile-safe-actions.ts
│   │   │   └── safe-actions.ts
│   │   ├── config/
│   │   │   ├── localized-metadata.ts
│   │   │   └── site-config.ts
│   │   ├── constants/
│   │   │   ├── cookies.ts
│   │   │   ├── errors.ts
│   │   │   ├── paths.ts
│   │   │   ├── placeholders.ts
│   │   │   ├── regexs.ts
│   │   │   ├── screen.ts
│   │   │   ├── social-platforms.tsx
│   │   │   ├── statistics.ts
│   │   │   ├── success.ts
│   │   │   └── workout-set-types.ts
│   │   ├── hooks/
│   │   │   ├── use-clipboard.ts
│   │   │   ├── use-premium-plans.ts
│   │   │   ├── useBoolean.ts
│   │   │   ├── useIsMobile.ts
│   │   │   └── useScrollToTop.ts
│   │   ├── lib/
│   │   │   ├── access-control.ts
│   │   │   ├── analytics/
│   │   │   │   ├── client.tsx
│   │   │   │   ├── events.ts
│   │   │   │   └── server.ts
│   │   │   ├── attribute-value-translation.ts
│   │   │   ├── date.ts
│   │   │   ├── format.ts
│   │   │   ├── guards.ts
│   │   │   ├── i18n-mapper.ts
│   │   │   ├── locale-slug.ts
│   │   │   ├── location/
│   │   │   │   ├── eu-countries.ts
│   │   │   │   └── location.ts
│   │   │   ├── logger.ts
│   │   │   ├── mail/
│   │   │   │   └── sendEmail.ts
│   │   │   ├── mdx/
│   │   │   │   └── load-mdx.ts
│   │   │   ├── network/
│   │   │   │   └── use-network-status.ts
│   │   │   ├── premium/
│   │   │   │   ├── premium.manager.ts
│   │   │   │   ├── premium.service.ts
│   │   │   │   ├── providers/
│   │   │   │   │   ├── base-provider.ts
│   │   │   │   │   └── stripe-provider.ts
│   │   │   │   ├── use-pending-checkout.ts
│   │   │   │   ├── use-premium-redirect.ts
│   │   │   │   └── use-premium.ts
│   │   │   ├── prisma.ts
│   │   │   ├── revenuecat/
│   │   │   │   ├── index.ts
│   │   │   │   ├── revenuecat.api.ts
│   │   │   │   ├── revenuecat.config.ts
│   │   │   │   └── revenuecat.mapping.ts
│   │   │   ├── server-url.ts
│   │   │   ├── slug.ts
│   │   │   ├── structured-data.ts
│   │   │   ├── utils.ts
│   │   │   ├── version.ts
│   │   │   ├── web-share.ts
│   │   │   ├── weight-conversion.ts
│   │   │   ├── workout-session/
│   │   │   │   ├── equipments.ts
│   │   │   │   ├── types/
│   │   │   │   │   └── workout-session.ts
│   │   │   │   ├── use-workout-session.service.ts
│   │   │   │   ├── workout-session.api.ts
│   │   │   │   └── workout-session.local.ts
│   │   │   └── youtube.ts
│   │   ├── schemas/
│   │   │   └── url.ts
│   │   ├── styles/
│   │   │   ├── additional-styles/
│   │   │   │   ├── highlights.css
│   │   │   │   └── utility-patterns.css
│   │   │   └── globals.css
│   │   └── types/
│   │       ├── i18n.types.ts
│   │       ├── next.ts
│   │       ├── premium.types.ts
│   │       ├── statistics.types.ts
│   │       └── storage.ts
│   └── widgets/
│       ├── 404.tsx
│       └── language-selector/
│           └── language-selector.tsx
├── tailwind.config.ts
├── tsconfig.json
└── workout-cool.code-workspace

================================================
FILE CONTENTS
================================================

================================================
FILE: .cursorrules
================================================
## 🧑‍💻 Development Guidelines

This project follows **Next.js (App Router)** and is structured using **Feature-Sliced Design (FSD)** for modularity, scalability, and clear
separation of concerns.

Use this prompt and coding standards to ensure consistency across the codebase:

---

### 🔧 Code Style and Structure

- Write concise, expressive, and idiomatic **TypeScript**
- Use **functional programming** patterns (avoid classes and side effects)
- Prefer **composition** over inheritance, and modularization over duplication
- Organize each `feature/`, `entity/`, or `widget/` with:

  - model/ → logic (React Query, actions, hooks)
  - schema/ → Zod schemas for validation ui/ → client components (TSX)
  - lib/ → pure helper functions
  - types/ → interfaces & TS types

- All external dependencies (**API**, `localStorage`, `Date`) must be **abstracted** in `shared/lib/`
- Avoid direct calls to:
- `fetch` → use actions or `shared/api/`
- `new Date()` → use `shared/lib/date` abstraction
- `localStorage` → wrap in `shared/lib/storage`

---

### 🧠 Naming Conventions

- Use `kebab-case` for **directories** (e.g. `features/auth/signup`)
- Use **named exports** (no default exports for components)
- Use descriptive names with **auxiliary verbs** (e.g. `isLoading`, `hasError`, `canSubmit`)
- Components:
- Pure UI: `src/components/ui/`
- Shared logic: `src/shared/lib/`
- Composition: `src/widgets/`

---

### 📐 TypeScript Usage

- Use `interface` over `type` for objects
- Avoid `enum`; use `as const` object maps instead
- Use `infer` and `z.infer<typeof schema>` for accurate form types
- Types live in `types/` or colocated with usage

---

### 📦 Feature Architecture

**Keep React component logic inside the relevant feature:**

features/auth/signup/ ├── model/ → useSignUp.ts, signup.action.ts ├── schema/ → signup.schema.ts ├── ui/ → signup-form.tsx

If reusable between many features (e.g. `User`, `Link`, `Session`), move logic to `entities/`.

---

### 🧪 Error Handling & Validation

- Use **Zod** for schema validation
- Prefer early returns & guard clauses
- Use `ActionError` in server actions and handle them with `next-safe-action`
- Wrap React components in `ErrorBoundary` (or `shared/ui/ErrorBoundaries.tsx`)
- Display user-friendly errors via `toast()` or `<Alert />`

---

### 💅 UI & Styling

- Use **Shadcn UI**, **Radix**, and **Tailwind CSS** with **mobile-first** responsive design
- Design theme:

  - **Minimal**, professional with a **slightly playful touch**
  - Inspired by **Apple**, tailored to fitness coaches
  - Emphasize visuals: badges, progress bars, illustrations
  - Use `lucide-react` icons, subtle borders, hover feedback
  - Avoid drop shadows; prefer light borders and soft hover effects

- Animations:

  - Elegant and performant (use `framer-motion` if needed)
  - Use `transition`, `duration-xxx`, and `ease-xxx` from Tailwind

- UX Principles:

  - Clear hierarchy
  - Responsive: no overflow, no overlap
  - All buttons and interactive elements should provide feedback
  - Use @tailwind.config.ts for the theme.

- **UI Stack**:

  - **Shadcn UI**, **Radix UI**, and **Tailwind CSS** (mobile-first approach)
  - Icons: **lucide-react**

- **Design Language**:

  - 🎨 **Modern & minimalist**, inspired by **Apple’s design system**, with a **slightly more colorful palette**
  - Interface should be **clean**, **cohesive**, and **functional** without sacrificing features
  - Avoid drop shadows; prefer **subtle borders** where relevant
  - Ensure a **clear visual hierarchy** and **intuitive navigation**

- **Interactive Components**:

  - Buttons and inputs must be **elegant**, with **subtle visual feedback** (hover, click, validation)
  - Use **addictive micro-interactions** sparingly to enhance engagement without clutter

- **Animations**:

  - Use Tailwind’s built-in utilities: `transition`, `duration-xxx`, `ease-xxx` for basic transitions
  - Use `framer-motion` for advanced animations only if necessary
  - ✅ **Performance comes first**: animations must be smooth and lightweight

- **Responsiveness**:

  - Fully responsive layout: **no overlapping**, **no overflow**
  - Consistent behavior across all devices, from mobile to desktop

- **User Experience**:
  - All interactive elements must provide **clear visual feedback**
  - Interfaces should remain **simple to navigate**, even when **feature-rich**

---

### 🧱 Rendering & Performance

- Favor **Server Components** (`RSC`) and SSR for pages and logic
- Limit `'use client'` usage — only where needed:
  - form states, event listeners, animations
- Wrap all client components in `<Suspense />` with fallback
- Use dynamic import for non-critical UI (e.g. `Dialog`, `Chart`)
- Optimize media:
  - Use **WebP** images with width/height
  - Enable lazy loading where possible

---

### 🔍 Data, Forms, Actions

- Use `@tanstack/react-query` for client state
- Use `next-safe-action` for server mutations and queries
- All actions should:
  - Have clear schema (`schema/`)
  - Model expected errors with `ActionError`
  - Return typed output
  - Use the clientAction from `@/shared/api/safe-actions`
- Use `Form`, `FormField`, `FormMessage` from Shadcn for all forms

---

### 🧭 Routing & Navigation

- All routes defined in `app/`, avoid logic here
- Use constants in `shared/constants/paths.ts`
- For search parameters, use `nuqs` (`useQueryState`) — never manipulate `router.query` directly
- Follow Next.js App Router standards for layouts and segments

---

- [Feature-Sliced Design](https://feature-sliced.design/)
- [Shadcn UI](https://ui.shadcn.com/)
- [Zod](https://zod.dev/)


================================================
FILE: .github/FUNDING.yml
================================================
github: snouzy
ko_fi: workoutcool
# buy_me_a_coffee: workout_cool


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ""
labels: ""
assignees: ""
---

**Describe the bug** A clear and concise description of what the bug is.

**To Reproduce** Steps to reproduce the behavior:

1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Expected behavior** A clear and concise description of what you expected to happen.

**Screenshots** If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**

- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]

**Smartphone (please complete the following information):**

- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]

**Additional context** Add any other context about the problem here.


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
  - name: 💬 Workout Cool Discord
    url: https://discord.gg/NtrsUBuHUB
    about: Please use our Discord server for all questions, discussions, and support.

================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest a feature for this project
title: ""
labels: ""
assignees: ""
---

**Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always
frustrated when [...]

**Describe the solution you'd like** A clear and concise description of what you want to happen.

**Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered.

**Additional context** Add any other context or screenshots about the feature request here.


================================================
FILE: .github/pull_request_template.md
================================================
## 📝 Description

<!-- Briefly describe the changes made -->

## 📋 Checklist

- [ ] My code follows the project conventions
- [ ] This PR includes breaking changes
- [ ] I have updated documentation if necessary

## 🗃️ Prisma Migrations (if applicable)

- [ ] I have created a migration
- [ ] I have tested the migration locally

## 📸 Screenshots (if applicable)

<!-- Add screenshots for visual changes -->

## 🔗 Related Issues

<!-- Reference issues: Closes #123, Fixes #456 -->


================================================
FILE: .github/workflows/ci.yml
================================================
name: ci

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: Setup pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 9

      - name: Get pnpm store directory
        shell: bash
        run: |
          echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV

      - name: Setup pnpm cache
        uses: actions/cache@v4
        with:
          path: ${{ env.STORE_PATH }}
          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
          restore-keys: |
            ${{ runner.os }}-pnpm-store-

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Generate Prisma client
        run: pnpm prisma generate

      - name: Run linting
        run: pnpm lint

  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: Setup pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 9

      - name: Get pnpm store directory
        shell: bash
        run: |
          echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV

      - name: Setup pnpm cache
        uses: actions/cache@v4
        with:
          path: ${{ env.STORE_PATH }}
          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
          restore-keys: |
            ${{ runner.os }}-pnpm-store-

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Generate Prisma client
        run: pnpm prisma generate

      - name: Build project
        run: pnpm build
        env:
          BETTER_AUTH_URL: http://localhost:3000
          DATABASE_URL: postgresql://user:password@localhost:5432/test_db
          GOOGLE_CLIENT_ID: test_client_id
          GOOGLE_CLIENT_SECRET: test_client_secret
          RESEND_API_KEY: re_test_key
          BETTER_AUTH_SECRET: test_secret_key_32_chars_minimum
          OPENPANEL_SECRET_KEY: test_secret
          NEXT_PUBLIC_OPENPANEL_CLIENT_ID: test_client_id
          NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: test_client_id
          NEXT_PUBLIC_STRIPE_PRICE_MONTHLY_EU: test_price_monthly
          NEXT_PUBLIC_STRIPE_PRICE_YEARLY_EU: test_price_yearly
          NEXT_PUBLIC_STRIPE_PRICE_MONTHLY_US: test_price_monthly
          NEXT_PUBLIC_STRIPE_PRICE_YEARLY_US: test_price_yearly
          NEXT_PUBLIC_STRIPE_PRICE_MONTHLY_LATAM: test_price_monthly
          NEXT_PUBLIC_STRIPE_PRICE_YEARLY_LATAM: test_price_yearly
          NEXT_PUBLIC_STRIPE_PRICE_MONTHLY_BR: test_price_monthly
          NEXT_PUBLIC_STRIPE_PRICE_YEARLY_BR: test_price_yearly
          NEXT_PUBLIC_STRIPE_PRICE_MONTHLY_RU: test_price_monthly
          NEXT_PUBLIC_STRIPE_PRICE_YEARLY_RU: test_price_yearly
          NEXT_PUBLIC_STRIPE_PRICE_MONTHLY_CN: test_price_monthly
          NEXT_PUBLIC_STRIPE_PRICE_YEARLY_CN: test_price_yearly
          NEXT_PUBLIC_APP_URL: http://localhost:3000
          STRIPE_SECRET_KEY: test_secret_key
          REVENUECAT_SECRET_KEY: test_secret_key
          REVENUECAT_WEBHOOK_SECRET: test_webhook_secret
          STRIPE_WEBHOOK_SECRET: test_webhook_secret
          NEXT_PUBLIC_SHOW_ADS: false
          NEXT_PUBLIC_AD_CLIENT: test_client_id
          NEXT_PUBLIC_VERTICAL_LEFT_BANNER_AD_SLOT: 1234567890
          NEXT_PUBLIC_VERTICAL_RIGHT_BANNER_AD_SLOT: 1234567890
          NEXT_PUBLIC_EQUIPMENT_SELECTION_BANNER_AD_SLOT: 1234567890
          NEXT_PUBLIC_EXERCISE_SELECTION_BANNER_AD_SLOT: 1234567890
          NEXT_PUBLIC_MUSCLE_SELECTION_BANNER_AD_SLOT: 1234567890
          NEXT_PUBLIC_TOP_WORKOUT_SESSION_BANNER_AD_SLOT: 1234567890
          NEXT_PUBLIC_BOTTOM_WORKOUT_SESSION_BANNER_AD_SLOT: 1234567890
          NEXT_PUBLIC_TOP_STEPPER_STEP_1_BANNER_AD_SLOT: 1234567890
          NEXT_PUBLIC_TOP_STEPPER_STEP_2_BANNER_AD_SLOT: 1234567890
          NEXT_PUBLIC_TOP_STEPPER_STEP_3_BANNER_AD_SLOT: 1234567890
          NEXT_PUBLIC_TOP_PROGRAMS_BANNER_AD_SLOT: 1234567890
          NEXT_PUBLIC_BOTTOM_PROGRAMS_BANNER_AD_SLOT: 1234567890
          NEXT_PUBLIC_TOP_TOOLS_BANNER_AD_SLOT: 1234567890
          NEXT_PUBLIC_BOTTOM_TOOLS_BANNER_AD_SLOT: 1234567890
          NEXT_PUBLIC_TOP_CALCULATOR_HUB_BANNER_AD_SLOT: 1234567890
          NEXT_PUBLIC_BOTTOM_CALCULATOR_HUB_BANNER_AD_SLOT: 1234567890
          NEXT_PUBLIC_TOP_PROGRAM_DETAILS_BANNER_AD_SLOT: 1234567890
          NEXT_PUBLIC_BOTTOM_PROGRAM_DETAILS_BANNER_AD_SLOT: 1234567890
          NEXT_PUBLIC_TOP_PROFILE_BANNER_AD_SLOT: 1234567890
          NEXT_PUBLIC_IN_ARTICLE_BMI_1_AD_SLOT: 1234567890
          NEXT_PUBLIC_IN_ARTICLE_BMI_2_AD_SLOT: 1234567890
          NEXT_PUBLIC_TOP_BMI_BANNER_AD_SLOT: 1234567890
          NEXT_PUBLIC_BOTTOM_BMI_BANNER_AD_SLOT: 1234567890
          NEXT_PUBLIC_TOP_HEART_ZONES_BANNER_AD_SLOT: 1234567890
          NEXT_PUBLIC_BOTTOM_HEART_ZONES_BANNER_AD_SLOT: 1234567890
          NEXT_PUBLIC_IN_ARTICLE_HEART_ZONES_AD_SLOT_1: 1234567890
          NEXT_PUBLIC_IN_ARTICLE_HEART_ZONES_AD_SLOT_2: 1234567890
          NEXT_PUBLIC_IN_ARTICLE_HEART_ZONES_AD_SLOT_3: 1234567890
          NEXT_PUBLIC_TOP_MIFFLIN_ST_JEOR_CALCULATOR_AD_SLOT: 1234567890
          NEXT_PUBLIC_BOTTOM_CALORIE_CALCULATOR_AD_SLOT: 1234567890
          NEXT_PUBLIC_TOP_OXFORD_CALCULATOR_AD_SLOT: 1234567890
          NEXT_PUBLIC_BOTTOM_OXFORD_CALCULATOR_AD_SLOT: 1234567890
          NEXT_PUBLIC_TOP_HARRIS_BENEDICT_CALCULATOR_AD_SLOT: 1234567890
          NEXT_PUBLIC_TOP_KATCH_MCARDLE_CALCULATOR_AD_SLOT: 1234567890
          NEXT_PUBLIC_TOP_CUNNINGHAM_CALCULATOR_AD_SLOT: 1234567890
          NEXT_PUBLIC_TOP_CALORIE_CALCULATOR_COMPARISON_AD_SLOT: 1234567890
          NEXT_PUBLIC_BOTTOM_CALORIE_CALCULATOR_COMPARISON_AD_SLOT: 1234567890


================================================
FILE: .github/workflows/notify-discord-issues.yml
================================================
name: Discord Issue Notification

on:
  issues:
    types: [opened, reopened, closed]
  workflow_dispatch:
    inputs:
      issue_number:
        description: "Issue number"
        required: true
        type: string

jobs:
  Discord:
    runs-on: ubuntu-latest
    name: Discord Issue Notifier
    steps:
      - uses: actions/checkout@v4
        if: github.event_name == 'workflow_dispatch'

      - name: Get issue info for manual trigger
        id: issue-info
        if: github.event_name == 'workflow_dispatch'
        run: |
          ISSUE_INFO=$(gh issue view ${{ github.event.inputs.issue_number }} --json number,title,url,author,state,labels,createdAt)
          echo "number=$(echo "$ISSUE_INFO" | jq -r '.number')" >> $GITHUB_OUTPUT
          echo "title=$(echo "$ISSUE_INFO" | jq -r '.title')" >> $GITHUB_OUTPUT
          echo "html_url=$(echo "$ISSUE_INFO" | jq -r '.url')" >> $GITHUB_OUTPUT
          echo "author_login=$(echo "$ISSUE_INFO" | jq -r '.author.login')" >> $GITHUB_OUTPUT
          echo "author_html_url=https://github.com/$(echo "$ISSUE_INFO" | jq -r '.author.login')" >> $GITHUB_OUTPUT
          echo "state=$(echo "$ISSUE_INFO" | jq -r '.state')" >> $GITHUB_OUTPUT
          echo "created_at=$(echo "$ISSUE_INFO" | jq -r '.createdAt')" >> $GITHUB_OUTPUT
          echo "labels=$(echo "$ISSUE_INFO" | jq -r '.labels | map(.name) | join(", ") // "None"')" >> $GITHUB_OUTPUT
        env:
          GH_TOKEN: ${{ github.token }}

      - name: Determine action color and emoji
        id: action-info
        run: |
          if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
            # For manual trigger, use the current state
            case "${{ steps.issue-info.outputs.state }}" in
              "OPEN")
                echo "color=15158332" >> $GITHUB_OUTPUT  # Red
                echo "emoji=🔴" >> $GITHUB_OUTPUT
                echo "action_text=Open" >> $GITHUB_OUTPUT
                ;;
              "CLOSED")
                echo "color=5763719" >> $GITHUB_OUTPUT   # Green
                echo "emoji=🟢" >> $GITHUB_OUTPUT
                echo "action_text=Closed" >> $GITHUB_OUTPUT
                ;;
            esac
          else
            # For automatic trigger, use the action
            case "${{ github.event.action }}" in
              "opened")
                echo "color=15158332" >> $GITHUB_OUTPUT  # Red
                echo "emoji=🔴" >> $GITHUB_OUTPUT
                echo "action_text=Opened" >> $GITHUB_OUTPUT
                ;;
              "reopened")
                echo "color=16776960" >> $GITHUB_OUTPUT  # Yellow
                echo "emoji=🟡" >> $GITHUB_OUTPUT
                echo "action_text=Reopened" >> $GITHUB_OUTPUT
                ;;
              "closed")
                echo "color=5763719" >> $GITHUB_OUTPUT   # Green
                echo "emoji=🟢" >> $GITHUB_OUTPUT
                echo "action_text=Closed" >> $GITHUB_OUTPUT
                ;;
            esac
          fi

      - name: Create Discord webhook payload
        run: |
          # Determine data source based on trigger type
          if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
            ISSUE_NUMBER="${{ steps.issue-info.outputs.number }}"
            ISSUE_TITLE="${{ steps.issue-info.outputs.title }}"
            ISSUE_URL="${{ steps.issue-info.outputs.html_url }}"
            AUTHOR_LOGIN="${{ steps.issue-info.outputs.author_login }}"
            AUTHOR_URL="${{ steps.issue-info.outputs.author_html_url }}"
            ISSUE_STATE="${{ steps.issue-info.outputs.state }}"
            ISSUE_LABELS="${{ steps.issue-info.outputs.labels }}"
            CREATED_AT="${{ steps.issue-info.outputs.created_at }}"
          else
            ISSUE_NUMBER="${{ github.event.issue.number }}"
            ISSUE_TITLE="${{ github.event.issue.title }}"
            ISSUE_URL="${{ github.event.issue.html_url }}"
            AUTHOR_LOGIN="${{ github.event.issue.user.login }}"
            AUTHOR_URL="${{ github.event.issue.user.html_url }}"
            ISSUE_STATE="${{ github.event.issue.state }}"
            ISSUE_LABELS="${{ github.event.issue.labels[0].name && join(github.event.issue.labels.*.name, ', ') || 'None' }}"
            CREATED_AT="${{ github.event.issue.created_at }}"
          fi

          # Create a temporary JSON file
          cat > discord_payload.json << EOF
          {
            "avatar_url": "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png",
            "embeds": [
              {
                "title": "${{ steps.action-info.outputs.emoji }} Issue ${{ steps.action-info.outputs.action_text }}: #${ISSUE_NUMBER}",
                "description": "${ISSUE_TITLE}",
                "url": "${ISSUE_URL}",
                "color": ${{ steps.action-info.outputs.color }},
                "thumbnail": {
                  "url": "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png"
                },
                "fields": [
                  {
                    "name": "📋 Issue #",
                    "value": "\`#${ISSUE_NUMBER}\`",
                    "inline": true
                  },
                  {
                    "name": "👤 Author",
                    "value": "[${AUTHOR_LOGIN}](${AUTHOR_URL})",
                    "inline": true
                  },
                  {
                    "name": "📁 Repository",
                    "value": "[${{ github.event.repository.name }}](${{ github.event.repository.html_url }})",
                    "inline": true
                  },
                  {
                    "name": "🏷️ Labels",
                    "value": "${ISSUE_LABELS}",
                    "inline": true
                  },
                  {
                    "name": "📊 State",
                    "value": "\`${ISSUE_STATE}\`",
                    "inline": true
                  },
                  {
                    "name": "🔗 View Issue",
                    "value": "[Issue Page](${ISSUE_URL})",
                    "inline": true
                  }
                ],
                "timestamp": "${CREATED_AT}",
                "footer": {
                  "text": "Workout Cool • Issue ${{ steps.action-info.outputs.action_text }}",
                  "icon_url": "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png"
                }
              }
            ]
          }
          EOF

      - name: Send Discord notification
        run: |
          curl -H "Content-Type: application/json" \
               -d @discord_payload.json \
               "${{ secrets.DISCORD_ISSUES_WEBHOOK }}"


================================================
FILE: .github/workflows/notify-discord-pr.yml
================================================
name: Discord PR Notification

on:
  pull_request:
    types: [opened]
  workflow_dispatch:
    inputs:
      pr_number:
        description: "Pull Request number"
        required: true
        type: string

jobs:
  Discord:
    runs-on: ubuntu-latest
    name: Discord PR Notifier
    if: github.event.pull_request.head.repo.full_name == github.repository
    steps:
      - uses: actions/checkout@v4
        if: github.event_name == 'workflow_dispatch'

      - name: Get PR info for manual trigger
        id: pr-info
        if: github.event_name == 'workflow_dispatch'
        run: |
          PR_INFO=$(gh pr view ${{ github.event.inputs.pr_number }} --json number,title,url,author,state,labels,createdAt,headRefName,baseRefName,isDraft,mergeable)
          echo "number=$(echo "$PR_INFO" | jq -r '.number')" >> $GITHUB_OUTPUT
          echo "title=$(echo "$PR_INFO" | jq -r '.title')" >> $GITHUB_OUTPUT
          echo "html_url=$(echo "$PR_INFO" | jq -r '.url')" >> $GITHUB_OUTPUT
          echo "author_login=$(echo "$PR_INFO" | jq -r '.author.login')" >> $GITHUB_OUTPUT
          echo "author_html_url=https://github.com/$(echo "$PR_INFO" | jq -r '.author.login')" >> $GITHUB_OUTPUT
          echo "state=$(echo "$PR_INFO" | jq -r '.state')" >> $GITHUB_OUTPUT
          echo "created_at=$(echo "$PR_INFO" | jq -r '.createdAt')" >> $GITHUB_OUTPUT
          echo "labels=$(echo "$PR_INFO" | jq -r '.labels | map(.name) | join(", ") // "None"')" >> $GITHUB_OUTPUT
          echo "head_ref=$(echo "$PR_INFO" | jq -r '.headRefName')" >> $GITHUB_OUTPUT
          echo "base_ref=$(echo "$PR_INFO" | jq -r '.baseRefName')" >> $GITHUB_OUTPUT
          echo "is_draft=$(echo "$PR_INFO" | jq -r '.isDraft')" >> $GITHUB_OUTPUT
          echo "mergeable=$(echo "$PR_INFO" | jq -r '.mergeable // "UNKNOWN"')" >> $GITHUB_OUTPUT
        env:
          GH_TOKEN: ${{ github.token }}

      - name: Determine action color and emoji
        id: action-info
        run: |
          if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
            # For manual trigger, use the current state
            case "${{ steps.pr-info.outputs.state }}" in
              "OPEN")
                if [ "${{ steps.pr-info.outputs.is_draft }}" = "true" ]; then
                  echo "color=8421504" >> $GITHUB_OUTPUT   # Gray
                  echo "emoji=📝" >> $GITHUB_OUTPUT
                  echo "action_text=Draft" >> $GITHUB_OUTPUT
                else
                  echo "color=5763719" >> $GITHUB_OUTPUT   # Green
                  echo "emoji=🔄" >> $GITHUB_OUTPUT
                  echo "action_text=Open" >> $GITHUB_OUTPUT
                fi
                ;;
              "CLOSED")
                echo "color=15158332" >> $GITHUB_OUTPUT    # Red
                echo "emoji=❌" >> $GITHUB_OUTPUT
                echo "action_text=Closed" >> $GITHUB_OUTPUT
                ;;
              "MERGED")
                echo "color=6559689" >> $GITHUB_OUTPUT     # Purple
                echo "emoji=🎉" >> $GITHUB_OUTPUT
                echo "action_text=Merged" >> $GITHUB_OUTPUT
                ;;
            esac
          else
            # For automatic trigger, use the action
            case "${{ github.event.action }}" in
              "opened")
                echo "color=5763719" >> $GITHUB_OUTPUT     # Green
                echo "emoji=🔄" >> $GITHUB_OUTPUT
                echo "action_text=Opened" >> $GITHUB_OUTPUT
                ;;
              "reopened")
                echo "color=16776960" >> $GITHUB_OUTPUT    # Yellow
                echo "emoji=🔄" >> $GITHUB_OUTPUT
                echo "action_text=Reopened" >> $GITHUB_OUTPUT
                ;;
              "closed")
                if [ "${{ github.event.pull_request.merged }}" = "true" ]; then
                  echo "color=6559689" >> $GITHUB_OUTPUT   # Purple
                  echo "emoji=🎉" >> $GITHUB_OUTPUT
                  echo "action_text=Merged" >> $GITHUB_OUTPUT
                else
                  echo "color=15158332" >> $GITHUB_OUTPUT  # Red
                  echo "emoji=❌" >> $GITHUB_OUTPUT
                  echo "action_text=Closed" >> $GITHUB_OUTPUT
                fi
                ;;
              "ready_for_review")
                echo "color=5763719" >> $GITHUB_OUTPUT     # Green
                echo "emoji=👀" >> $GITHUB_OUTPUT
                echo "action_text=Ready for Review" >> $GITHUB_OUTPUT
                ;;
              "converted_to_draft")
                echo "color=8421504" >> $GITHUB_OUTPUT     # Gray
                echo "emoji=📝" >> $GITHUB_OUTPUT
                echo "action_text=Converted to Draft" >> $GITHUB_OUTPUT
                ;;
            esac
          fi

      - name: Create Discord webhook payload
        run: |
          # Determine data source based on trigger type
          if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
            PR_NUMBER="${{ steps.pr-info.outputs.number }}"
            PR_TITLE="${{ steps.pr-info.outputs.title }}"
            PR_URL="${{ steps.pr-info.outputs.html_url }}"
            AUTHOR_LOGIN="${{ steps.pr-info.outputs.author_login }}"
            AUTHOR_URL="${{ steps.pr-info.outputs.author_html_url }}"
            PR_STATE="${{ steps.pr-info.outputs.state }}"
            PR_LABELS="${{ steps.pr-info.outputs.labels }}"
            CREATED_AT="${{ steps.pr-info.outputs.created_at }}"
            HEAD_REF="${{ steps.pr-info.outputs.head_ref }}"
            BASE_REF="${{ steps.pr-info.outputs.base_ref }}"
            IS_DRAFT="${{ steps.pr-info.outputs.is_draft }}"
          else
            PR_NUMBER="${{ github.event.pull_request.number }}"
            PR_TITLE="${{ github.event.pull_request.title }}"
            PR_URL="${{ github.event.pull_request.html_url }}"
            AUTHOR_LOGIN="${{ github.event.pull_request.user.login }}"
            AUTHOR_URL="${{ github.event.pull_request.user.html_url }}"
            PR_STATE="${{ github.event.pull_request.state }}"
            PR_LABELS="${{ github.event.pull_request.labels[0].name && join(github.event.pull_request.labels.*.name, ', ') || 'None' }}"
            CREATED_AT="${{ github.event.pull_request.created_at }}"
            HEAD_REF="${{ github.event.pull_request.head.ref }}"
            BASE_REF="${{ github.event.pull_request.base.ref }}"
            IS_DRAFT="${{ github.event.pull_request.draft }}"
          fi

          # Escape special characters for JSON
          PR_TITLE_ESCAPED=$(echo "$PR_TITLE" | sed 's/"/\\"/g' | sed "s/'/\\'/g")

          # Create a temporary JSON file using jq to ensure valid JSON
          jq -n \
            --arg avatar_url "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" \
            --arg title "${{ steps.action-info.outputs.emoji }} Pull Request ${{ steps.action-info.outputs.action_text }}: #${PR_NUMBER}" \
            --arg description "$PR_TITLE_ESCAPED" \
            --arg url "$PR_URL" \
            --argjson color ${{ steps.action-info.outputs.color }} \
            --arg thumbnail_url "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" \
            --arg pr_number "#${PR_NUMBER}" \
            --arg author_name "$AUTHOR_LOGIN" \
            --arg author_url "$AUTHOR_URL" \
            --arg repo_name "${{ github.event.repository.name }}" \
            --arg repo_url "${{ github.event.repository.html_url }}" \
            --arg branch "${HEAD_REF} → ${BASE_REF}" \
            --arg labels "$PR_LABELS" \
            --arg status "$PR_STATE" \
            --arg pr_url "$PR_URL" \
            --arg timestamp "$CREATED_AT" \
            --arg footer_text "Workout Cool • PR ${{ steps.action-info.outputs.action_text }}" \
            --arg footer_icon "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" \
            '{
              "avatar_url": $avatar_url,
              "embeds": [
                {
                  "title": $title,
                  "description": $description,
                  "url": $url,
                  "color": $color,
                  "thumbnail": {
                    "url": $thumbnail_url
                  },
                  "fields": [
                    {
                      "name": "📋 PR #",
                      "value": $pr_number,
                      "inline": true
                    },
                    {
                      "name": "👤 Author",
                      "value": ("[\($author_name)](\($author_url))"),
                      "inline": true
                    },
                    {
                      "name": "📁 Repository",
                      "value": ("[\($repo_name)](\($repo_url))"),
                      "inline": true
                    },
                    {
                      "name": "🌿 Branch",
                      "value": $branch,
                      "inline": true
                    },
                    {
                      "name": "🏷️ Labels",
                      "value": $labels,
                      "inline": true
                    },
                    {
                      "name": "📊 Status",
                      "value": $status,
                      "inline": true
                    },
                    {
                      "name": "🔗 View PR",
                      "value": ("[Pull Request](\($pr_url))"),
                      "inline": true
                    }
                  ],
                  "timestamp": $timestamp,
                  "footer": {
                    "text": $footer_text,
                    "icon_url": $footer_icon
                  }
                }
              ]
            }' > discord_payload.json

      - name: Send Discord notification
        run: |
          curl -H "Content-Type: application/json" \
               -d @discord_payload.json \
               "${{ secrets.DISCORD_PR_WEBHOOK }}"


================================================
FILE: .github/workflows/notify-discord.yml
================================================
name: Discord Notification

on:
  release:
    types: [published]
  workflow_dispatch:
    inputs:
      tag_name:
        description: "Tag name (leave empty for latest release)"
        required: false
        type: string
      custom_title:
        description: "Custom title for the release notification"
        required: false
        type: string

jobs:
  Discord:
    runs-on: ubuntu-latest
    name: Discord Notifier
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Get release info
        id: release-info
        run: |
          if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
            # For manual trigger, get the latest release or use the input
            if [ -n "${{ github.event.inputs.tag_name }}" ]; then
              TAG_NAME="${{ github.event.inputs.tag_name }}"
            else
              TAG_NAME=$(gh release list --limit 1 --json tagName --jq '.[0].tagName')
            fi
            
            # Get release info via GitHub CLI
            RELEASE_INFO=$(gh release view "$TAG_NAME" --json name,body,url,author,publishedAt,tagName)
            
            echo "tag_name=$(echo "$RELEASE_INFO" | jq -r '.tagName')" >> $GITHUB_OUTPUT
            echo "name=$(echo "$RELEASE_INFO" | jq -r '.name')" >> $GITHUB_OUTPUT
            
            # Use EOF for the body which may contain special characters
            echo "body<<EOF" >> $GITHUB_OUTPUT
            echo "$RELEASE_INFO" | jq -r '.body' >> $GITHUB_OUTPUT
            echo "EOF" >> $GITHUB_OUTPUT
            
            echo "html_url=$(echo "$RELEASE_INFO" | jq -r '.url')" >> $GITHUB_OUTPUT
            echo "author_login=$(echo "$RELEASE_INFO" | jq -r '.author.login')" >> $GITHUB_OUTPUT
            echo "author_html_url=https://github.com/$(echo "$RELEASE_INFO" | jq -r '.author.login')" >> $GITHUB_OUTPUT
            echo "published_at=$(echo "$RELEASE_INFO" | jq -r '.publishedAt')" >> $GITHUB_OUTPUT
          else
            # For automatic trigger, use event data
            echo "tag_name=${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT
            echo "name=${{ github.event.release.name }}" >> $GITHUB_OUTPUT
            
            # Use EOF for the automatic release body as well
            echo "body<<EOF" >> $GITHUB_OUTPUT
            echo "${{ github.event.release.body }}" >> $GITHUB_OUTPUT
            echo "EOF" >> $GITHUB_OUTPUT
            
            echo "html_url=${{ github.event.release.html_url }}" >> $GITHUB_OUTPUT
            echo "author_login=${{ github.event.release.author.login }}" >> $GITHUB_OUTPUT
            echo "author_html_url=${{ github.event.release.author.html_url }}" >> $GITHUB_OUTPUT
            echo "published_at=${{ github.event.release.published_at }}" >> $GITHUB_OUTPUT
          fi
        env:
          GH_TOKEN: ${{ github.token }}

      - name: Get previous release
        id: previous-release
        run: |
          PREV_TAG=$(git tag --sort=-version:refname | grep -v '${{ steps.release-info.outputs.tag_name }}' | head -n1)
          echo "previous_tag=${PREV_TAG}" >> $GITHUB_OUTPUT

      - name: Get changed files since last release
        id: changed-files
        uses: tj-actions/changed-files@v44
        with:
          base_sha: ${{ steps.previous-release.outputs.previous_tag }}
          separator: "\n• "

      - name: Create Discord webhook payload
        run: |
          # Create a temporary JSON file
          cat > discord_payload.json << 'EOF'
          {
            "avatar_url": "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png",
            "embeds": [
              {
                "title": "🚀 New Release: ${{ steps.release-info.outputs.tag_name }}${{ github.event.inputs.custom_title && ' - ' || '' }}${{ github.event.inputs.custom_title || '' }}",
                "description": "${{ steps.release-info.outputs.name }}",
                "url": "${{ steps.release-info.outputs.html_url }}",
                "color": 5763719,
                "thumbnail": {
                  "url": "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png"
                },
                "fields": [
                  {
                    "name": "📦 Version",
                    "value": "`${{ steps.release-info.outputs.tag_name }}`",
                    "inline": true
                  },
                  {
                    "name": "👤 Released by",
                    "value": "[${{ steps.release-info.outputs.author_login }}](${{ steps.release-info.outputs.author_html_url }})",
                    "inline": true
                  },
                  {
                    "name": "📁 Repository",
                    "value": "[${{ github.event.repository.name }}](${{ github.event.repository.html_url }})",
                    "inline": true
                  },
                  {
                    "name": "🔗 Download",
                    "value": "[Release Page](${{ steps.release-info.outputs.html_url }})",
                    "inline": true
                  }
                ],
                "timestamp": "${{ steps.release-info.outputs.published_at }}",
                "footer": {
                  "text": "Workout Cool • Release",
                  "icon_url": "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png"
                }
              }
            ]
          }
          EOF

      - name: Send Discord notification
        run: |
          curl -H "Content-Type: application/json" \
               -d @discord_payload.json \
               "${{ secrets.DISCORD_RELEASE_WEBHOOK }}"

# https://stackoverflow.com/a/68068674/19395252
# https://birdie0.github.io/discord-webhooks-guide/structure/embeds.html
# https://github.com/marketplace/actions/changed-files


================================================
FILE: .github/workflows/publish-ghcr-image.yml
================================================
name: Publish Docker GHCR Image

on:
  release:
    types: [published]
  workflow_dispatch:
    inputs:
      version:
        description: "Version tag for the Docker image"
        required: true
        default: "1.2.5"
        type: string
permissions:
  contents: read
  packages: write

jobs:
  build_and_publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Log in to GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Set image tag
        id: vars
        run: |
          if [ "${{ github.event_name }}" = "release" ]; then
            # Get the release tag name
            RELEASE_TAG="${{ github.event.release.tag_name }}"
            # Remove 'v' prefix if present for Docker tag
            tag="${RELEASE_TAG#v}"
            echo "tag=$tag" >> $GITHUB_OUTPUT
          else
            echo "tag=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
          fi

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Build and push Docker image
        uses: docker/build-push-action@v6
        with:
          context: .
          file: ./Dockerfile
          push: true
          tags: |
            ghcr.io/snouzy/workout-cool:${{ steps.vars.outputs.tag }}
            ghcr.io/snouzy/workout-cool:latest
          platforms: linux/amd64,linux/arm64


================================================
FILE: .gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/_next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files (can opt-in for committing if needed)
.env
.env.local
.env.test
.env.development
.env.production

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

# editors
.idea
.zed
scripts/private/
scripts/personal/
product-development/
.claude/
.cache/


================================================
FILE: .npmrc
================================================
public-hoist-pattern[]=*import-in-the-middle*
public-hoist-pattern[]=*require-in-the-middle*

================================================
FILE: .prettierrc
================================================
{
  "plugins": ["prettier-plugin-sort-json"],
  "printWidth": 140,
  "proseWrap": "always",
  "singleQuote": false
}


================================================
FILE: .vscode/settings.json
================================================
{
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  },
  "typescript.tsdk": "node_modules/typescript/lib"
}


================================================
FILE: AGENTS.md
================================================
# AGENTS.md - Development Guide for AI Coding Agents

## Build/Test Commands

- `pnpm dev` - Start development server with Turbopack
- `pnpm build` - Build production bundle
- `pnpm lint` - Run ESLint (no test framework detected)
- `pnpm db:seed` - Seed database with sample data
- `tsx scripts/[script-name].ts` - Run TypeScript scripts directly

## Architecture & Structure

- **Next.js 15** with App Router, **Feature-Sliced Design (FSD)**
- Structure: `features/[feature]/[model|schema|ui|lib]/` for business logic
- `src/components/ui/` for reusable UI, `src/shared/` for cross-cutting concerns
- Use **Server Components** by default, `'use client'` only when needed

## Code Style & Conventions

- **TypeScript** with strict mode, functional programming patterns
- **Named exports only** (no default exports for components)
- **kebab-case** for directories, **camelCase** for variables, **PascalCase** for components
- Use `interface` over `type`, avoid `enum` (use `as const` objects)
- Double quotes (`"`) enforced, 140 char line limit, no trailing commas

## Import Organization (enforced by ESLint)

```typescript
// External libraries (alphabetical desc)
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";

// Internal modules
import { useI18n } from "locales/client";
import { Button } from "@/components/ui/button";
import { paths } from "@/shared/constants/paths";
```

## Key Patterns

- **Zod schemas** for validation in `schema/` directories
- **React Hook Form** with zodResolver for forms
- **next-safe-action** for server actions with typed errors
- **@tanstack/react-query** for client state management
- **Shadcn UI + Radix + Tailwind** for styling (mobile-first)
- Abstract external deps in `shared/lib/` (no direct fetch, Date, localStorage)


================================================
FILE: CLAUDE.md
================================================
# CLAUDE.md

## Project Overview

Workout.cool is a fitness app with two main components:

- **Website** Next.js (App Router) web client with Server Actions Location: `/Users/mathiasbradiceanu/dev/perso/workout-cool-web`

- **Mobile App** React Native app for iOS and Android Consumes the Workout.cool Next.js API Location:
  `/Users/mathiasbradiceanu/dev/perso/workout-cool-mobile`

## Architecture

### System Components

1. **Web Client (Next.js)**

   - Uses App Router and Server Actions for data mutations
   - Provides REST/JSON API endpoints consumed by the mobile app
   - TailwindCSS for styling
   - Contain the schema of the prisma database in `/Users/mathiasbradiceanu/dev/perso/workout-cool-web/prisma/schema.prisma`

2. **Mobile App (React Native / Expo)**

   - Communicates with the Next.js API for workouts
   - Push notifications and offline support for session data

3. **Both projects are using the FSD design system**.

### Data Flow

1. Mobile app and browser make API requests to the Next.js server
2. Next.js Server Actions handle form submissions, data mutations, and fetches
3. Data is stored/retrieved from the database via Next.js backend logic
4. Web client renders pages and exposes JSON endpoints
5. Mobile app syncs progress and displays workout sessions

## Key Features

- **3-Step Session Builder**: Equipment → Target Muscles → Generated Exercises
- **Embedded Videos**: Guide users through each exercise
- **In-Session Tracking**: Add sets with Reps, Weight, Time, or Bodyweight
- **Session History**: “Commit-style” log of past workouts on user profile
- **Repeat & Share**: Re-run past sessions or share summaries with others

## External Integrations

- **Database**: PostgreSQL via Next.js data layer
- **ORM**: Prisma, the schema is under `/Users/mathiasbradiceanu/dev/perso/workout-cool-web/prisma/schema.prisma`
- **Authentication**: BetterAuth (email/password, OAuth)
- **Video Hosting**: YouTube

## Linting

- ESLint and Prettier configured in both web and mobile workspaces

## Deployment

- **Website**: Vercel (Next.js)
- **Mobile App**: Expo EAS Build & Updates


================================================
FILE: CONTRIBUTING.md
================================================
### ✅ Review & Contribution Flow

- Before starting, **create an issue** for the task you want to work on.
- **Assign yourself** to the issue so it’s clear who’s working on it.
- Keep PRs focused: one issue = one PR (preferably small and scoped).
- All PRs need **at least one maintainer review**.
- We use **"Squash and merge"** to keep history clean.
- Address review comments quickly and respectfully.

---

### 🤔 Need Help?

- **General questions** → use GitHub Discussions
- **Bug reports or features** → open an Issue
- **Live chat** → [Join our Discord](https://discord.gg/NtrsUBuHUB)

---

### 📚 Useful Links

- [Feature-Sliced Design](https://feature-sliced.design/)
- [Next.js Docs](https://nextjs.org/docs)
- [Prisma Docs](https://www.prisma.io/docs/)

---

### 🌟 Recognition

We credit contributors in:

- the GitHub contributors list
- release notes (for impactful work)
- internal documentation if relevant

Thanks again for contributing to Workout Cool! 💪

Questions? Just open an issue or ping a maintainer.


================================================
FILE: Dockerfile
================================================
FROM node:20-alpine AS base

WORKDIR /app
RUN npm install -g pnpm

# Install dependencies
FROM base AS deps
COPY package.json pnpm-lock.yaml ./
COPY prisma ./prisma
RUN pnpm install --frozen-lockfile

# Build the app
FROM base AS builder
COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/prisma ./prisma
COPY . .
COPY .env.example .env

RUN pnpm run build

# Production image, copy only necessary files
FROM base AS runner
WORKDIR /app

COPY --from=builder /app/public ./public
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/data ./data

COPY scripts /app/scripts
RUN chmod +x /app/scripts/setup.sh

ENTRYPOINT ["/app/scripts/setup.sh"]

EXPOSE 3000
ENV PORT=3000

CMD ["pnpm", "start"]

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2023 Mathias Bradiceanu

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: Makefile
================================================
.PHONY: dev setup up down help

help:
	@echo "🚀 Workout Cool Development Commands"
	@echo ""
	@echo "  dev     - Start development server (automatically sets up everything)"
	@echo "  setup   - One-time setup: database, schema, and sample data"
	@echo "  db      - Start PostgreSQL database only"
	@echo "  down    - Stop all services"
	@echo ""


db:
	@echo "🐘 Starting PostgreSQL database..."
	docker compose up -d postgres


setup: db
	@echo "📦 Installing dependencies..."
	pnpm install --frozen-lockfile
	@echo "🔄 Applying database migrations..."
	npx prisma migrate deploy
	npx prisma generate
	@echo "🌱 Seeding database with sample data..."
	pnpm run import:exercises-full ./data/sample-exercises.csv
	pnpm run db:seed-leaderboard
	@echo "✅ Setup complete!"


dev: setup
	@echo "🚀 Starting Next.js development server..."
	pnpm dev

down:
	@echo "🛑 Stopping all services..."
	docker compose down


================================================
FILE: README.md
================================================
<div align="center">
<img src="public/logo.png" alt="Workout.cool Logo" width="120" height="120">
<h1>Workout.cool</h1>
<h3><em>Modern fitness coaching platform with comprehensive exercise database</em></h3>
<p>
<a href="https://github.com/Snouzy/workout-cool/graphs/contributors"><img src="https://img.shields.io/github/contributors/Snouzy/workout-cool?style=plastic" alt="Contributors">
<a href="https://github.com/Snouzy/workout-cool/network/members">  
<img src="https://img.shields.io/github/forks/Snouzy/workout-cool" alt="Forks">
<a href="https://github.com/Snouzy/workout-cool/stargazers">
<img src="https://img.shields.io/github/stars/Snouzy/workout-cool" alt="Stars">
<a href="https://github.com/Snouzy/workout-cool/issues">  
<img src="https://img.shields.io/github/issues/Snouzy/workout-cool" alt="Issues">
<img src="https://img.shields.io/github/repo-size/Snouzy/workout-cool" alt="Repository Size">
<a href="LICENSE">
  <img src="https://img.shields.io/badge/License-MIT-green.svg" alt="MIT License">
</a>

<p>
    <a href="https://discord.gg/NtrsUBuHUB">
      <img src="https://img.shields.io/badge/Discord-Join%20Community-5865F2?style=for-the-badge&logo=discord&logoColor=white" alt="Discord">
    </a>
    <a href="https://ko-fi.com/workoutcool">
      <img src="https://img.shields.io/badge/Ko--fi-Support%20Project-FF5E5B?style=for-the-badge&logo=ko-fi&logoColor=white" alt="Ko-fi">
    </a>
  </p>
  <!-- Keep these links. Translations will automatically update with the README. -->
  <a href="https://readme-i18n.com/Snouzy/workout-cool?lang=de">Deutsch</a> |
  <a href="https://readme-i18n.com/Snouzy/workout-cool?lang=es">Español</a> |
  <a href="https://readme-i18n.com/Snouzy/workout-cool?lang=fr">français</a> |
  <a href="https://readme-i18n.com/Snouzy/workout-cool?lang=ja">日本語</a> |
  <a href="https://readme-i18n.com/Snouzy/workout-cool?lang=ko">한국어</a> |
  <a href="https://readme-i18n.com/Snouzy/workout-cool?lang=pt">Português</a> |
  <a href="https://readme-i18n.com/Snouzy/workout-cool?lang=ru">Русский</a> |
  <a href="https://readme-i18n.com/Snouzy/workout-cool?lang=zh">中文</a>
</p>
</div>

## Table of Contents

- [About](#about)
- [Project Origin & Motivation](#-project-origin--motivation)
- [Quick Start](#quick-start)
- [Exercise Database Import](#exercise-database-import)
- [Project Architecture](#project-architecture)
- [Contributing](#contributing)
- [Self-hosting](#deployment--self-hosting)
- [Resources](#resources)
- [License](#license)
- [Sponsor This Project](#-sponsor-this-project)

## Contributors

<a href="https://github.com/Snouzy/workout-cool/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=Snouzy/workout-cool&nocache=1" />
</a>

## Sponsors

<div>
  <h4>They are helping making workout.cool free and open-source for everyone :</h4>

<a href="https://vercel.com/oss">
  <img alt="Vercel OSS Program" src="https://vercel.com/oss/program-badge.svg" />
</a>
<br/>
<br/>

  <table>
    <tr>
      <td align="center">
        <a href="https://github.com/lj020326">
          <img src="https://github.com/lj020326.png" width="50px;" alt="lj020326"/>
          <br />
          <sub><b>lj020326</b></sub>
          <br />
        </a>
      </td>
      <td align="center">
        <a href="https://github.com/lucasnevespereira">
          <img src="https://github.com/lucasnevespereira.png" width="50px;" alt="lucasnevespereira"/>
          <br />
          <sub><b>lucasnevespereira</b></sub>
          <br />
        </a>
      </td>
    </tr>
  </table>

</div>

## About

A comprehensive fitness coaching platform that allows create workout plans for you, track progress, and access a vast exercise database with
detailed instructions and video demonstrations.

## 🎯 Project Origin & Motivation

This project was born from a personal mission to revive and improve upon a previous fitness platform. As the **primary contributor** to the
original [workout.lol](https://github.com/workout-lol/workout-lol) project, I witnessed its journey and abandonment. 🥹

### The Story Behind **_workout.cool_**

- 🏗️ **Original Contributor**: I was the main contributor to workout.lol
- 💼 **Business Challenges**: The original project faced major hurdles with exercise video partnerships (no reliable video provider) could
  be established
- 💰 **Project Sale**: Due to these partnership issues, the project was sold to another party
- 📉 **Abandonment**: The new owner quickly realized that **exercise video licensing costs were prohibitively expensive**, began to be sick
  and abandoned the entire project
- 🔄 **Revival Attempts**: For the past **9 months**, I've been trying to reconnect with the new stakeholder
- 📧 **Radio Silence**: Despite multiple (15) attempts, there has been no response
- 🚀 **New Beginning**: Rather than let this valuable work disappear, I decided to create a fresh, modern implementation

### Why **_workout.cool_** Exists

**Someone had to step up.**

The opensource fitness community deserves better than broken promises and abandoned platforms.

I'm not building this for profit.

This isn't just a revival : it's an evolution. **workout.cool** represents everything the original project could have been, with the
reliability, modern approach, and **maintenance** that the fitness open source community deserves.

## 👥 From the Community, For the Community

**I'm not just a developer : I'm a user who refused to let our community down.**

I experienced firsthand the frustration of watching a beloved tool slowly disappear. Like many of you, I had workouts saved, progress
tracked, and a routine built around the platform.

### My Mission: Rescue & Revive.

_If you were part of the original workout.lol community, welcome back! If you're new here, welcome to the future of fitness platform
management._

## Quick Start

### Prerequisites

- [Node.js](https://nodejs.org/) (v18+)
- [pnpm](https://pnpm.io/) (v8+)
- [Docker](https://www.docker.com/)

### Installation

1. **Clone the repository**

   ```bash
   git clone https://github.com/Snouzy/workout-cool.git
   cd workout-cool
   ```

2. **Choose your installation method:**

<details>
<summary><b>🐳 With Docker</b></summary>

### Docker Installation

1. **Copy environment variables**

   ```bash
   cp .env.example .env
   ```

2. **Start everything for development:**

   ```sh
   make dev
   ```

   - This will start the database in Docker, run migrations, seed the DB, and start the Next.js dev server.
   - To stop services run `make down`

3. **Open your browser** Navigate to [http://localhost:3000](http://localhost:3000)

</details>

<details>
<summary><b>💻 Without Docker</b></summary>

### Manual Installation

1. **Install dependencies**

   ```bash
   pnpm install
   ```

2. **Copy environment variables**

   ```bash
   cp .env.example .env
   ```

3. **Set up PostgreSQL database**

   - If you don't already have it, install PostgreSQL locally
   - Create a database named `workout_cool` : `createdb -h localhost -p 5432 -U postgres workout_cool`

4. **Run database migrations**

   ```bash
   npx prisma migrate dev
   ```

5. **Seed the database (optional)**

See the - [Exercise database import section](#exercise-database-import)

6. **Start the development server**

   ```bash
   pnpm dev
   ```

7. **Open your browser** Navigate to [http://localhost:3000](http://localhost:3000)

</details>

## Exercise Database Import

The project includes a comprehensive exercise database. To import a sample of exercises:

### Prerequisites for Import

1. **Prepare your CSV file**

Your CSV should have these columns:

```
id,name,name_en,description,description_en,full_video_url,full_video_image_url,introduction,introduction_en,slug,slug_en,attribute_name,attribute_value
```

You can use the provided example.

### Import Commands

```bash
# Import exercises from a CSV file
pnpm run import:exercises-full /path/to/your/exercises.csv

# Example with the provided sample data
pnpm run import:exercises-full ./data/sample-exercises.csv
```

### CSV Format Example

```csv
id,name,name_en,description,description_en,full_video_url,full_video_image_url,introduction,introduction_en,slug,slug_en,attribute_name,attribute_value
157,"Fentes arrières à la barre","Barbell Reverse Lunges","<p>Stand upright...</p>","<p>Stand upright...</p>",https://youtube.com/...,https://img.youtube.com/...,slug-fr,slug-en,TYPE,STRENGTH
157,"Fentes arrières à la barre","Barbell Reverse Lunges","<p>Stand upright...</p>","<p>Stand upright...</p>",https://youtube.com/...,https://img.youtube.com/...,slug-fr,slug-en,PRIMARY_MUSCLE,QUADRICEPS
```

Want unlimited exercise for local development ?

Just ask chatGPT with the prompt from `./scripts/import-exercises-with-attributes.prompt.md`

## Project Architecture

This project follows **Feature-Sliced Design (FSD)** principles with Next.js App Router:

```
src/
├── app/ # Next.js pages, routes and layouts
├── processes/ # Business flows (multi-feature)
├── widgets/ # Composable UI with logic (Sidebar, Header)
├── features/ # Business units (auth, exercise-management)
├── entities/ # Domain entities (user, exercise, workout)
├── shared/ # Shared code (UI, lib, config, types)
└── styles/ # Global CSS, themes
```

### Architecture Principles

- **Feature-driven**: Each feature is independent and reusable
- **Clear domain isolation**: `shared` → `entities` → `features` → `widgets` → `app`
- **Consistency**: Between business logic, UI, and data layers

### Example Feature Structure

```
features/
└── exercise-management/
├── ui/ # UI components (ExerciseForm, ExerciseCard)
├── model/ # Hooks, state management (useExercises)
├── lib/ # Utilities (exercise-helpers)
└── api/ # Server actions or API calls
```

## Contributing

We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.

### Development Workflow

1. **Create an issue** for the feature/bug you want to work on. Say that you will work on it (or no)
2. Fork the repository
3. Create your feature|fix|chore|refactor branch (`git checkout -b feature/amazing-feature`)
4. Make your changes following our [code standards](#code-style)
5. Commit your changes (`git commit -m 'feat: add amazing feature'`)
6. Push to the branch (`git push origin feature/amazing-feature`)
7. Open a Pull Request (one issue = one PR)

**📋 For complete contribution guidelines, see our [Contributing Guide](CONTRIBUTING.md)**

### Code Style

- Follow TypeScript best practices
- Use Feature-Sliced Design architecture
- Write meaningful commit messages

## Deployment / Self-hosting

> 📖 **For detailed self-hosting instructions, see our [Complete Self-hosting Guide](docs/SELF-HOSTING.md)**
>
> 📺 **You can also watch a [3-minute video guide on self-hosting Workout.Cool](https://www.youtube.com/watch?v=HQecjb0CfAo).**


To seed the database with the sample exercises, set the `SEED_SAMPLE_DATA` env variable to `true`.

### Using Docker

```bash
# Build the Docker image
docker build -t yourusername/workout-cool .

# Run the container
docker run -p 3000:3000 --env-file .env.production yourusername/workout-cool
```

### Using Docker Compose

#### DATABASE_URL

Update the `host` to point to the `postgres` service instead of `localhost`
`DATABASE_URL=postgresql://username:password@postgres:5432/workout_cool`

```bash
docker compose up -d
```

### Manual Deployment

```bash
# Build the application
pnpm build

# Run database migrations
export DATABASE_URL="your-production-db-url"
npx prisma migrate deploy

# Start the production server
pnpm start
```

## Resources

- [Feature-Sliced Design](https://feature-sliced.design/)
- [Next.js Documentation](https://nextjs.org/docs)
- [Prisma Documentation](https://www.prisma.io/docs/)
- [Better Auth](https://github.com/better-auth/better-auth)

## License

This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.

[![MIT License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)

## 🤝 Join the Rescue Mission

**This is about rebuilding what we lost, together.**

### How You Can Help

- 🌟 **Star this repo** to show the world our community is alive and thriving
- 💬 **Join our Discord** to connect with other fitness enthusiasts and developers
- 🐛 **Report issues** you find. I'm listening to every single one
- 💡 **Share your feature requests** finally, someone who will actually implement them !
- 🔄 **Spread the word** to fellow fitness enthusiasts who lost hope
- 🤝 **Contribute code** if you're a developer : let's build this together

<div align="center">
  <a href="https://discord.gg/NtrsUBuHUB" target="_blank">
    <img src="https://img.shields.io/badge/Discord-Join%20Our%20Community-5865F2?style=for-the-badge&logo=discord&logoColor=white" alt="Discord">
  </a>
  <br><br>
  <a href="https://www.producthunt.com/products/workout-cool?embed=true&utm_source=badge-featured&utm_medium=badge&utm_source=badge-workout&#0045;cool" target="_blank">
    <img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=980519&theme=light&t=1750436372984" alt="Product Hunt" width="180">
  </a>
</div>

## 💖 Sponsor This Project

Appear in the README and on the website as supporter by donating:

<div align="center">
  <a href="https://ko-fi.com/workoutcool" target="_blank">
    <img src="https://ko-fi.com/img/githubbutton_sm.svg" alt="Sponsor on Ko-fi" />
  </a>
  &nbsp;&nbsp;&nbsp;
  <!-- TODO: setup -->
  <!-- <a href="https://buymeacoffee.com/workout_cool" target="_blank">
    <img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="41" width="174" />
  </a> -->
</div>

<p align="center" style="margin-top:20px;">
  <em>If you believe in open-source fitness tools and want to help this project thrive,<br>
  consider buying me a coffee ☕ or sponsoring the continued development.</em>
</p>

<p align="center">
  Your support helps cover hosting costs, exercise database updates, and continuous improvement.<br>
  Thank you for keeping <strong>workout.cool</strong> alive and evolving 💪
</p>

<br />
<br />
<a href="https://vercel.com/oss">
<img alt="Vercel OSS Program" src="https://vercel.com/oss/program-badge.svg" />
</a>


================================================
FILE: app/[locale]/(admin)/admin/[...catchAll]/not-found.tsx
================================================
import { Page404 } from "@/widgets/404";

export default function NotFoundPage() {
  return <Page404 />;
}


================================================
FILE: app/[locale]/(admin)/admin/[...catchAll]/page.tsx
================================================
import { Page404 } from "@/widgets/404";

export default function AdminCatchAll() {
  return <Page404 />;
}


================================================
FILE: app/[locale]/(admin)/admin/dashboard/page.tsx
================================================
import { Suspense } from "react";
import Image from "next/image";
import { Users, Target } from "lucide-react";

import { prisma } from "@/shared/lib/prisma";
import { Skeleton } from "@/components/ui/skeleton";

async function getDashboardStats() {
  const [totalUsers, totalWorkoutSessions, totalExercises, activeSubscriptions, recentUsers, recentWorkouts, totalPrograms] =
    await Promise.all([
      // Total users
      prisma.user.count(),

      // Total workout sessions
      prisma.workoutSession.count(),

      // Total exercises
      prisma.exercise.count(),

      // Active subscriptions
      prisma.subscription.count({
        where: {
          status: "ACTIVE",
        },
      }),

      // Users created in last 7 days
      prisma.user.count({
        where: {
          createdAt: {
            gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
          },
        },
      }),

      // Workout sessions in last 7 days
      prisma.workoutSession.count({
        where: {
          startedAt: {
            gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
          },
        },
      }),

      // Total programs
      prisma.program.count(),
    ]);

  return {
    totalUsers,
    totalWorkoutSessions,
    totalExercises,
    activeSubscriptions,
    recentUsers,
    recentWorkouts,
    totalPrograms,
  };
}

async function DashboardStats() {
  const stats = await getDashboardStats();

  return (
    <div className="grid gap-4 md:gap-6">
      <div className="grid grid-cols-2 gap-4 md:grid-cols-4 md:gap-6">
        <div className="group col-span-3">
          <div className="relative overflow-hidden rounded-2xl border border-gray-200 bg-gradient-to-br from-blue-50 to-blue-100 p-5 transition-all duration-200 ease-in-out hover:scale-[1.02] hover:border-blue-300 dark:border-gray-700 dark:from-blue-950/50 dark:to-blue-900/30 dark:hover:border-blue-600">
            <div className="flex items-start justify-between">
              <div>
                <div className="mb-2 flex items-center space-x-2">
                  <div className="rounded-xl bg-blue-500 p-2">
                    <Users className="h-5 w-5 text-white" />
                  </div>
                  <span className="text-xs font-medium uppercase tracking-wider text-blue-600 dark:text-blue-400">Communauté</span>
                </div>
                <h3 className="text-3xl font-bold text-gray-900 dark:text-white">{stats.totalUsers.toLocaleString()}</h3>
                <p className="text-sm text-gray-600 dark:text-gray-300">
                  <p className="text-xs text-gray-600 dark:text-gray-300">Utilisateurs</p>
                  <span className="font-semibold text-green-600 dark:text-green-400">+{stats.recentUsers}</span> cette semaine
                </p>
              </div>
              <div className="transition-transform duration-200 group-hover:rotate-6">
                <Image alt="Happy mascot" className="h-12 w-12" height={48} src="/images/emojis/WorkoutCoolHappy.png" width={48} />
              </div>
            </div>
          </div>
        </div>

        {/* Workout Sessions Card */}
        <div className="group col-span-2 md:col-span-1">
          <div className="relative overflow-hidden rounded-2xl border border-gray-200 bg-gradient-to-br from-green-50 to-emerald-100 p-4 transition-all duration-200 ease-in-out hover:scale-[1.02] hover:border-green-300 dark:border-gray-700 dark:from-green-950/50 dark:to-emerald-900/30 dark:hover:border-green-600">
            <div className="mb-3 flex items-center justify-between">
              <div className="rounded-xl bg-green-500 p-2">
                <Image
                  alt="Swag mascot"
                  className="h-8 w-8 transition-transform duration-200 group-hover:scale-110"
                  height={32}
                  src="/images/emojis/WorkoutCoolSwag.png"
                  width={32}
                />
              </div>
            </div>
            <h3 className="text-2xl font-bold text-gray-900 dark:text-white">{stats.totalWorkoutSessions.toLocaleString()}</h3>
            <p className="text-xs text-gray-600 dark:text-gray-300">Sessions</p>
            <p className="text-xs text-green-600 dark:text-green-400">+{stats.recentWorkouts} cette semaine</p>
          </div>
        </div>
      </div>

      {/* Row 2 */}
      <div className="grid grid-cols-1 gap-4 md:grid-cols-3 md:gap-6">
        {/* Programs Card */}
        <div className="group">
          <div className="relative overflow-hidden rounded-2xl border border-gray-200 bg-gradient-to-br from-amber-50 to-yellow-100 p-4 transition-all duration-200 ease-in-out hover:scale-[1.02] hover:border-amber-300 dark:border-gray-700 dark:from-amber-950/50 dark:to-yellow-900/30 dark:hover:border-amber-600">
            <div className="mb-3 flex items-center justify-between">
              <div className="rounded-xl bg-amber-500 p-2">
                <Image
                  alt="Wooow mascot"
                  className="h-8 w-8 transition-transform duration-200 group-hover:scale-110"
                  height={32}
                  src="/images/emojis/WorkoutCoolWooow.png"
                  width={32}
                />
              </div>
            </div>
            <h3 className="text-xl font-bold text-gray-900 dark:text-white">{stats.totalPrograms.toLocaleString()}</h3>
            <p className="text-xs text-gray-600 dark:text-gray-300">Programmes</p>
          </div>
        </div>

        <div className="group">
          <div className="relative overflow-hidden rounded-2xl border border-gray-200 bg-gradient-to-br from-purple-50 to-violet-100 p-4 transition-all duration-200 ease-in-out hover:scale-[1.02] hover:border-purple-300 dark:border-gray-700 dark:from-purple-950/50 dark:to-violet-900/30 dark:hover:border-purple-600">
            <div className="mb-3 flex items-center justify-between">
              <div className="rounded-xl bg-purple-500 p-2">
                <Image
                  alt="Love mascot"
                  className="h-8 w-8 transition-transform duration-200 group-hover:scale-110"
                  height={32}
                  src="/images/emojis/WorkoutCoolBiceps.png"
                  width={32}
                />
              </div>
            </div>
            <h3 className="text-2xl font-bold text-gray-900 dark:text-white">{stats.totalExercises.toLocaleString()}</h3>
            <p className="text-xs text-gray-600 dark:text-gray-300">Exercices</p>
          </div>
        </div>

        {/* Growth Card */}
        <div className="group">
          <div className="relative overflow-hidden rounded-2xl border border-gray-200 bg-gradient-to-br from-cyan-50 to-blue-100 p-4 transition-all duration-200 ease-in-out hover:scale-[1.02] hover:border-cyan-300 dark:border-gray-700 dark:from-cyan-950/50 dark:to-blue-900/30 dark:hover:border-cyan-600">
            <div className="mb-3 flex items-center justify-between">
              <div className="rounded-xl bg-cyan-500 p-2">
                <Image
                  alt="Teeth mascot"
                  className="h-8 w-8 transition-transform duration-200 group-hover:scale-110"
                  height={32}
                  src="/images/emojis/WorkoutCoolTeeths.png"
                  width={32}
                />
              </div>
            </div>
            <h3 className="text-xl font-bold text-gray-900 dark:text-white">{stats.activeSubscriptions}</h3>
            <p className="text-xs text-gray-600 dark:text-gray-300">Abonnés</p>
          </div>
        </div>
      </div>
    </div>
  );
}

function DashboardStatsLoading() {
  return (
    <div className="grid gap-4 md:gap-6">
      <div className="grid grid-cols-2 gap-4 md:grid-cols-4 md:gap-6">
        <div className="col-span-2 md:col-span-2">
          <div className="rounded-2xl border border-gray-200 p-6 dark:border-gray-700">
            <Skeleton className="mb-4 h-6 w-24" />
            <Skeleton className="mb-2 h-8 w-20" />
            <Skeleton className="h-4 w-32" />
          </div>
        </div>
        <div className="rounded-2xl border border-gray-200 p-4 dark:border-gray-700">
          <Skeleton className="mb-3 h-6 w-full" />
          <Skeleton className="mb-2 h-6 w-16" />
          <Skeleton className="h-3 w-20" />
        </div>
        <div className="rounded-2xl border border-gray-200 p-4 dark:border-gray-700">
          <Skeleton className="mb-3 h-6 w-full" />
          <Skeleton className="mb-2 h-6 w-16" />
          <Skeleton className="h-3 w-20" />
        </div>
      </div>
      <div className="grid grid-cols-1 gap-4 md:grid-cols-3 md:gap-6">
        {Array.from({ length: 3 }).map((_, i) => (
          <div className="rounded-2xl border border-gray-200 p-4 dark:border-gray-700" key={i}>
            <Skeleton className="mb-3 h-6 w-full" />
            <Skeleton className="mb-2 h-6 w-12" />
            <Skeleton className="h-3 w-16" />
          </div>
        ))}
      </div>
    </div>
  );
}

export default function AdminDashboard() {
  return (
    <div className="space-y-6">
      <div className="flex items-center space-x-4">
        <div className="rounded-2xl bg-gradient-to-r from-blue-500 to-purple-600 p-3">
          <Target className="h-6 w-6 text-white" />
        </div>
        <div>
          <h1 className="text-3xl font-bold tracking-tight text-gray-900 dark:text-white">Dashboard Admin</h1>
          <p className="text-gray-600 dark:text-gray-300">WorkoutCool Admin</p>
        </div>
      </div>

      <Suspense fallback={<DashboardStatsLoading />}>
        <DashboardStats />
      </Suspense>
    </div>
  );
}


================================================
FILE: app/[locale]/(admin)/admin/layout.tsx
================================================
import { ReactElement } from "react";
import { redirect } from "next/navigation";
import { UserRole } from "@prisma/client";

import { AdminSidebar } from "@/features/admin/layout/admin-sidebar/ui/admin-sidebar";
import { AdminHeader } from "@/features/admin/layout/admin-sidebar/ui/admin-header";
import { serverRequiredUser } from "@/entities/user/model/get-server-session-user";

interface AdminLayoutProps {
  params: Promise<{ locale: string }>;
  children: ReactElement;
}

export default async function AdminLayout({ children }: AdminLayoutProps) {
  const user = await serverRequiredUser();

  if (user.role !== UserRole.admin) {
    redirect("/");
  }

  return (
    <div className="flex bg-gray-50 dark:bg-gray-900">
      {/* Sidebar */}
      <AdminSidebar />

      {/* Main content */}
      <div className="flex flex-1 flex-col overflow-hidden">
        {/* Header */}
        <AdminHeader user={user} />

        {/* Page content */}
        <main className="flex-1 overflow-y-auto bg-white p-6 dark:bg-gray-800">
          <div className="mx-auto max-w-7xl w-full">{children}</div>
        </main>
      </div>
    </div>
  );
}


================================================
FILE: app/[locale]/(admin)/admin/not-found.tsx
================================================
import { Page404 } from "@/widgets/404";

export default function NotFoundPage() {
  return <Page404 />;
}


================================================
FILE: app/[locale]/(admin)/admin/programs/[id]/edit/page.tsx
================================================
import { notFound } from "next/navigation";

import { ProgramBuilder } from "@/features/admin/programs/ui/program-builder";
import { getProgramById } from "@/features/admin/programs/actions/get-programs.action";

interface ProgramEditPageProps {
  params: Promise<{ id: string }>;
}

export default async function ProgramEditPage({ params }: ProgramEditPageProps) {
  const { id } = await params;

  const program = await getProgramById(id);

  if (!program) {
    notFound();
  }

  return <ProgramBuilder program={program} />;
}


================================================
FILE: app/[locale]/(admin)/admin/programs/page.tsx
================================================
import { Suspense } from "react";

import { ProgramsList } from "@/features/admin/programs/ui/programs-list";
import { CreateProgramButton } from "@/features/admin/programs/ui/create-program-button";

export default function AdminPrograms() {
  return (
    <div className="space-y-6">
      <div className="flex items-center justify-between">
        <div>
          <h1 className="text-3xl font-bold tracking-tight">Programs</h1>
          <p className="text-muted-foreground">Create, edit, view and delete programs.</p>
        </div>
        <CreateProgramButton />
      </div>

      <Suspense fallback={<div>Loading programs...</div>}>
        <ProgramsList />
      </Suspense>
    </div>
  );
}


================================================
FILE: app/[locale]/(admin)/admin/settings/page.tsx
================================================
export default function AdminSettings() {
  return (
    <div className="space-y-6">
      <div>
        <h1 className="text-3xl font-bold tracking-tight">Settings</h1>
        <p className="text-muted-foreground">Configuration and administration of the system.</p>
      </div>
    </div>
  );
}


================================================
FILE: app/[locale]/(admin)/admin/users/page.tsx
================================================
import { redirect } from "next/navigation";
import { UserRole } from "@prisma/client";

import { UsersTable } from "@/features/admin/users/list/ui/users-table";
import { getUsersAction } from "@/entities/user/model/get-users.actions";
import { serverRequiredUser } from "@/entities/user/model/get-server-session-user";

export default async function AdminUsersPage() {
  try {
    const user = await serverRequiredUser();

    if (user.role !== UserRole.admin) {
      redirect("/");
    }

    // Call the action with proper error handling
    const result = await getUsersAction({
      page: 1,
      limit: 10,
    });

    // Check if the action was successful
    if (!result || !result.data) {
      throw new Error("Impossible de charger les utilisateurs");
    }

    return (
      <div className="space-y-6">
        <div>
          <h1 className="text-3xl font-bold tracking-tight">Utilisateurs</h1>
          <p className="text-muted-foreground">Gestion et administration des comptes utilisateurs</p>
        </div>
        <UsersTable initialUsers={result} />
      </div>
    );
  } catch (error) {
    console.error("Error in admin users page:", error);

    return (
      <div className="space-y-6">
        <div>
          <h1 className="text-3xl font-bold tracking-tight">Utilisateurs</h1>
          <p className="text-muted-foreground">Gestion et administration des comptes utilisateurs</p>
        </div>
        <div className="rounded-lg border border-red-200 bg-red-50 p-6 dark:border-red-800 dark:bg-red-950">
          <h2 className="text-lg font-semibold text-red-800 dark:text-red-200">Erreur de chargement</h2>
          <p className="mt-2 text-sm text-red-700 dark:text-red-300">
            Impossible de charger la liste des utilisateurs. Veuillez réessayer plus tard.
          </p>
          <p className="mt-1 text-xs text-red-600 dark:text-red-400">{error instanceof Error ? error.message : "Erreur inconnue"}</p>
        </div>
      </div>
    );
  }
}


================================================
FILE: app/[locale]/(app)/(legal-and-payment)/layout.tsx
================================================
import { LayoutParams } from "@/shared/types/next";

type LocaleParams = Record<string, string> & {
  locale: string;
};

export default function RouteLayout({ children, params: _ }: LayoutParams<LocaleParams>) {
  return (
    <div className="bg-muted/30 text-foreground flex min-h-screen flex-col">
      {/* Fixe l'espace sous le header flottant */}
      <div className="h-16" />

      {/* Contenu principal centré avec marge */}
      <main className="flex-1 px-4 py-12">
        <div className="mx-auto w-full max-w-4xl">{children}</div>
      </main>
    </div>
  );
}


================================================
FILE: app/[locale]/(app)/(legal-and-payment)/legal/privacy/page.tsx
================================================
import { getLocalizedMdx } from "@/shared/lib/mdx/load-mdx";
import { Typography } from "@/components/ui/typography";

type PageProps = {
  params: Promise<{ locale: string }>;
};

export default async function PrivacyPolicyPage({ params }: PageProps) {
  const { locale } = await params;
  const content = await getLocalizedMdx("privacy-policy", locale);

  return (
    <div className="bg-muted/50 py-12">
      <div className="container mx-auto max-w-4xl px-4">
        <header className="mb-10 text-center">
          <Typography className="mb-2 text-3xl md:text-4xl" variant="h1">
            {locale === "fr" ? "Politique de Confidentialité" : "Privacy Policy"}
          </Typography>
          <p className="text-muted-foreground text-base md:text-lg">
            {locale === "fr"
              ? "Voici comment nous traitons vos données personnelles."
              : "How we handle your personal data at Workout Cool."}
          </p>
        </header>

        <div className="prose prose-neutral max-w-none dark:prose-invert">{content}</div>
      </div>
    </div>
  );
}


================================================
FILE: app/[locale]/(app)/(legal-and-payment)/legal/sales-terms/page.tsx
================================================
import { getLocalizedMdx } from "@/shared/lib/mdx/load-mdx";
import { Layout, LayoutContent } from "@/features/page/layout";
import { Typography } from "@/components/ui/typography";

type PageProps = {
  params: Promise<{ locale: string }>;
};

export default async function SalesTermsPage({ params }: PageProps) {
  const { locale } = await params;
  const content = await getLocalizedMdx("sales-terms", locale);

  return (
    <div className="bg-muted/50 py-12">
      <div className="container mx-auto max-w-4xl px-4">
        <header className="mb-10 text-center">
          <Typography className="mb-2 text-3xl md:text-4xl" variant="h1">
            {locale === "fr" ? "Conditions Générales de Vente" : "General Terms of Sale"}
          </Typography>
          <p className="text-muted-foreground text-base md:text-lg">
            {locale === "fr"
              ? "Les conditions qui régissent l’achat d’un abonnement Workout Cool."
              : "The terms governing the purchase of a Workout Cool subscription."}
          </p>
        </header>

        <Layout>
          <LayoutContent className="prose prose-neutral max-w-none dark:prose-invert">{content}</LayoutContent>
        </Layout>
      </div>
    </div>
  );
}


================================================
FILE: app/[locale]/(app)/(legal-and-payment)/legal/terms/page.tsx
================================================
import { getLocalizedMdx } from "@/shared/lib/mdx/load-mdx";
import { Layout, LayoutContent } from "@/features/page/layout";
import { Typography } from "@/components/ui/typography";

type PageProps = {
  params: Promise<{ locale: string }>;
};

export default async function TermsPage({ params }: PageProps) {
  const { locale } = await params;
  const content = await getLocalizedMdx("terms", locale);

  return (
    <div className="bg-muted/50 py-12">
      <div className="container mx-auto max-w-4xl px-4">
        <header className="mb-10 text-center">
          <Typography className="mb-2 text-3xl md:text-4xl" variant="h1">
            {locale === "fr" ? "Conditions Générales d’Utilisation" : "Terms of Use"}
          </Typography>
          <p className="text-muted-foreground text-base md:text-lg">
            {locale === "fr"
              ? "Merci de lire attentivement ces conditions avant d’utiliser nos services."
              : "Please read these terms carefully before using our services."}
          </p>
        </header>

        <Layout>
          <LayoutContent className="prose prose-neutral max-w-none dark:prose-invert">{content}</LayoutContent>
        </Layout>
      </div>
    </div>
  );
}


================================================
FILE: app/[locale]/(app)/[slug]/layout.tsx
================================================
import { ReactElement } from "react";

interface RootLayoutProps {
    params: Promise<{ locale: string }>;
    children: ReactElement;
  }

export default async function RootLayout({ children }: RootLayoutProps) {

    return (
        <div>
            {children}
        </div>
    )
}

================================================
FILE: app/[locale]/(app)/about/page.tsx
================================================
import { getLocalizedMdx } from "@/shared/lib/mdx/load-mdx";

type PageProps = {
  params: Promise<{ locale: string }>;
};

export default async function AboutPage({ params }: PageProps) {
  const { locale } = await params;
  const content = await getLocalizedMdx("about", locale);

  return (
    <div className="bg-muted/50 py-12 min-h-screen">
      <div className="container mx-auto max-w-3xl px-4">
        <div className="prose prose-neutral max-w-none dark:prose-invert">{content}</div>
      </div>
    </div>
  );
}


================================================
FILE: app/[locale]/(app)/auth/(auth-layout)/forgot-password/page.tsx
================================================
import { ForgotPasswordForm } from "@/features/auth/forgot-password/ui/forgot-password-form";

export default async function ForgotPasswordPage() {
  return <ForgotPasswordForm />;
}


================================================
FILE: app/[locale]/(app)/auth/(auth-layout)/layout.tsx
================================================
import { redirect } from "next/navigation";
import { headers } from "next/headers";

import { getI18n } from "locales/server";
import { paths } from "@/shared/constants/paths";
import { auth } from "@/features/auth/lib/better-auth";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";

import type { LayoutParams } from "@/shared/types/next";

export default async function AuthLayout(props: LayoutParams<{}>) {
  const t = await getI18n();

  const headerStore = await headers();
  const searchParams = Object.fromEntries(new URLSearchParams(headerStore.get("searchParams") || ""));
  const translatedError = t(`next_auth_errors.${searchParams.error}` as keyof typeof t);

  const user = await auth.api.getSession({ headers: headerStore });

  if (user) {
    redirect(`/${paths.root}`);
  }

  return (
    <>
      <div className="h-full flex">
        {searchParams.error && (
          <Alert className="mb-4" variant="error">
            <AlertTitle>{translatedError}</AlertTitle>
            <AlertDescription>{t("signin_error_subtitle")}</AlertDescription>
          </Alert>
        )}

        <div className="flex flex-1 items-center justify-center">
          <div className="w-full max-w-md">{props.children}</div>
        </div>
      </div>
    </>
  );
}


================================================
FILE: app/[locale]/(app)/auth/(auth-layout)/reset-password/page.tsx
================================================
import { ResetPasswordForm } from "@/features/auth/reset-password/ui/reset-password-form";

export default function ResetPasswordPage() {
  return <ResetPasswordForm />;
}


================================================
FILE: app/[locale]/(app)/auth/(auth-layout)/signin/page.tsx
================================================
import { CredentialsLoginForm } from "@/features/auth/signin/ui/CredentialsLoginForm";

export default async function AuthSignInPage() {
  return <CredentialsLoginForm />;
}


================================================
FILE: app/[locale]/(app)/auth/(auth-layout)/signup/page.tsx
================================================
import Link from "next/link";

import { getI18n } from "locales/server";
import { paths } from "@/shared/constants/paths";
import { SignUpForm } from "@/features/auth/signup/ui/signup-form";

export const metadata = {
  title: "Sign Up - Workout.cool",
  description: "Créez votre compte pour commencer",
};

export default async function AuthSignUpPage() {
  const t = await getI18n();

  return (
    <div className="container mx-auto max-w-lg px-4 py-8">
      <div className="mb-8 space-y-2">
        <h1 className="text-3xl font-bold tracking-tight">{t("register_title")}</h1>
        <p className="text-muted-foreground">{t("register_description")}</p>
      </div>

      <SignUpForm />

      <div className="text-muted-foreground mt-6 text-center text-sm">
        <p>
          {t("register_terms")}{" "}
          <Link className="font-medium text-primary underline-offset-4 hover:underline" href={paths.privacy}>
            {t("register_privacy")}
          </Link>{" "}
          .
        </p>
      </div>
    </div>
  );
}

================================================
FILE: app/[locale]/(app)/auth/error/page.tsx
================================================
import Link from "next/link";

import { Card, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
import { buttonVariants } from "@/components/ui/button";

export default async function AuthErrorPage({ params }: { params: Promise<{ error: string }> }) {
  const result = await params;

  return (
    <div className="flex h-full flex-col">
      <Card>
        <CardHeader>
          <CardTitle>Error</CardTitle>
          <CardDescription>{result.error}</CardDescription>
        </CardHeader>
        <CardFooter className="flex items-center gap-2">
          <Link className={buttonVariants({ size: "small" })} href="/">
            Home
          </Link>
        </CardFooter>
      </Card>
    </div>
  );
}


================================================
FILE: app/[locale]/(app)/auth/error.tsx
================================================
"use client";

import { useEffect } from "react";

import { logger } from "@/shared/lib/logger";
import { Card, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";

import type { ErrorParams } from "@/shared/types/next";

export default function RouteError({ error, reset }: ErrorParams) {
  useEffect(() => {
    // Log the error to an error reporting service
    logger.error(error);
  }, [error]);

  return (
    <Card>
      <CardHeader>
        <CardTitle>Sorry, something went wrong. Please try again later.</CardTitle>
      </CardHeader>
      <CardFooter>
        <Button onClick={reset}>Try again</Button>
      </CardFooter>
    </Card>
  );
}


================================================
FILE: app/[locale]/(app)/auth/layout.tsx
================================================
export default function AuthLayout({ children }: { children: React.ReactNode }) {
  return <>{children}</>;
}


================================================
FILE: app/[locale]/(app)/auth/signout/page.tsx
================================================
export default function AuthSignOutPage() {
  return <div>AuthSignOutPage</div>;
}


================================================
FILE: app/[locale]/(app)/auth/verify-email/layout.tsx
================================================
import { ReactElement } from "react";
import { redirect } from "next/navigation";

import { getServerUrl } from "@/shared/lib/server-url";
import { paths } from "@/shared/constants/paths";
import { serverRequiredUser } from "@/entities/user/model/get-server-session-user";

interface RootLayoutProps {
  params: Promise<{ locale: string }>;
  children: ReactElement;
}

export default async function RootLayout({ children }: RootLayoutProps) {
  const auth = await serverRequiredUser();

  if (auth.emailVerified) {
    redirect(`${getServerUrl()}/${paths.root}`);
  }

  return children;
}


================================================
FILE: app/[locale]/(app)/auth/verify-email/page.tsx
================================================
"use client";

import { VerifyEmailPage } from "@/features/auth/verify-email/ui/verify-email-page";

export default function VerifyEmailRootPage() {
  return <VerifyEmailPage />;
}


================================================
FILE: app/[locale]/(app)/auth/verify-request/page.tsx
================================================
import Image from "next/image";

import { SiteConfig } from "@/shared/config/site-config";
import { Typography } from "@/components/ui/typography";
import { Card, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";

interface VerifyRequestPageParams {
  params: Promise<Record<string, string>>;
  searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
}

export default async function AuthVerifyRequestPage({ params: _p, searchParams: _s }: VerifyRequestPageParams) {
  return (
    <div className="h-full">
      <header className="flex items-center gap-2 px-4 pt-4">
        <Image alt="app icon" height={32} src={SiteConfig.appIcon} width={32} />
        <Typography variant="h2">{SiteConfig.title}</Typography>
      </header>
      <div className="flex h-full items-center justify-center">
        <Card className="w-full max-w-md">
          <CardHeader>
            <CardTitle>Almost There!</CardTitle>
            <CardDescription>
              {
                "To complete the verification, head over to your email inbox. You'll find a magic link from us. Click on it, and you're all set!"
              }
            </CardDescription>
          </CardHeader>
        </Card>
      </div>
    </div>
  );
}


================================================
FILE: app/[locale]/(app)/layout.tsx
================================================
import { ReactElement } from "react";

import { Header } from "@/features/layout/Header";
import { Footer } from "@/features/layout/Footer";
import { BottomNavigation } from "@/features/layout/BottomNavigation";

interface RootLayoutProps {
  params: Promise<{ locale: string }>;
  children: ReactElement;
}

export default async function RootLayout({ children }: RootLayoutProps) {
  return (
    <div className="card w-full max-w-3xl min-h-[500px] max-h-[90vh] bg-white dark:bg-[#232324] shadow-xl border border-base-200 dark:border-slate-700 flex flex-col justify-between overflow-hidden max-sm:rounded-none max-sm:h-full rounded-lg">
      <Header />
      <div className="flex-1 overflow-auto flex flex-col">{children}</div>
      <BottomNavigation />
      <Footer />
    </div>
  );
}


================================================
FILE: app/[locale]/(app)/leaderboard/page.tsx
================================================
import { Metadata } from "next";

import { Locale } from "locales/types";
import { getI18n } from "locales/server";
import LeaderboardPage from "@/features/leaderboard/ui/leaderboard-page";
import { Breadcrumbs } from "@/components/seo/breadcrumbs";

export const metadata: Metadata = {
  title: "🏆 Workout Streak Leaderboard",
  description: "See who's dominating their fitness journey with the longest workout streaks! Join the leaderboard and track your progress.",
};

export default async function LeaderboardRootPage({ params }: { params: Promise<{ locale: string }> }) {
  const { locale } = (await params) as { locale: Locale };
  const t = await getI18n();

  const breadcrumbItems = [
    {
      label: t("breadcrumbs.home"),
      href: `/${locale}`,
    },
    {
      label: t("bottom_navigation.leaderboard"),
      current: true,
    },
  ];

  return (
    <>
      <Breadcrumbs items={breadcrumbItems} />
      <LeaderboardPage />
    </>
  );
}


================================================
FILE: app/[locale]/(app)/onboarding/layout.tsx
================================================
import { LayoutParams } from "@/shared/types/next";

export default async function OnboardingLayout(props: LayoutParams<{}>) {
  // TODO: add onboarding logic

  return props.children;
}


================================================
FILE: app/[locale]/(app)/onboarding/page.tsx
================================================
export default async function OnboardingPage() {
  return (
    <main className="bg-muted flex min-h-screen flex-col items-center justify-center">
      <div>Onboarding</div>
    </main>
  );
}


================================================
FILE: app/[locale]/(app)/page.tsx
================================================
import React from "react";

import { getServerUrl } from "@/shared/lib/server-url";
import { SiteConfig } from "@/shared/config/site-config";
import { WorkoutStepper } from "@/features/workout-builder";

import type { Metadata } from "next";

export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }): Promise<Metadata> {
  const { locale } = await params;

  const isEnglish = locale === "en";
  const title = isEnglish ? "Build Your Perfect Workout" : "Créez Votre Entraînement Parfait";
  const description = isEnglish
    ? "Create free workout routines with our comprehensive exercise database. Track your progress and achieve your fitness goals. 🏋️"
    : "Créez des routines d'entraînement gratuites avec notre base de données d'exercices complète. Suivez vos progrès et atteignez vos objectifs fitness. 🏋️";

  return {
    title,
    description,
    keywords: isEnglish
      ? ["workout builder", "exercise planner", "fitness routine", "personalized training", "muscle targeting", "free workout"]
      : [
          "créateur d'entraînement",
          "planificateur d'exercices",
          "routine fitness",
          "entraînement personnalisé",
          "ciblage musculaire",
          "entraînement gratuit",
        ],
    openGraph: {
      title: `${title} | ${SiteConfig.title}`,
      description,
      images: [
        {
          url: `${getServerUrl()}/images/default-og-image_${locale}.jpg`,
          width: SiteConfig.seo.ogImage.width,
          height: SiteConfig.seo.ogImage.height,
          alt: title,
        },
      ],
    },
    twitter: {
      title: `${title} | ${SiteConfig.title}`,
      description,
      images: [`${getServerUrl()}/images/default-og-image_${locale}.jpg`],
    },
  };
}

export default async function HomePage() {
  return (
    <div className="bg-background text-foreground relative flex flex-col h-full">
      <WorkoutStepper />
    </div>
  );
}


================================================
FILE: app/[locale]/(app)/premium/page.tsx
================================================
import { Metadata } from "next";

import { PremiumUpgradeCard } from "@/features/premium/ui/premium-upgrade-card";

export const metadata: Metadata = {
  title: "Premium Plans - Train freely, support the mission",
  description:
    "Join thousands of fitness enthusiasts who believe in open-source training freedom. Support our mission while unlocking advanced features.",
  keywords: ["premium", "fitness", "workout", "open-source", "subscription", "training"],
  openGraph: {
    title: "Premium Plans - Support the Workout.cool Mission 💪",
    description: "For passionate fitness enthusiasts who believe in open-source and training freedom. Core features always free!",
    type: "website",
  },
  twitter: {
    card: "summary_large_image",
    title: "Premium Plans - Workout.cool",
    description: "Train freely, support the mission. Join the passionate fitness community!",
  },
};

export default function PremiumPage() {
  return (
    <div className="bg-white dark:bg-gray-950">
      {/* Main Content */}
      <div className="relative" data-section="pricing">
        <PremiumUpgradeCard />
      </div>

      {/* Mobile Sticky CTA */}
      {/* <MobileStickyCard /> */}
    </div>
  );
}


================================================
FILE: app/[locale]/(app)/profile/page.tsx
================================================
"use client";
import { useRouter } from "next/navigation";

import { useI18n } from "locales/client";
import { WorkoutSessionList } from "@/features/workout-session/ui/workout-session-list";
import { WorkoutSessionHeatmap } from "@/features/workout-session/ui/workout-session-heatmap";
import { useWorkoutSessions } from "@/features/workout-session/model/use-workout-sessions";
import { env } from "@/env";
import { useCurrentSession } from "@/entities/user/model/useCurrentSession";
import { LocalAlert } from "@/components/ui/local-alert";
import { Button } from "@/components/ui/button";
import { HorizontalTopBanner } from "@/components/ads";

export default function ProfilePage() {
  const router = useRouter();
  const t = useI18n();
  const { data: sessions = [] } = useWorkoutSessions();
  const session = useCurrentSession();

  const values: Record<string, number> = {};
  sessions.forEach((session) => {
    const date = session.startedAt.slice(0, 10);
    values[date] = (values[date] || 0) + 1;
  });

  const until =
    sessions.length > 0
      ? sessions.reduce((max, s) => (s.startedAt > max ? s.startedAt : max), sessions[0].startedAt).slice(0, 10)
      : new Date().toISOString().slice(0, 10);

  return (
    <div className="px-2 sm:px-6">
      {env.NEXT_PUBLIC_TOP_PROFILE_BANNER_AD_SLOT && <HorizontalTopBanner adSlot={env.NEXT_PUBLIC_TOP_PROFILE_BANNER_AD_SLOT} />}
      {!session && <LocalAlert className="my-4" />}
      {session && (
        <div className="mt-4">
          <div>
            <h2 className="text-2xl font-bold">Hello, {session.user?.name} 👋</h2>
          </div>
        </div>
      )}

      <div className="mt-4">
        <WorkoutSessionHeatmap until={until} values={values} />
      </div>
      <WorkoutSessionList />
      <div className="mt-8 flex justify-center">
        <Button onClick={() => router.push("/")} size="large">
          {t("profile.new_workout")}
        </Button>
      </div>
    </div>
  );
}


================================================
FILE: app/[locale]/(app)/programs/[slug]/page.tsx
================================================
import { notFound } from "next/navigation";
import { headers } from "next/headers";
import { Metadata } from "next";

import { Locale } from "locales/types";
import { getI18n } from "locales/server";
import { generateStructuredData, StructuredDataScript } from "@/shared/lib/structured-data";
import { getLocalizedMetadata } from "@/shared/config/localized-metadata";
import { ProgramDetailPage } from "@/features/programs/ui/program-detail-page";
import { getProgramDescription, getProgramTitle } from "@/features/programs/lib/translations-mapper";
import { generateProgramSEOKeywords } from "@/features/programs/lib/program-metadata";
import { getProgramBySlug } from "@/features/programs/actions/get-program-by-slug.action";
import { auth } from "@/features/auth/lib/better-auth";

interface ProgramDetailPageProps {
  params: Promise<{ slug: string; locale: Locale }>;
}

export async function generateMetadata({ params }: ProgramDetailPageProps): Promise<Metadata> {
  const { slug, locale } = await params;
  const t = await getI18n();
  const program = await getProgramBySlug(slug);
  const localizedData = getLocalizedMetadata(locale);

  if (!program) {
    return { title: t("programs.not_found") };
  }

  const localizedTitle = getProgramTitle(program, locale);
  const localizedDescription = getProgramDescription(program, locale);
  const seoKeywords = generateProgramSEOKeywords(program, locale, t);

  return {
    title: `${localizedTitle} - ${localizedData.title}`,
    description: localizedDescription,
    keywords: seoKeywords,
    openGraph: {
      title: `${localizedTitle} - ${localizedData.title}`,
      description: localizedDescription,
      images: [
        {
          url: program.image, // TODO: specific opengraph image for each program (upload admin side)
          width: 400,
          height: 600,
          alt: localizedTitle,
        },
      ],
    },
    twitter: {
      card: "summary_large_image",
      title: `${localizedTitle} - ${localizedData.title}`,
      description: localizedDescription,
      images: [program.image],
    },
  };
}

export default async function ProgramDetailPageRoute({ params }: ProgramDetailPageProps) {
  const { slug, locale } = await params;
  const program = await getProgramBySlug(slug);

  if (!program) {
    notFound();
  }

  const session = await auth.api.getSession({
    headers: await headers(),
  });

  // Generate Course structured data
  const localizedTitle = getProgramTitle(program, locale);
  const localizedDescription = getProgramDescription(program, locale);

  const courseStructuredData = generateStructuredData({
    type: "Course",
    locale,
    title: localizedTitle,
    description: localizedDescription,
    courseData: {
      id: program.id,
      level: program.level,
      category: program.category,
      durationWeeks: program.durationWeeks,
      sessionsPerWeek: program.sessionsPerWeek,
      sessionDurationMin: program.sessionDurationMin,
      equipment: program.equipment,
      isPremium: program.isPremium,
      participantCount: program.participantCount,
      totalSessions: program.weeks.reduce((acc, week) => acc + week.sessions.length, 0),
      totalExercises: program.weeks.reduce(
        (acc, week) => acc + week.sessions.reduce((sessAcc, session) => sessAcc + session.totalExercises, 0),
        0,
      ),
      coaches: program.coaches,
    },
  });

  // Breadcrumbs

  return (
    <>
      <StructuredDataScript data={courseStructuredData} />
      <ProgramDetailPage isAuthenticated={!!session} program={program} />
    </>
  );
}


================================================
FILE: app/[locale]/(app)/programs/[slug]/session/[sessionSlug]/ProgramSessionClient.tsx
================================================
"use client";

import { useState } from "react";
import { useRouter } from "next/navigation";
import { ArrowLeft, Play } from "lucide-react";
import { ExerciseAttributeNameEnum, ProgramWeek } from "@prisma/client";

import { useCurrentLocale, useI18n } from "locales/client";
import { canStartSession } from "@/shared/lib/access-control";
import { WorkoutSessionSets } from "@/features/workout-session/ui/workout-session-sets";
import { WorkoutSessionHeader } from "@/features/workout-session/ui/workout-session-header";
import { useWorkoutSession } from "@/features/workout-session/model/use-workout-session";
import { SessionAccessGuard } from "@/features/programs/ui/session-access-guard";
import { getSessionDescription, getSessionTitle, getSessionSlug, getProgramTitle } from "@/features/programs/lib/translations-mapper";
import { startProgramSession } from "@/features/programs/actions/start-program-session.action";
import { enrollInProgram } from "@/features/programs/actions/enroll-program.action";
import { completeProgramSession } from "@/features/programs/actions/complete-program-session.action";
import { ProgramSessionWithExercises } from "@/entities/program-session/types/program-session.types";
import { ProgramI18nReference } from "@/entities/program/types/program.types";
import { Button } from "@/components/ui/button";
import { SessionRichSnippets } from "@/components/seo/session-rich-snippets";

interface ProgramSessionClientProps {
  program: ProgramI18nReference;
  week: ProgramWeek;
  session: ProgramSessionWithExercises;
  isAuthenticated: boolean;
  isPremium: boolean;
}

export function ProgramSessionClient({ program, week, session, isAuthenticated, isPremium }: ProgramSessionClientProps) {
  const t = useI18n();
  const locale = useCurrentLocale();
  const router = useRouter();
  const { startWorkout, session: workoutSession, completeWorkout, isWorkoutActive, quitWorkout } = useWorkoutSession();
  const [isLoading, setIsLoading] = useState(false);
  const [_enrollmentId, setEnrollmentId] = useState<string | null>(null);
  const [sessionProgressId, setSessionProgressId] = useState<string | null>(null);
  const [showCongrats, setShowCongrats] = useState(false);
  const [hasStartedWorkout, setHasStartedWorkout] = useState(false);

  const programTitle = getProgramTitle(program, locale);
  const programSessionTitle = getSessionTitle(session, locale);
  const programSessionDescription = getSessionDescription(session, locale);
  const programSlug = getSessionSlug(program, locale);
  const sessionSlug = getSessionSlug(session, locale);

  // Access control context
  const accessContext = {
    isAuthenticated,
    isPremium,
    isSessionPremium: session.isPremium,
  };

  const handleStartWorkout = async () => {
    if (!canStartSession(accessContext)) return;

    setIsLoading(true);
    try {
      // Ensure user is enrolled
      const { enrollment } = await enrollInProgram(program.id);
      setEnrollmentId(enrollment.id);

      // Start or resume session
      const { sessionProgress } = await startProgramSession(enrollment.id, session.id);
      setSessionProgressId(sessionProgress.id);

      // Convert program exercises to workout format
      const exercises = session.exercises.map((ex) => ({
        id: ex.exercise.id,
        name: ex.exercise.name,
        nameEn: ex.exercise.nameEn || null,
        description: ex.exercise.description || "",
        descriptionEn: ex.exercise.descriptionEn || "",
        fullVideoUrl: ex.exercise.fullVideoUrl || null,
        fullVideoImageUrl: ex.exercise.fullVideoImageUrl || null,
        introduction: null,
        introductionEn: null,
        slug: null,
        slugEn: null,
        createdAt: new Date(),
        updatedAt: new Date(),
        order: ex.order,
        attributes: ex.exercise.attributes.map((attr) => ({
          id: attr.id,
          createdAt: new Date(),
          updatedAt: new Date(),
          exerciseId: ex.exercise.id,
          attributeNameId: attr.attributeNameId,
          attributeValueId: attr.attributeValueId,
          attributeName: attr.attributeName,
          attributeValue: attr.attributeValue,
        })),
      }));

      // Extract equipment and muscles from session exercises
      const equipment = session.exercises.flatMap((ex) =>
        ex.exercise.attributes
          .filter((attr) => attr.attributeName === ExerciseAttributeNameEnum.EQUIPMENT)
          .map((attr) => attr.attributeValue),
      );

      const muscles = session.exercises.flatMap((ex) =>
        ex.exercise.attributes
          .filter((attr) => attr.attributeName === ExerciseAttributeNameEnum.PRIMARY_MUSCLE)
          .map((attr) => attr.attributeValue),
      );

      // Convert suggestedSets to workout format
      const exercisesWithSets = exercises.map((exercise, idx) => {
        const programExercise = session.exercises[idx];
        const suggestedSets = programExercise?.suggestedSets || [];

        const workoutSets = suggestedSets.map((suggestedSet, setIndex) => ({
          id: `${exercise.id}-set-${setIndex + 1}`,
          setIndex,
          types: suggestedSet.types || [],
          valuesInt: suggestedSet.valuesInt || [],
          valuesSec: suggestedSet.valuesSec || [],
          units: suggestedSet.units || [],
          completed: false,
        }));

        return {
          ...exercise,
          sets:
            workoutSets.length > 0
              ? workoutSets
              : [
                  {
                    id: `${exercise.id}-set-1`,
                    setIndex: 0,
                    types: ["REPS"],
                    valuesInt: [],
                    valuesSec: [],
                    units: [],
                    completed: false,
                  },
                ],
        };
      });

      startWorkout(exercisesWithSets, equipment, muscles);
      setHasStartedWorkout(true);
    } catch (error) {
      console.error("Failed to start session:", error);
      alert(t("programs.error_starting_session"));
    } finally {
      setIsLoading(false);
    }
  };

  const handleCompleteSession = async () => {
    if (!workoutSession || !sessionProgressId) return;

    try {
      // Complete the workout
      completeWorkout();

      // Save to database and mark session as complete
      const { isCompleted, nextWeek, nextSession } = await completeProgramSession(sessionProgressId, workoutSession.id);

      setShowCongrats(true);

      if (isCompleted) {
        router.push(`/programs/${programSlug}?completed=true&refresh=${Date.now()}`);
      } else {
        router.push(`/programs/${programSlug}?week=${nextWeek}&session=${nextSession}&refresh=${Date.now()}`);
      }
    } catch (error) {
      console.error("Failed to complete session:", error);
    }
  };

  const handleQuitWorkout = () => {
    quitWorkout();
    setHasStartedWorkout(false);
  };

  // Show workout interface if user has started the workout
  if (hasStartedWorkout && isWorkoutActive && workoutSession) {
    return (
      <div className="w-full max-w-6xl mx-auto">
        <WorkoutSessionHeader onQuitWorkout={handleQuitWorkout} />
        <WorkoutSessionSets isWorkoutActive={isWorkoutActive} onCongrats={handleCompleteSession} showCongrats={showCongrats} />
      </div>
    );
  }

  const totalSets = session.exercises.reduce((total, ex) => total + ex.suggestedSets.length, 0);

  // Use access guard to handle authentication and premium restrictions
  return (
    <SessionAccessGuard
      context={accessContext}
      programSlug={programSlug}
      sessionDescription={programSessionDescription}
      sessionSlug={sessionSlug}
      sessionTitle={programSessionTitle}
    >
      <div className="flex flex-col h-screen">
        {/* Header */}
        <div className="bg-gradient-to-r from-[#4F8EF7] to-[#25CB78] text-white p-4">
          <div className="flex items-center gap-4 mb-2">
            <Button
              className="text-white hover:bg-white/20"
              onClick={() => router.push(`/programs/${programSlug}`)}
              size="icon"
              variant="ghost"
            >
              <ArrowLeft size={20} />
            </Button>
            <div className="flex-1">
              <p className="text-sm opacity-90">
                {programTitle} - {t("programs.week")} {week.weekNumber}
              </p>
              <h1 className="text-xl font-bold">{programSessionTitle}</h1>
            </div>
          </div>
        </div>

        {/* Session preview content */}
        <main className="flex-1 bg-gray-50 dark:bg-gray-900 p-2 md:p-6">
          <div className="max-w-4xl mx-auto">
            <article className="bg-white dark:bg-gray-800 rounded-lg p-2 sm:p-6 mb-6">
              {/* Session info */}
              <header className="flex items-center justify-between mb-6 flex-col text-center">
                <div className="mt-5 sm:mt-0 flex flex-col gap-2">
                  <h1 className="text-2xl font-bold text-gray-900 dark:text-white">{programSessionTitle}</h1>
                  <SessionRichSnippets
                    duration={Math.round(session.exercises.length * 3)}
                    exerciseCount={session.exercises.length}
                    totalSets={totalSets}
                  />
                </div>
                {programSessionDescription && <p className="text-gray-600 dark:text-gray-400 mt-2">{programSessionDescription}</p>}
              </header>

              {/* Exercise list */}
              <section className="space-y-4 mb-8">
                <h2 className="text-lg font-semibold text-gray-900 dark:text-white">{t("programs.exercises_in_session")}</h2>
                <div className="grid gap-3">
                  {session.exercises.map((exercise, index) => {
                    const exerciseName = locale === "fr" ? exercise.exercise.name : exercise.exercise.nameEn;
                    return (
                      <div className="flex items-center p-4 bg-gray-50 dark:bg-gray-700 rounded-lg" key={exercise.id}>
                        <div className="w-8 h-8 bg-blue-500 text-white rounded-full flex items-center justify-center text-sm font-bold mr-4">
                          {index + 1}
                        </div>
                        <div className="flex-1">
                          <h3 className="font-medium text-gray-900 dark:text-white">{exerciseName}</h3>
                        </div>
                        <div className="text-right">
                          <span className="text-sm text-gray-500 dark:text-gray-400">
                            {exercise.suggestedSets.length} {t("programs.set", { count: exercise.suggestedSets.length })}
                          </span>
                        </div>
                      </div>
                    );
                  })}
                </div>
              </section>

              {/* Start workout button */}
              <footer className="flex justify-center">
                <Button
                  className="bg-gradient-to-r from-[#4F8EF7] to-[#25CB78] hover:from-[#4F8EF7]/80 hover:to-[#25CB78]/80 text-white px-8 py-4 text-lg font-bold rounded-xl flex items-center gap-3"
                  disabled={isLoading}
                  onClick={handleStartWorkout}
                >
                  {isLoading ? (
                    <>
                      <div className="animate-spin rounded-full h-5 w-5 border-b-2 border-white"></div>
                      {t("programs.starting_session")}
                    </>
                  ) : (
                    <>
                      <Play size={20} />
                      {t("programs.start_session")}
                    </>
                  )}
                </Button>
              </footer>
            </article>
          </div>
        </main>
      </div>
    </SessionAccessGuard>
  );
}


================================================
FILE: app/[locale]/(app)/programs/[slug]/session/[sessionSlug]/page.tsx
================================================
import { notFound } from "next/navigation";
import { headers } from "next/headers";
import { Metadata } from "next";

import { Locale } from "locales/types";
import { getI18n } from "locales/server";
import { generateStructuredData, StructuredDataScript } from "@/shared/lib/structured-data";
import { getSessionTitle, getProgramTitle } from "@/features/programs/lib/translations-mapper";
import { generateSessionMetadata } from "@/features/programs/lib/session-metadata";
import { getSessionBySlug } from "@/features/programs/actions/get-session-by-slug.action";
import { auth } from "@/features/auth/lib/better-auth";
import { Breadcrumbs } from "@/components/seo/breadcrumbs";

// Import the existing session client component
import { ProgramSessionClient } from "./ProgramSessionClient";

interface SessionDetailPageProps {
  params: Promise<{ slug: string; sessionSlug: string; locale: Locale }>;
}

export async function generateMetadata({ params }: SessionDetailPageProps): Promise<Metadata> {
  const { slug, sessionSlug, locale } = await params;
  const t = await getI18n();
  const response = await getSessionBySlug(slug, sessionSlug, locale);

  if (!response) {
    return { title: t("programs.not_found") };
  }

  const sessionMetadata = generateSessionMetadata(response.session, response.program, locale);
  const imageUrl = response.session.exercises[0]?.exercise.fullVideoImageUrl || "/images/default-workout.jpg";

  return {
    title: sessionMetadata.title,
    description: sessionMetadata.description,
    keywords: sessionMetadata.keywords,
    openGraph: {
      title: sessionMetadata.title,
      description: sessionMetadata.description,
      url: `https://www.workout.cool/${locale}/programs/${slug}/session/${sessionSlug}`,
      siteName: "Workout Cool",
      images: [
        {
          url: imageUrl,
          width: 800,
          height: 600,
          alt: sessionMetadata.title,
        },
      ],
      locale: locale === "zh-CN" ? "zh_CN" : locale.replace("-", "_"),
      type: "website",
    },
    twitter: {
      card: "summary_large_image",
      title: sessionMetadata.title,
      description: sessionMetadata.description,
      images: [imageUrl],
      creator: "@WorkoutCool",
    },
    alternates: {
      canonical: `https://www.workout.cool/${locale}/programs/${slug}/session/${sessionSlug}`,
      languages: {
        "fr-FR": `https://www.workout.cool/fr/programs/${slug}/session/${sessionSlug}`,
        "en-US": `https://www.workout.cool/en/programs/${slug}/session/${sessionSlug}`,
        "es-ES": `https://www.workout.cool/es/programs/${slug}/session/${sessionSlug}`,
        "pt-PT": `https://www.workout.cool/pt/programs/${slug}/session/${sessionSlug}`,
        "ru-RU": `https://www.workout.cool/ru/programs/${slug}/session/${sessionSlug}`,
        "zh-CN": `https://www.workout.cool/zh-CN/programs/${slug}/session/${sessionSlug}`,
        "x-default": `https://www.workout.cool/programs/${slug}/session/${sessionSlug}`,
      },
    },
    robots: {
      index: true,
      follow: true,
      googleBot: {
        index: true,
        follow: true,
        "max-video-preview": -1,
        "max-image-preview": "large",
        "max-snippet": -1,
      },
    },
  };
}

export default async function SessionDetailPage({ params }: SessionDetailPageProps) {
  const { slug, sessionSlug, locale } = await params;
  const response = await getSessionBySlug(slug, sessionSlug, locale);

  if (!response) {
    notFound();
  }

  const authSession = await auth.api.getSession({
    headers: await headers(),
  });

  // Pass authentication and premium status
  const isAuthenticated = !!authSession?.user;
  const isPremium = authSession?.user?.isPremium || false;

  const t = await getI18n();
  const sessionTitle = getSessionTitle(response.session, locale);
  const programTitle = getProgramTitle(response.program, locale);

  // Generate breadcrumb items
  const breadcrumbItems = [
    {
      label: t("breadcrumbs.home"),
      href: `/${locale}`,
    },
    {
      label: t("programs.workout_programs"),
      href: `/${locale}/programs`,
    },
    {
      label: programTitle,
      href: `/${locale}/programs/${slug}`,
    },
    {
      label: sessionTitle,
      current: true,
    },
  ];

  // Generate VideoObject structured data
  const sessionStructuredData = generateStructuredData({
    type: "VideoObject",
    locale,
    title: `${sessionTitle} - ${programTitle}`,
    description: response.session.description || `${sessionTitle} workout session`,
    url: `https://www.workout.cool/${locale}/programs/${slug}/session/${sessionSlug}`,
    image: response.session.exercises[0]?.exercise.fullVideoImageUrl || undefined,
    sessionData: {
      duration: Math.round(response.session.exercises.length * 3), // Estimate 3 min per exercise
      exercises: response.session.exercises.map((ex) => ({
        name: ex.exercise.name,
        sets: ex.suggestedSets.length,
      })),
      thumbnailUrl: response.session.exercises[0]?.exercise.fullVideoImageUrl || undefined,
      videoUrl: response.session.exercises[0]?.exercise.fullVideoUrl || undefined,
    },
  });

  return (
    <>
      <StructuredDataScript data={sessionStructuredData} />
      <Breadcrumbs items={breadcrumbItems} />
      <ProgramSessionClient
        isAuthenticated={isAuthenticated}
        isPremium={isPremium}
        program={response.program}
        session={response.session}
        week={response.week}
      />
    </>
  );
}


================================================
FILE: app/[locale]/(app)/programs/page.tsx
================================================
import { Metadata } from "next";

import { Locale } from "locales/types";
import { getI18n } from "locales/server";
import { ProgramsPage } from "@/features/programs/ui/programs-page";
import { Breadcrumbs } from "@/components/seo/breadcrumbs";

export const metadata: Metadata = {
  title: "Programmes",
  description: "Découvrez nos programmes d'entraînement gamifiés pour tous les niveaux - Rejoins la communauté WorkoutCool !",
};

export default async function ProgramsRootPage({ params }: { params: Promise<{ locale: string }> }) {
  const { locale } = (await params) as { locale: Locale };
  const t = await getI18n();

  const breadcrumbItems = [
    {
      label: t("breadcrumbs.home"),
      href: `/${locale}`,
    },
    {
      label: t("programs.workout_programs"),
      current: true,
    },
  ];

  return (
    <>
      <Breadcrumbs items={breadcrumbItems} />
      <ProgramsPage locale={locale} />
    </>
  );
}


================================================
FILE: app/[locale]/(app)/statistics/page.tsx
================================================
import React from "react";

import { getI18n } from "locales/server";
import { ExercisesBrowser } from "@/features/statistics/components/ExercisesBrowser";
import { PremiumGate } from "@/components/ui/premium-gate";

export default async function StatisticsPage() {
  const t = await getI18n();
  return (
    <div className="container mx-auto px-2 sm:px-4 py-8 max-w-7xl">
      <div className="text-center mb-12">
        <h1 className="text-4xl sm:text-6xl font-black mb-4 bg-gradient-to-r from-[#4F8EF7] via-[#8B5CF6] to-[#25CB78] bg-clip-text text-transparent">
          {t("statistics.title")}
        </h1>
        <p className="text-xl text-gray-600 dark:text-gray-300 mb-8 max-w-2xl mx-auto">{t("statistics.page_subtitle")}</p>

        {/* Stats hero social proof */}
        <PremiumGate
          fallback={
            <div className="flex justify-center gap-8 mb-8">
              <div className="text-center">
                <p className="text-3xl font-bold text-[#4F8EF7]">15.4K+</p>
                <p className="text-sm text-gray-500 dark:text-gray-400">{t("statistics.active_daily_users")}</p>
              </div>
              <div className="text-center">
                <p className="text-3xl font-bold text-[#25CB78]">89%</p>
                <p className="text-sm text-gray-500 dark:text-gray-400">{t("statistics.success_rate")}</p>
              </div>
              <div className="text-center">
                <p className="text-3xl font-bold text-[#8B5CF6]">4.8★</p>
                <p className="text-sm text-gray-500 dark:text-gray-400">{t("statistics.user_rating")}</p>
              </div>
            </div>
          }
          feature="Statistics"
        >
          {/* this is the premium content ↓ */}
          <div />
        </PremiumGate>
      </div>

      {/* Main Content */}
      <ExercisesBrowser />
    </div>
  );
}


================================================
FILE: app/[locale]/(app)/tools/bmi-calculator/bmi-calculator.utils.ts
================================================
import { TFunction } from "locales/client";

export type UnitSystem = "metric" | "imperial";

export interface BmiData {
  height: number; // cm for metric, inches for imperial
  weight: number; // kg for metric, lbs for imperial
  unit: UnitSystem;
}

export interface BmiResult {
  bmi: number;
  bmiPrime: number;
  ponderalIndex: number;
  category: BmiCategory;
  healthRisk: HealthRisk;
  recommendations: string[];
  detailedInfo: {
    bmiRange: { min: number; max: number };
    idealWeight: { min: number; max: number };
    weightToLose?: number;
    weightToGain?: number;
  };
}

export type BmiCategory = 
  | "severe_thinness" 
  | "moderate_thinness" 
  | "mild_thinness" 
  | "normal" 
  | "overweight" 
  | "obese_class_1" 
  | "obese_class_2" 
  | "obese_class_3";

export type HealthRisk = "low" | "normal" | "increased" | "high" | "very_high" | "extremely_high";

export function calculateBmi(data: BmiData, t: TFunction): BmiResult {
  const { height: initialHeight, weight: initialWeight, unit } = data;
  let height = initialHeight;
  let weight = initialWeight;

  // Convert to metric if needed
  if (unit === "imperial") {
    height = height * 2.54; // inches to cm
    weight = weight * 0.453592; // lbs to kg
  }

  // Convert height from cm to meters
  const heightInMeters = height / 100;

  // Calculate BMI
  const bmi = weight / (heightInMeters * heightInMeters);

  // Calculate BMI Prime
  const bmiPrime = bmi / 25;

  // Calculate Ponderal Index
  const ponderalIndex = weight / (heightInMeters * heightInMeters * heightInMeters);

  // Determine category and health risk
  const category = getBmiCategory(bmi);
  const healthRisk = getHealthRisk(category);
  const recommendations = getRecommendations(category, t);

  // Calculate detailed info
  const bmiRange = getBmiRange(category);
  const idealWeight = calculateIdealWeight(heightInMeters);
  const weightToLose = (category === "overweight" || category === "obese_class_1" || category === "obese_class_2" || category === "obese_class_3")
    ? Math.max(0, weight - idealWeight.max)
    : undefined;
  const weightToGain = (category === "severe_thinness" || category === "moderate_thinness" || category === "mild_thinness")
    ? Math.max(0, idealWeight.min - weight)
    : undefined;

  return {
    bmi: Math.round(bmi * 10) / 10,
    bmiPrime: Math.round(bmiPrime * 100) / 100,
    ponderalIndex: Math.round(ponderalIndex * 10) / 10,
    category,
    healthRisk,
    recommendations,
    detailedInfo: {
      bmiRange,
      idealWeight,
      weightToLose,
      weightToGain,
    },
  };
}

export function getBmiCategory(bmi: number): BmiCategory {
  if (bmi < 16) return "severe_thinness";
  if (bmi < 17) return "moderate_thinness";
  if (bmi < 18.5) return "mild_thinness";
  if (bmi < 25) return "normal";
  if (bmi < 30) return "overweight";
  if (bmi < 35) return "obese_class_1";
  if (bmi < 40) return "obese_class_2";
  return "obese_class_3";
}

export function getHealthRisk(category: BmiCategory): HealthRisk {
  switch (category) {
    case "severe_thinness":
      return "very_high";
    case "moderate_thinness":
      return "high";
    case "mild_thinness":
      return "increased";
    case "normal":
      return "normal";
    case "overweight":
      return "increased";
    case "obese_class_1":
      return "high";
    case "obese_class_2":
      return "very_high";
    case "obese_class_3":
      return "extremely_high";
    default:
      return "normal";
  }
}

export function getRecommendations(category: BmiCategory, t: TFunction): string[] {
  switch (category) {
    case "severe_thinness":
      return [
        t("bmi-calculator.recommendations.severe_thinness.medical_consultation"),
        t("bmi-calculator.recommendations.severe_thinness.nutritional_assessment"),
        t("bmi-calculator.recommendations.severe_thinness.weight_gain_program"),
        t("bmi-calculator.recommendations.severe_thinness.screen_conditions"),
        t("bmi-calculator.recommendations.severe_thinness.psychological_evaluation"),
      ];
    case "moderate_thinness":
      return [
        t("bmi-calculator.recommendations.moderate_thinness.healthcare_provider"),
        t("bmi-calculator.recommendations.moderate_thinness.nutrient_dense_foods"),
        t("bmi-calculator.recommendations.moderate_thinness.registered_dietitian"),
        t("bmi-calculator.recommendations.moderate_thinness.monitor_malnutrition"),
        t("bmi-calculator.recommendations.moderate_thinness.gradual_weight_gain"),
      ];
    case "mild_thinness":
      return [
        t("bmi-calculator.recommendations.mild_thinness.consider_healthcare"),
        t("bmi-calculator.recommendations.mild_thinness.nutrient_dense_foods"),
        t("bmi-calculator.recommendations.mild_thinness.strength_training"),
        t("bmi-calculator.recommendations.mild_thinness.monitor_health"),
        t("bmi-calculator.recommendations.mild_thinness.gradual_weight_gain"),
      ];
    case "normal":
      return [
        t("bmi-calculator.recommendations.normal.maintain_weight"),
        t("bmi-calculator.recommendations.normal.physical_activity"),
        t("bmi-calculator.recommendations.normal.balanced_diet"),
        t("bmi-calculator.recommendations.normal.health_checkups"),
        t("bmi-calculator.recommendations.normal.overall_wellness"),
      ];
    case "overweight":
      return [
        t("bmi-calculator.recommendations.overweight.gradual_weight_loss"),
        t("bmi-calculator.recommendations.overweight.increase_activity"),
        t("bmi-calculator.recommendations.overweight.portion_control"),
        t("bmi-calculator.recommendations.overweight.healthcare_provider"),
        t("bmi-calculator.recommendations.overweight.lifestyle_goals"),
      ];
    case "obese_class_1":
      return [
        t("bmi-calculator.recommendations.obese_class_1.healthcare_provider"),
        t("bmi-calculator.recommendations.obese_class_1.weight_loss_target"),
        t("bmi-calculator.recommendations.obese_class_1.diet_exercise"),
        t("bmi-calculator.recommendations.obese_class_1.nutritional_counseling"),
        t("bmi-calculator.recommendations.obese_class_1.screen_conditions"),
      ];
    case "obese_class_2":
      return [
        t("bmi-calculator.recommendations.obese_class_2.medical_supervision"),
        t("bmi-calculator.recommendations.obese_class_2.lifestyle_programs"),
        t("bmi-calculator.recommendations.obese_class_2.evaluate_conditions"),
        t("bmi-calculator.recommendations.obese_class_2.medical_treatments"),
        t("bmi-calculator.recommendations.obese_class_2.bariatric_surgery"),
      ];
    case "obese_class_3":
      return [
        t("bmi-calculator.recommendations.obese_class_3.medical_consultation"),
        t("bmi-calculator.recommendations.obese_class_3.bariatric_surgery"),
        t("bmi-calculator.recommendations.obese_class_3.weight_management"),
        t("bmi-calculator.recommendations.obese_class_3.health_complications"),
        t("bmi-calculator.recommendations.obese_class_3.multidisciplinary"),
      ];
    default:
      return [];
  }
}

export function getBmiRange(category: BmiCategory): { min: number; max: number } {
  switch (category) {
    case "severe_thinness":
      return { min: 0, max: 15.9 };
    case "moderate_thinness":
      return { min: 16, max: 16.9 };
    case "mild_thinness":
      return { min: 17, max: 18.4 };
    case "normal":
      return { min: 18.5, max: 24.9 };
    case "overweight":
      return { min: 25, max: 29.9 };
    case "obese_class_1":
      return { min: 30, max: 34.9 };
    case "obese_class_2":
      return { min: 35, max: 39.9 };
    case "obese_class_3":
      return { min: 40, max: 100 };
    default:
      return { min: 0, max: 100 };
  }
}

export function calculateIdealWeight(heightInMeters: number): { min: number; max: number } {
  // Calculate ideal weight range based on normal BMI (18.5-24.9)
  const minWeight = 18.5 * heightInMeters * heightInMeters;
  const maxWeight = 24.9 * heightInMeters * heightInMeters;
  
  return {
    min: Math.round(minWeight * 10) / 10,
    max: Math.round(maxWeight * 10) / 10,
  };
}

export function convertHeight(height: number, fromUnit: UnitSystem, toUnit: UnitSystem): number {
  if (fromUnit === toUnit) return height;
  
  if (fromUnit === "imperial" && toUnit === "metric") {
    return height * 2.54; // inches to cm
  } else {
    return height / 2.54; // cm to inches
  }
}

export function convertWeight(weight: number, fromUnit: UnitSystem, toUnit: UnitSystem): number {
  if (fromUnit === toUnit) return weight;
  
  if (fromUnit === "imperial" && toUnit === "metric") {
    return weight * 0.453592; // lbs to kg
  } else {
    return weight / 0.453592; // kg to lbs
  }
}

// Additional utility functions for enhanced BMI analysis

export function getBmiPrimeCategory(bmiPrime: number): string {
  if (bmiPrime < 0.64) return "severe_thinness";
  if (bmiPrime < 0.68) return "moderate_thinness";
  if (bmiPrime < 0.74) return "mild_thinness";
  if (bmiPrime <= 1) return "normal";
  if (bmiPrime <= 1.2) return "overweight";
  if (bmiPrime <= 1.4) return "obese_class_1";
  if (bmiPrime <= 1.6) return "obese_class_2";
  return "obese_class_3";
}

export function getPonderalIndexCategory(pi: number): string {
  // Ponderal Index normal range is typically 11-14 kg/m³
  if (pi < 11) return "low";
  if (pi <= 14) return "normal";
  return "high";
}

export function getHealthRisks(_category: BmiCategory, t: TFunction): { overweight: string[]; underweight: string[] } {
  const overweightRisks = [
    t("bmi-calculator.health_risks.overweight.high_blood_pressure"),
    t("bmi-calculator.health_risks.overweight.ldl_cholesterol"),
    t("bmi-calculator.health_risks.overweight.hdl_cholesterol"),
    t("bmi-calculator.health_risks.overweight.triglycerides"),
    t("bmi-calculator.health_risks.overweight.type_2_diabetes"),
    t("bmi-calculator.health_risks.overweight.coronary_heart_disease"),
    t("bmi-calculator.health_risks.overweight.stroke"),
    t("bmi-calculator.health_risks.overweight.gallbladder_disease"),
    t("bmi-calculator.health_risks.overweight.osteoarthritis"),
    t("bmi-calculator.health_risks.overweight.sleep_apnea"),
    t("bmi-calculator.health_risks.overweight.certain_cancers"),
    t("bmi-calculator.health_risks.overweight.low_quality_life"),
    t("bmi-calculator.health_risks.overweight.mental_illnesses"),
    t("bmi-calculator.health_risks.overweight.body_pains"),
    t("bmi-calculator.health_risks.overweight.increased_mortality"),
  ];

  const underweightRisks = [
    t("bmi-calculator.health_risks.underweight.malnutrition"),
    t("bmi-calculator.health_risks.underweight.anemia"),
    t("bmi-calculator.health_risks.underweight.osteoporosis"),
    t("bmi-calculator.health_risks.underweight.immune_function"),
    t("bmi-calculator.health_risks.underweight.growth_development"),
    t("bmi-calculator.health_risks.underweight.reproductive_issues"),
    t("bmi-calculator.health_risks.underweight.miscarriage_risk"),
    t("bmi-calculator.health_risks.underweight.surgery_complications"),
    t("bmi-calculator.health_risks.underweight.increased_mortality"),
    t("bmi-calculator.health_risks.underweight.underlying_conditions"),
  ];

  return { overweight: overweightRisks, underweight: underweightRisks };
}

================================================
FILE: app/[locale]/(app)/tools/bmi-calculator/page.tsx
================================================
import React from "react";
import { Metadata } from "next";

import { getI18n } from "locales/server";
import { BmiEducationalContent } from "app/[locale]/(app)/tools/bmi-calculator/shared/components/BmiEducationalContent";
import { BmiCalculatorClient } from "app/[locale]/(app)/tools/bmi-calculator/shared/BmiCalculatorClient";
import { getServerUrl } from "@/shared/lib/server-url";
import { env } from "@/env";
import { generateSEOMetadata, SEOScripts } from "@/components/seo/SEOHead";
import { HorizontalBottomBanner, HorizontalTopBanner } from "@/components/ads";

export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }): Promise<Metadata> {
  const { locale } = await params;
  const t = await getI18n();

  return generateSEOMetadata({
    title: t("tools.bmi-calculator-hub.meta.title"),
    description: t("tools.bmi-calculator-hub.meta.description"),
    keywords: [
      ...t("tools.bmi-calculator-hub.meta.keywords").split(", "),
      "BMI formula",
      "BMI prime",
      "ponderal index",
      "WHO BMI classification",
      "CDC BMI percentiles",
      "BMI health risks",
      "BMI limitations",
      "body mass index calculator",
      "BMI chart",
      "BMI table",
      "overweight risks",
      "underweight risks",
      "BMI for adults",
      "BMI for children",
      "BMI accuracy",
    ],
    locale,
    canonical: `${getServerUrl()}/${locale}/tools/bmi-calculator`,
    structuredData: {
      type: "Calculator",
      calculatorData: {
        calculatorType: "bmi",
        inputFields: ["height", "weight", "age", "gender"],
        outputFields: [
          "BMI",
          "BMI Prime",
          "Ponderal Index",
          "BMI category",
          "health risk assessment",
          "ideal weight range",
          "health recommendations",
        ],
        formula: "BMI = weight (kg) / height (m)²",
        accuracy: "Standard WHO classification with detailed health risk assessment",
        targetAudience: [
          "health conscious individuals",
          "fitness enthusiasts",
          "medical professionals",
          "general public",
          "parents",
          "athletes",
        ],
        relatedCalculators: ["standard-calculator", "adjusted-calculator", "pediatric-calculator", "bmi-comparison"],
      },
    },
  });
}

export default async function BmiCalculatorPage({ params }: { params: Promise<{ locale: string }> }) {
  const { locale } = await params;
  const t = await getI18n();

  return (
    <>
      <SEOScripts
        canonical={`${getServerUrl()}/${locale}/tools/bmi-calculator`}
        description={t("tools.bmi-calculator-hub.meta.description")}
        locale={locale}
        structuredData={{
          type: "Calculator",
          calculatorData: {
            calculatorType: "bmi",
            inputFields: ["height", "weight", "age", "gender"],
            outputFields: ["BMI", "BMI category", "health recommendations"],
            formula: "BMI = weight (kg) / height (m)²",
            accuracy: "Standard WHO classification",
            targetAudience: ["health conscious individuals", "fitness enthusiasts", "medical professionals", "general public"],
            relatedCalculators: ["standard-calculator", "adjusted-calculator", "pediatric-calculator", "bmi-comparison"],
          },
        }}
        title={t("tools.bmi-calculator-hub.meta.title")}
      />
      <div className="light:bg-white dark:bg-base-200/20">
        {env.NEXT_PUBLIC_TOP_BMI_BANNER_AD_SLOT && <HorizontalTopBanner adSlot={env.NEXT_PUBLIC_TOP_BMI_BANNER_AD_SLOT} />}
        <div className="container mx-auto px-2 sm:px-4 py-4 sm:py-8 max-w-4xl">
          {/* Header */}
          <div className="text-center max-w-3xl mx-auto mb-8">
            <h1 className="text-3xl sm:text-4xl font-bold mb-4 text-base-content dark:text-base-content/90">
              {t("tools.bmi-calculator-hub.standard.page_title")}
            </h1>
            <p className="text-lg text-base-content/70 dark:text-base-content/60">
              {t("tools.bmi-calculator-hub.standard.page_description")}
            </p>
          </div>

          {/* Calculator */}
          <BmiCalculatorClient />

          {/* Educational Content */}
          <div className="mt-16">
            <BmiEducationalContent />
          </div>
        </div>
        {env.NEXT_PUBLIC_BOTTOM_BMI_BANNER_AD_SLOT && <HorizontalBottomBanner adSlot={env.NEXT_PUBLIC_BOTTOM_BMI_BANNER_AD_SLOT} />}
      </div>
    </>
  );
}


================================================
FILE: app/[locale]/(app)/tools/bmi-calculator/shared/BmiCalculatorClient.tsx
================================================
"use client";

import React, { useState, useEffect } from "react";

import { useI18n } from "locales/client";
import {
  BmiData,
  BmiResult,
  UnitSystem,
  calculateBmi,
  convertHeight,
  convertWeight,
} from "app/[locale]/(app)/tools/bmi-calculator/bmi-calculator.utils";

import { BmiWeightInput } from "./components/BmiWeightInput";
import { BmiUnitSelector } from "./components/BmiUnitSelector";
import { BmiResultsDisplay } from "./components/BmiResultsDisplay";
import { BmiHeightInput } from "./components/BmiHeightInput";

export function BmiCalculatorClient() {
  const t = useI18n();
  const [unit, setUnit] = useState<UnitSystem>("metric");
  const [height, setHeight] = useState<number>(170); // cm for metric, inches for imperial
  const [weight, setWeight] = useState<number>(70); // kg for metric, lbs for imperial
  const [result, setResult] = useState<BmiResult | null>(null);
  const [isInitialized, setIsInitialized] = useState<boolean>(false);

  // Convert values when unit system changes (but not on first render)
  useEffect(() => {
    if (!isInitialized) {
      setIsInitialized(true);
      return;
    }

    if (unit === "imperial") {
      // Convert from metric to imperial
      setHeight(Math.round(convertHeight(height, "metric", "imperial")));
      setWeight(Math.round(convertWeight(weight, "metric", "imperial") * 10) / 10);
    } else {
      // Convert from imperial to metric
      setHeight(Math.round(convertHeight(height, "imperial", "metric")));
      setWeight(Math.round(convertWeight(weight, "imperial", "metric") * 10) / 10);
    }
  }, [unit]);

  // Calculate BMI whenever inputs change
  useEffect(() => {
    if (height > 0 && weight > 0) {
      const bmiData: BmiData = { height, weight, unit };
      const bmiResult = calculateBmi(bmiData, t);
      setResult(bmiResult);
    } else {
      setResult(null);
    }
  }, [height, weight, unit]);

  return (
    <div className="space-y-8">
      {/* Input Form */}
      <div className="bg-base-100 dark:bg-base-200/30 rounded-2xl p-4 sm:p-8 border border-base-content/10">
        <div className="space-y-6">
          {/* Unit Selector */}
          <BmiUnitSelector onChange={setUnit} value={unit} />

          {/* Height and Weight Inputs */}
          <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
            <BmiHeightInput onChange={setHeight} unit={unit} value={height} />
            <BmiWeightInput onChange={setWeight} unit={unit} value={weight} />
          </div>
        </div>
      </div>

      {/* Results */}
      {result && (
        <div className="bg-base-100 dark:bg-base-200/30 rounded-2xl p-4 sm:p-8 border border-base-content/10">
          <BmiResultsDisplay result={result} />
        </div>
      )}
    </div>
  );
}


================================================
FILE: app/[locale]/(app)/tools/bmi-calculator/shared/components/BmiEducationalContent.tsx
================================================
"use client";

import { useI18n } from "locales/client";
import { env } from "@/env";
import { InArticle } from "@/components/ads";

import { FormulaCard, createFraction, createSuperscript } from "./MathEquation";

export function BmiEducationalContent() {
  const t = useI18n();

  return (
    <div className="space-y-12 max-w-4xl mx-auto">
      {/* BMI Introduction */}
      <section className="space-y-6">
        <h2 className="text-3xl font-bold text-base-content">{t("bmi-calculator.educational.introduction_title")}</h2>
        <div className="prose prose-lg max-w-none text-base-content/80">
          <p>{t("bmi-calculator.educational.introduction_text")}</p>
          <p>{t("bmi-calculator.educational.introduction_usage")}</p>
        </div>
      </section>

      {/* BMI Tables */}
      <section className="space-y-8">
        <div className="space-y-6">
          {env.NEXT_PUBLIC_IN_ARTICLE_BMI_1_AD_SLOT && <InArticle adSlot={env.NEXT_PUBLIC_IN_ARTICLE_BMI_1_AD_SLOT} />}
          <h2 className="text-3xl font-bold text-base-content">{t("bmi-calculator.educational.adult_table_title")}</h2>

          <p className="text-base-content/80">{t("bmi-calculator.educational.adult_table_description")}</p>

          {/* WHO Adult BMI Table */}
          <div className="overflow-x-auto">
            <table className="table table-zebra w-full bg-base-100">
              <thead>
                <tr className="bg-primary text-primary-content">
                  <th className="text-left">{t("bmi-calculator.educational.classification")}</th>
                  <th className="text-center">{t("bmi-calculator.educational.bmi_range")}</th>
                </tr>
              </thead>
              <tbody>
                <tr>
                  <td className="font-medium">{t("bmi-calculator.category_severe_thinness")}</td>
                  <td className="text-center font-mono">&lt; 16</td>
                </tr>
                <tr>
                  <td className="font-medium">{t("bmi-calculator.category_moderate_thinness")}</td>
                  <td className="text-center font-mono">16 - 17</td>
                </tr>
                <tr>
                  <td className="font-medium">{t("bmi-calculator.category_mild_thinness")}</td>
                  <td className="text-center font-mono">17 - 18.5</td>
                </tr>
                <tr className="bg-success/20">
                  <td className="font-medium">{t("bmi-calculator.category_normal")}</td>
                  <td className="text-center font-mono font-bold">18.5 - 25</td>
                </tr>
                <tr>
                  <td className="font-medium">{t("bmi-calculator.category_overweight")}</td>
                  <td className="text-center font-mono">25 - 30</td>
                </tr>
                <tr>
                  <td className="font-medium">{t("bmi-calculator.category_obese_class_1")}</td>
                  <td className="text-center font-mono">30 - 35</td>
                </tr>
                <tr>
                  <td className="font-medium">{t("bmi-calculator.category_obese_class_2")}</td>
                  <td className="text-center font-mono">35 - 40</td>
                </tr>
                <tr>
                  <td className="font-medium">{t("bmi-calculator.category_obese_class_3")}</td>
                  <td className="text-center font-mono">&gt; 40</td>
                </tr>
              </tbody>
            </table>
          </div>
        </div>

        {/* Children BMI Table */}
        <div className="space-y-6">
          <h2 className="text-3xl font-bold text-base-content">{t("bmi-calculator.educational.children_table_title")}</h2>
          <p className="text-base-content/80">{t("bmi-calculator.educational.children_table_description")}</p>

          <div className="overflow-x-auto">
            <table className="table table-zebra w-full bg-base-100">
              <thead>
                <tr className="bg-primary text-primary-content">
                  <th className="text-left">{t("bmi-calculator.educational.category")}</th>
                  <th className="text-center">{t("bmi-calculator.educational.percentile_range")}</th>
                </tr>
              </thead>
              <tbody>
                <tr>
                  <td className="font-medium">{t("bmi-calculator.educational.underweight")}</td>
                  <td className="text-center font-mono">&lt; 5%</td>
                </tr>
                <tr className="bg-success/20">
                  <td className="font-medium">{t("bmi-calculator.educational.healthy_weight")}</td>
                  <td className="text-center font-mono font-bold">5% - 85%</td>
                </tr>
                <tr>
                  <td className="font-medium">{t("bmi-calculator.educational.at_risk_overweight")}</td>
                  <td className="text-center font-mono">85% - 95%</td>
                </tr>
                <tr>
                  <td className="font-medium">{t("bmi-calculator.educational.overweight")}</td>
                  <td className="text-center font-mono">&gt; 95%</td>
                </tr>
              </tbody>
            </table>
          </div>
        </div>
      </section>

      {/* Health Risks */}
      <section className="space-y-8">
        <div className="space-y-6">
          <h2 className="text-3xl font-bold text-base-content">{t("bmi-calculator.educational.overweight_risks_title")}</h2>
          <p className="text-base-content/80">{t("bmi-calculator.educational.overweight_risks_intro")}</p>

          <div className="grid md:grid-cols-2 gap-4">
            <div className="space-y-3">
              <h4 className="text-lg font-semibold text-base-content">{t("bmi-calculator.educational.cardiovascular_risks")}</h4>
              <ul className="space-y-2 text-base-content/80">
                <li className="flex items-start gap-2">
                  <span className="text-error">•</span>
                  {t("bmi-calculator.educational.high_blood_pressure")}
                </li>
                <li className="flex items-start gap-2">
                  <span className="text-error">•</span>
                  {t("bmi-calculator.educational.cholesterol_issues")}
                </li>
                <li className="flex items-start gap-2">
                  <span className="text-error">•</span>
                  {t("bmi-calculator.educational.coronary_heart_disease")}
                </li>
                <li className="flex items-start gap-2">
                  <span className="text-error">•</span>
                  {t("bmi-calculator.educational.stroke")}
                </li>
              </ul>
            </div>

            <div className="space-y-3">
              <h4 className="text-lg font-semibold text-base-content">{t("bmi-calculator.educational.metabolic_risks")}</h4>
              <ul className="space-y-2 text-base-content/80">
                <li className="flex items-start gap-2">
                  <span className="text-error">•</span>
                  {t("bmi-calculator.educational.type_2_diabetes")}
                </li>
                <li className="flex items-start gap-2">
                  <span className="text-error">•</span>
                  {t("bmi-calculator.educational.gallbladder_disease")}
                </li>
                <li className="flex items-start gap-2">
                  <span className="text-error">•</span>
                  {t("bmi-calculator.educational.sleep_apnea")}
                </li>
                <li className="flex items-start gap-2">
                  <span className="text-error">•</span>
                  {t("bmi-calculator.educational.osteoarthritis")}
                </li>
              </ul>
            </div>
          </div>

          <div className="space-y-3">
            <h4 className="text-lg font-semibold text-base-content">{t("bmi-calculator.educational.other_risks")}</h4>
            <ul className="grid md:grid-cols-2 gap-2 text-base-content/80">
              <li className="flex items-start gap-2">
                <span className="text-error">•</span>
                {t("bmi-calculator.educational.certain_cancers")}
              </li>
              <li className="flex items-start gap-2">
                <span className="text-error">•</span>
                {t("bmi-calculator.educational.mental_health_issues")}
              </li>
              <li className="flex items-start gap-2">
                <span className="text-error">•</span>
                {t("bmi-calculator.educational.reduced_quality_life")}
              </li>
              <li className="flex items-start gap-2">
                <span className="text-error">•</span>
                {t("bmi-calculator.educational.increased_mortality")}
              </li>
            </ul>
          </div>
        </div>

        {/* Underweight Risks */}
        <div className="space-y-6">
          <h2 className="text-3xl font-bold text-base-content">{t("bmi-calculator.educational.underweight_risks_title")}</h2>
          <p className="text-base-content/80">{t("bmi-calculator.educational.underweight_risks_intro")}</p>

          <div className="grid md:grid-cols-2 gap-4">
            <ul className="space-y-2 text-base-content/80">
              <li className="flex items-start gap-2">
                <span className="text-warning mt-1">•</span>
                {t("bmi-calculator.educational.malnutrition")}
              </li>
              <li className="flex items-start gap-2">
                <span className="text-warning mt-1">•</span>
                {t("bmi-calculator.educational.osteoporosis")}
              </li>
              <li className="flex items-start gap-2">
                <span className="text-warning mt-1">•</span>
                {t("bmi-calculator.educational.immune_function_decrease")}
              </li>
              <li className="flex items-start gap-2">
                <span className="text-warning mt-1">•</span>
                {t("bmi-calculator.educational.growth_development_issues")}
              </li>
            </ul>
            <ul className="space-y-2 text-base-content/80">
              <li className="flex items-start gap-2">
                <span className="text-warning mt-1">•</span>
                {t("bmi-calculator.educational.reproductive_issues")}
              </li>
              <li className="flex items-start gap-2">
                <span className="text-warning mt-1">•</span>
                {t("bmi-calculator.educational.surgery_complications")}
              </li>
              <li className="flex items-start gap-2">
                <span className="text-warning mt-1">•</span>
                {t("bmi-calculator.educational.increased_mortality_underweight")}
              </li>
            </ul>
          </div>
        </div>
      </section>

      {/* BMI Limitations */}
      <section className="space-y-6">
        <h2 className="text-3xl font-bold text-base-content">{t("bmi-calculator.limitations_title")}</h2>
        <div className="prose prose-lg max-w-none text-base-content/80">
          <p>{t("bmi-calculator.limitations_text")}</p>
        </div>

        <div className="grid md:grid-cols-2 gap-6">
          <div className="space-y-4">
            <h4 className="text-lg font-semibold text-base-content">{t("bmi-calculator.educational.adults_limitations")}</h4>
            <ul className="space-y-2 text-base-content/80">
              <li className="flex items-start gap-2">
                <span className="text-info mt-1">•</span>
                {t("bmi-calculator.educational.older_adults_fat")}
              </li>
              <li className="flex items-start gap-2">
                <span className="text-info mt-1">•</span>
                {t("bmi-calculator.educational.women_fat_difference")}
              </li>
              <li className="flex items-start gap-2">
                <span className="text-info mt-1">•</span>
                {t("bmi-calculator.educational.athletes_muscle_mass")}
              </li>
            </ul>
          </div>

          <div className="space-y-4">
            <h4 className="text-lg font-semibold text-base-content">{t("bmi-calculator.educational.children_limitations")}</h4>
            <ul className="space-y-2 text-base-content/80">
              <li className="flex items-start gap-2">
                <span className="text-info mt-1">•</span>
                {t("bmi-calculator.educational.height_maturation_influence")}
              </li>
              <li className="flex items-start gap-2">
                <span className="text-info mt-1">•</span>
                {t("bmi-calculator.educational.fat_free_mass_difference")}
              </li>
              <li className="flex items-start gap-2">
                <span className="text-info mt-1">•</span>
                {t("bmi-calculator.educational.population_accuracy")}
              </li>
            </ul>
          </div>
        </div>
      </section>

      {/* BMI Formulas */}
      <section className="space-y-6">
        <h2 className="text-3xl font-bold text-base-content">{t("bmi-calculator.educational.formulas_title")}</h2>

        <div className="grid md:grid-cols-2 gap-8">
          {/* Metric Formula */}
          <FormulaCard
            description="Standard international formula using kilograms and meters"
            equation={`BMI = ${createFraction("weight (kg)", createSuperscript("height", "2") + " (m)")}`}
            example={`${createFraction("70", createSuperscript("1.75", "2"))} = 22.9`}
            title={t("bmi-calculator.educational.metric_formula")}
          />

          {/* Imperial Formula */}
          <FormulaCard
            description="US customary units formula with conversion factor"
            equation={`BMI = 703 × ${createFraction("weight (lbs)", createSuperscript("height", "2") + " (in)")}`}
            example={`703 × ${createFraction("154", createSuperscript("69", "2"))} = 22.9`}
            title={t("bmi-calculator.educational.imperial_formula")}
          />
        </div>
      </section>

      {env.NEXT_PUBLIC_IN_ARTICLE_BMI_2_AD_SLOT && <InArticle adSlot={env.NEXT_PUBLIC_IN_ARTICLE_BMI_2_AD_SLOT} />}

      {/* BMI Prime Section */}
      <section className="space-y-6">
        <h2 className="text-3xl font-bold text-base-content">{t("bmi-calculator.about_bmi_prime")}</h2>
        <div className="prose prose-lg max-w-none text-base-content/80">
          <p>{t("bmi-calculator.bmi_prime_explanation")}</p>
        </div>

        <FormulaCard
          description={t("bmi-calculator.educational.bmi_prime_description")}
          equation={`BMI Prime = ${createFraction("BMI", "25")}`}
          title={t("bmi-calculator.educational.bmi_prime_formula")}
        />

        {/* BMI Prime Table */}
        <div className="overflow-x-auto">
          <table className="table table-zebra w-full bg-base-100">
            <thead>
              <tr className="bg-primary text-primary-content">
                <th className="text-left">Classification</th>
                <th className="text-center">BMI</th>
                <th className="text-center">BMI Prime</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td className="font-medium">{t("bmi-calculator.category_severe_thinness")}</td>
                <td className="text-center font-mono">&lt; 16</td>
                <td className="text-center font-mono">&lt; 0.64</td>
              </tr>
              <tr>
                <td className="font-medium">{t("bmi-calculator.category_moderate_thinness")}</td>
                <td className="text-center font-mono">16 - 17</td>
                <td className="text-center font-mono">0.64 - 0.68</td>
              </tr>
              <tr>
                <td className="font-medium">{t("bmi-calculator.category_mild_thinness")}</td>
                <td className="text-center font-mono">17 - 18.5</td>
                <td className="text-center font-mono">0.68 - 0.74</td>
              </tr>
              <tr className="bg-success/20">
                <td className="font-medium">{t("bmi-calculator.category_normal")}</td>
                <td className="text-center font-mono font-bold">18.5 - 25</td>
                <td className="text-center font-mono font-bold">0.74 - 1.0</td>
              </tr>
              <tr>
                <td className="font-medium">{t("bmi-calculator.category_overweight")}</td>
                <td className="text-center font-mono">25 - 30</td>
                <td className="text-center font-mono">1.0 - 1.2</td>
              </tr>
              <tr>
                <td className="font-medium">{t("bmi-calculator.category_obese_class_1")}</td>
                <td className="text-center font-mono">30 - 35</td>
                <td className="text-center font-mono">1.2 - 1.4</td>
              </tr>
              <tr>
                <td className="font-medium">{t("bmi-calculator.category_obese_class_2")}</td>
                <td className="text-center font-mono">35 - 40</td>
                <td className="text-center font-mono">1.4 - 1.6</td>
              </tr>
              <tr>
                <td className="font-medium">{t("bmi-calculator.category_obese_class_3")}</td>
                <td className="text-center font-mono">&gt; 40</td>
                <td className="text-center font-mono">&gt; 1.6</td>
              </tr>
            </tbody>
          </table>
        </div>
      </section>

      {/* Ponderal Index */}
      <section className="space-y-6">
        <h2 className="text-3xl font-bold text-base-content">{t("bmi-calculator.educational.ponderal_index_title")}</h2>
        <div className="prose prose-lg max-w-none text-base-content/80">
          <p>{t("bmi-calculator.educational.ponderal_index_explanation")}</p>
        </div>

        <div className="grid md:grid-cols-2 gap-8">
          {/* Metric PI Formula */}
          <FormulaCard
            description={t("bmi-calculator.educational.ponderal_index_metric_description")}
            equation={`PI = ${createFraction("weight (kg)", createSuperscript("height", "3") + " (m)")}`}
            title={t("bmi-calculator.educational.metric_formula")}
          />

          {/* Imperial PI Formula */}
          <FormulaCard
            description={t("bmi-calculator.educational.ponderal_index_imperial_description")}
            equation={`PI = ${createFraction(createSuperscript("height", "3") + " (in)", "∛weight (lbs)")}`}
            title={t("bmi-calculator.educational.imperial_formula")}
          />
        </div>
      </section>

      {/* Disclaimer */}
      <section className="bg-warning/10 border border-warning/20 rounded-lg p-6">
        <div className="flex items-start gap-3">
          <div className="text-warning text-xl">⚠️</div>
          <div className="space-y-2">
            <h4 className="font-semibold text-base-content">{t("bmi-calculator.educational.medical_disclaimer_title")}</h4>
            <p className="text-base-content/80">{t("bmi-calculator.disclaimer")}</p>
          </div>
        </div>
      </section>
    </div>
  );
}


================================================
FILE: app/[locale]/(app)/tools/bmi-calculator/shared/components/BmiHeightInput.tsx
================================================
"use client";

import React from "react";

import { useI18n } from "locales/client";
import { UnitSystem } from "app/[locale]/(app)/tools/bmi-calculator/bmi-calculator.utils";

interface BmiHeightInputProps {
  value: number;
  unit: UnitSystem;
  onChange: (height: number) => void;
}

export function BmiHeightInput({ value, unit, onChange }: BmiHeightInputProps) {
  const t = useI18n();

  // For imperial, we need to handle feet and inches
  if (unit === "imperial") {
    const totalInches = value;
    const feet = Math.floor(totalInches / 12);
    const inches = totalInches % 12;

    const handleFeetChange = (newFeet: number) => {
      onChange(newFeet * 12 + inches);
    };

    const handleInchesChange = (newInches: number) => {
      onChange(feet * 12 + newInches);
    };

    return (
      <div>
        <label className="text-sm font-medium text-base-content/70 uppercase tracking-wider">{t("bmi-calculator.height")}</label>
        <div className="mt-2 grid grid-cols-2 gap-2">
          <div>
            <input
              className="w-full px-4 py-3 rounded-xl border-2 border-base-content/15 dark:border-base-content/10 light:bg-white dark:bg-base-200/30 text-base-content focus:border-primary focus:outline-none transition-all duration-300 hover:border-primary/30 text-center font-semibold"
              max="7"
              min="4"
              onChange={(e) => handleFeetChange(Number(e.target.value))}
              type="number"
              value={feet}
            />
            <span className="text-xs text-base-content/60 dark:text-base-content/50 mt-1 block text-center font-medium">
              {t("bmi-calculator.feet")}
            </span>
          </div>
          <div>
            <input
              className="w-full px-4 py-3 rounded-xl border-2 border-base-content/15 dark:border-base-content/10 light:bg-white dark:bg-base-200/30 text-base-content focus:border-primary focus:outline-none transition-all duration-300 hover:border-primary/30 text-center font-semibold"
              max="11"
              min="0"
              onChange={(e) => handleInchesChange(Number(e.target.value))}
              type="number"
              value={inches}
            />
            <span className="text-xs text-base-content/60 dark:text-base-content/50 mt-1 block text-center font-medium">
              {t("bmi-calculator.inches")}
            </span>
          </div>
        </div>
      </div>
    );
  }

  // Metric - simple cm input
  return (
    <div>
      <label className="text-sm font-bold text-base-content/80 dark:text-base-content/70 uppercase tracking-wider mb-3 block">
        {t("bmi-calculator.height")}
      </label>
      <div className="mt-2">
        <div className="relative">
          <input
            className="w-full px-4 py-3 pr-12 rounded-xl border-2 border-base-content/15 dark:border-base-content/10 light:bg-white dark:bg-base-200/30 text-base-content focus:border-primary focus:outline-none transition-all duration-300 hover:border-primary/30 font-semibold"
            max="250"
            min="100"
            onChange={(e) => onChange(Number(e.target.value))}
            placeholder={t("bmi-calculator.height_placeholder")}
            type="number"
            value={value}
          />
          <span className="absolute right-4 top-1/2 -translate-y-1/2 text-sm text-base-content/60 dark:text-base-content/50 font-medium">
            {t("bmi-calculator.cm")}
          </span>
        </div>
      </div>
    </div>
  );
}


================================================
FILE: app/[locale]/(app)/tools/bmi-calculator/shared/components/BmiResultsDisplay.tsx
================================================
"use client";

import React from "react";
import { CheckCircleIcon, AlertTriangleIcon, XCircleIcon, InfoIcon, TrendingUpIcon, TrendingDownIcon } from "lucide-react";

import { useI18n } from "locales/client";
import { BmiResult, BmiCategory, HealthRisk } from "app/[locale]/(app)/tools/bmi-calculator/bmi-calculator.utils";

interface BmiResultsDisplayProps {
  result: BmiResult;
}

export function BmiResultsDisplay({ result }: BmiResultsDisplayProps) {
  const t = useI18n();

  const getCategoryColor = (category: BmiCategory) => {
    switch (category) {
      case "severe_thinness":
        return "text-red-700 dark:text-red-500";
      case "moderate_thinness":
        return "text-red-600 dark:text-red-400";
      case "mild_thinness":
        return "text-blue-600 dark:text-blue-400";
      case "normal":
        return "text-green-600 dark:text-green-400";
      case "overweight":
        return "text-yellow-600 dark:text-yellow-400";
      case "obese_class_1":
        return "text-orange-600 dark:text-orange-400";
      case "obese_class_2":
        return "text-red-600 dark:text-red-400";
      case "obese_class_3":
        return "text-red-700 dark:text-red-500";
      default:
        return "text-gray-600 dark:text-gray-400";
    }
  };

  const getRiskColor = (risk: HealthRisk) => {
    switch (risk) {
      case "low":
      case "normal":
        return "text-green-600 dark:text-green-400";
      case "increased":
        return "text-yellow-600 dark:text-yellow-400";
      case "high":
        return "text-orange-600 dark:text-orange-400";
      case "very_high":
        return "text-red-600 dark:text-red-400";
      case "extremely_high":
        return "text-red-700 dark:text-red-500";
      default:
        return "text-gray-600 dark:text-gray-400";
    }
  };

  const getRiskIcon = (risk: HealthRisk) => {
    switch (risk) {
      case "low":
      case "normal":
        return <CheckCircleIcon className="w-5 h-5" />;
      case "increased":
      case "high":
        return <AlertTriangleIcon className="w-5 h-5" />;
      case "very_high":
      case "extremely_high":
        return <XCircleIcon className="w-5 h-5" />;
      default:
        return <CheckCircleIcon className="w-5 h-5" />;
    }
  };

  const getBmiGradient = (category: BmiCategory) => {
    switch (category) {
      case "severe_thinness":
        return "from-red-600 to-red-700";
      case "moderate_thinness":
        return "from-red-500 to-red-600";
      case "mild_thinness":
        return "from-blue-500 to-blue-600";
      case "normal":
        return "from-green-500 to-green-600";
      case "overweight":
        return "from-yellow-500 to-yellow-600";
      case "obese_class_1":
        return "from-orange-500 to-orange-600";
      case "obese_class_2":
        return "from-red-500 to-red-600";
      case "obese_class_3":
        return "from-red-600 to-red-700";
      default:
        return "from-gray-500 to-gray-600";
    }
  };

  return (
    <div className="space-y-6">
      {/* Main Results Grid */}
      <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
        {/* BMI Value */}
        <div className="text-center">
          <div
            className={`inline-flex items-center justify-center w-32 h-32 rounded-full bg-gradient-to-br ${getBmiGradient(result.category)} text-white shadow-lg`}
          >
            <div className="text-center">
              <div className="text-3xl font-bold">{result.bmi}</div>
              <div className="text-sm opacity-90">{t("bmi-calculator.your_bmi")}</div>
            </div>
          </div>
        </div>

        {/* BMI Prime */}
        <div className="text-center">
          <div className="inline-flex items-center justify-center w-32 h-32 rounded-full bg-gradient-to-br from-purple-500 to-purple-600 text-white shadow-lg">
            <div className="text-center">
              <div className="text-3xl font-bold">{result.bmiPrime}</div>
              <div className="text-sm opacity-90">{t("bmi-calculator.bmi_prime")}</div>
            </div>
          </div>
        </div>

        {/* Ponderal Index */}
        <div className="text-center">
          <div className="inline-flex items-center justify-center w-32 h-32 rounded-full bg-gradient-to-br from-indigo-500 to-indigo-600 text-white shadow-lg">
            <div className="text-center">
              <div className="text-3xl font-bold">{result.ponderalIndex}</div>
              <div className="text-sm opacity-90">{t("bmi-calculator.ponderal_index")}</div>
            </div>
          </div>
        </div>
      </div>

      {/* Category and Risk */}
      <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
        <div className="bg-base-100 dark:bg-base-200/30 rounded-2xl p-6 border border-base-content/10">
          <h3 className="text-lg font-semibold mb-3 text-base-content dark:text-base-content/90">{t("bmi-calculator.bmi_category")}</h3>
          <div className={`text-xl font-bold ${getCategoryColor(result.category)}`}>
            {t(`bmi-calculator.category_${result.category}` as keyof typeof t)}
          </div>
          <div className="text-sm text-base-content/60 mt-2">
            BMI: {result.detailedInfo.bmiRange.min} - {result.detailedInfo.bmiRange.max}
          </div>
        </div>

        <div className="bg-base-100 dark:bg-base-200/30 rounded-2xl p-6 border border-base-content/10">
          <h3 className="text-lg font-semibold mb-3 text-base-content dark:text-base-content/90">{t("bmi-calculator.health_risk")}</h3>
          <div className={`flex items-center gap-2 text-xl font-bold ${getRiskColor(result.healthRisk)}`}>
            {getRiskIcon(result.healthRisk)}
            {t(`bmi-calculator.risk_${result.healthRisk}` as keyof typeof t)}
          </div>
        </div>
      </div>

      {/* Ideal Weight and Weight Goals */}
      <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
        <div className="bg-base-100 dark:bg-base-200/30 rounded-2xl p-6 border border-base-content/10">
          <h3 className="text-lg font-semibold mb-3 text-base-content dark:text-base-content/90">{t("bmi-calculator.ideal_weight")}</h3>
          <div className="text-lg font-bold text-green-600 dark:text-green-400">
            {result.detailedInfo.idealWeight.min} - {result.detailedInfo.idealWeight.max} kg
          </div>
          <div className="text-sm text-base-content/60 mt-1">{t("bmi-calculator.normal_range")}</div>
        </div>

        {(result.detailedInfo.weightToLose || result.detailedInfo.weightToGain) && (
          <div className="bg-base-100 dark:bg-base-200/30 rounded-2xl p-6 border border-base-content/10">
            <h3 className="text-lg font-semibold mb-3 text-base-content dark:text-base-content/90">
              {result.detailedInfo.weightToLose ? t("bmi-calculator.weight_to_lose") : t("bmi-calculator.weight_to_gain")}
            </h3>
            <div
              className={`flex items-center gap-2 text-lg font-bold ${result.detailedInfo.weightToLose ? "text-red-600 dark:text-red-400" : "text-blue-600 dark:text-blue-400"}`}
            >
              {result.detailedInfo.weightToLose ? <TrendingDownIcon className="w-5 h-5" /> : <TrendingUpIcon className="w-5 h-5" />}
              {result.detailedInfo.weightToLose || result.detailedInfo.weightToGain} kg
            </div>
          </div>
        )}
      </div>

      {/* Detailed BMI Range Reference */}
      <div className="bg-gradient-to-br from-primary/5 to-primary/10 dark:from-primary/10 dark:to-primary/5 rounded-2xl p-6 border border-primary/20">
        <h3 className="text-lg font-semibold mb-3 text-base-content dark:text-base-content/90">
          {t("bmi-calculator.bmi_range")} (WHO Classification)
        </h3>
        <div className="space-y-2 text-sm">
          <div className="flex justify-between">
            <span className="text-red-700 dark:text-red-500">{t("bmi-calculator.category_severe_thinness")}</span>
            <span className="text-base-content/70">{"< 16"}</span>
          </div>
          <div className="flex justify-between">
            <span className="text-red-600 dark:text-red-400">{t("bmi-calculator.category_moderate_thinness")}</span>
            <span className="text-base-content/70">16.0 - 16.9</span>
          </div>
          <div className="flex justify-between">
            <span className="text-blue-600 dark:text-blue-400">{t("bmi-calculator.category_mild_thinness")}</span>
            <span className="text-base-content/70">17.0 - 18.4</span>
          </div>
          <div className="flex justify-between">
            <span className="text-green-600 dark:text-green-400">{t("bmi-calculator.category_normal")}</span>
            <span className="text-base-content/70">18.5 - 24.9</span>
          </div>
          <div className="flex justify-between">
            <span className="text-yellow-600 dark:text-yellow-400">{t("bmi-calculator.category_overweight")}</span>
            <span className="text-base-content/70">25.0 - 29.9</span>
          </div>
          <div className="flex justify-between">
            <span className="text-orange-600 dark:text-orange-400">{t("bmi-calculator.category_obese_class_1")}</span>
            <span className="text-base-content/70">30.0 - 34.9</span>
          </div>
          <div className="flex justify-between">
            <span className="text-red-600 dark:text-red-400">{t("bmi-calculator.category_obese_class_2")}</span>
            <span className="text-base-content/70">35.0 - 39.9</span>
          </div>
          <div className="flex justify-between">
            <span className="text-red-700 dark:text-red-500">{t("bmi-calculator.category_obese_class_3")}</span>
            <span className="text-base-content/70">{"≥ 40.0"}</span>
          </div>
        </div>
      </div>

      {/* BMI Prime Information */}
      <div className="bg-base-100 dark:bg-base-200/30 rounded-2xl p-6 border border-base-content/10">
        <h3 className="text-lg font-semibold mb-3 text-base-content dark:text-base-content/90">{t("bmi-calculator.about_bmi_prime")}</h3>
        <p className="text-sm text-base-content/70 dark:text-base-content/60 mb-3">{t("bmi-calculator.bmi_prime_explanation")}</p>
        <div className="grid grid-cols-2 md:grid-cols-4 gap-2 text-xs">
          <div className="text-center p-2 bg-base-200 dark:bg-base-300/20 rounded">
            <div className="font-semibold">{"< 0.74"}</div>
            <div className="text-blue-600">{t("bmi-calculator.underweight")}</div>
          </div>
          <div className="text-center p-2 bg-base-200 dark:bg-base-300/20 rounded">
            <div className="font-semibold">0.74 - 1.0</div>
            <div className="text-green-600">{t("bmi-calculator.normal")}</div>
          </div>
          <div className="text-center p-2 bg-base-200 dark:bg-base-300/20 rounded">
            <div className="font-semibold">1.0 - 1.2</div>
            <div className="text-yellow-600">{t("bmi-calculator.overweight")}</div>
          </div>
          <div className="text-center p-2 bg-base-200 dark:bg-base-300/20 rounded">
            <div className="font-semibold">{"> 1.2"}</div>
            <div className="text-red-600">{t("bmi-calculator.obese")}</div>
          </div>
        </div>
      </div>

      {/* Recommendations */}
      <div className="bg-base-100 dark:bg-base-200/30 rounded-2xl p-6 border border-base-content/10">
        <h3 className="text-lg font-semibold mb-4 text-base-content dark:text-base-content/90">
          {t("bmi-calculator.recommendations_label")}
        </h3>
        <ul className="space-y-2">
          {result.recommendations.map((recommendation, index) => (
            <li className="flex items-start gap-3 text-sm text-base-content/70 dark:text-base-content/60" key={index}>
              <CheckCircleIcon className="w-4 h-4 text-primary mt-0.5 flex-shrink-0" />
              <span>{recommendation}</span>
            </li>
          ))}
        </ul>
      </div>

      {/* BMI Limitations */}
      <div className="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-2xl p-4">
        <div className="flex items-start gap-3">
          <InfoIcon className="w-5 h-5 text-blue-600 dark:text-blue-400 mt-0.5 flex-shrink-0" />
          <div>
            <h4 className="font-semibold text-blue-800 dark:text-blue-200 mb-2">{t("bmi-calculator.limitations_title")}</h4>
            <p className="text-sm text-blue-800 dark:text-blue-200">{t("bmi-calculator.limitations_text")}</p>
          </div>
        </div>
      </div>

      {/* Disclaimer */}
      <div className="bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-2xl p-4">
        <div className="flex items-start gap-3">
          <AlertTriangleIcon className="w-5 h-5 text-yellow-600 dark:text-yellow-400 mt-0.5 flex-shrink-0" />
          <p className="text-sm text-yellow-800 dark:text-yellow-200">{t("bmi-calculator.disclaimer")}</p>
        </div>
      </div>
    </div>
  );
}


================================================
FILE: app/[locale]/(app)/tools/bmi-calculator/shared/components/BmiUnitSelector.tsx
================================================
"use client";

import React from "react";

import { useI18n } from "locales/client";
import { UnitSystem } from "app/[locale]/(app)/tools/bmi-calculator/bmi-calculator.utils";

interface BmiUnitSelectorProps {
  value: UnitSystem;
  onChange: (unit: UnitSystem) => void;
}

export function BmiUnitSelector({ value, onChange }: BmiUnitSelectorProps) {
  const t = useI18n();

  return (
    <div>
      <label className="text-sm font-bold text-base-content/80 dark:text-base-content/70 uppercase tracking-wider mb-3 block">
        {t("bmi-calculator.units")}
      </label>
      <div className="grid grid-cols-2 gap-3">
        <button
          className={`group flex items-center justify-center gap-2 sm:gap-3 py-3 sm:py-4 px-3 sm:px-4 rounded-2xl border-2 transition-all duration-300 touch-manipulation ${
            value === "metric"
              ? "border-[#4F8EF7] bg-gradient-to-br from-[#4F8EF7]/20 to-[#238BE6]/10 text-[#4F8EF7] dark:from-[#4F8EF7]/15 dark:to-[#238BE6]/5 scale-[1.02]"
              : "border-base-content/15 dark:border-base-content/10 hover:border-[#4F8EF7]/50 light:bg-white dark:bg-base-200/30 hover:bg-base-200/50 active:bg-base-200/70"
          }`}
          onClick={() => onChange("metric")}
        >
          <span className="text-sm font-semibold">{t("bmi-calculator.metric")}</span>
        </button>
        <button
          className={`group flex items-center justify-center gap-2 sm:gap-3 py-3 sm:py-4 px-3 sm:px-4 rounded-2xl border-2 transition-all duration-300 touch-manipulation ${
            value === "imperial"
              ? "border-[#4F8EF7] bg-gradient-to-br from-[#4F8EF7]/20 to-[#238BE6]/10 text-[#4F8EF7] dark:from-[#4F8EF7]/15 dark:to-[#238BE6]/5 scale-[1.02]"
              : "border-base-content/15 dark:border-base-content/10 hover:border-[#4F8EF7]/50 light:bg-white dark:bg-base-200/30 hover:bg-base-200/50 active:bg-base-200/70"
          }`}
          onClick={() => onChange("imperial")}
        >
          <span className="text-sm font-semibold">{t("bmi-calculator.imperial")}</span>
        </button>
      </div>
    </div>
  );
}


================================================
FILE: app/[locale]/(app)/tools/bmi-calculator/shared/components/BmiWeightInput.tsx
================================================
"use client";

import React from "react";

import { useI18n } from "locales/client";
import { UnitSystem } from "app/[locale]/(app)/tools/bmi-calculator/bmi-calculator.utils";

interface BmiWeightInputProps {
  value: number;
  unit: UnitSystem;
  onChange: (weight: number) => void;
}

export function BmiWeightInput({ value, unit, onChange }: BmiWeightInputProps) {
  const t = useI18n();
  const unitLabel = unit === "metric" ? t("bmi-calculator.kg") : t("bmi-calculator.lbs");
  const min = unit === "metric" ? 30 : 66;
  const max = unit === "metric" ? 300 : 660;

  return (
    <div>
      <label className="text-sm font-bold text-base-content/80 dark:text-base-content/70 uppercase tracking-wider mb-3 block">
        {t("bmi-calculator.weight")}
      </label>
      <div className="relative">
        <input
          className="w-full px-4 py-3 pr-12 rounded-xl border-2 border-base-content/15 dark:border-base-content/10 light:bg-white dark:bg-base-200/30 text-base-content focus:border-primary focus:outline-none transition-all duration-300 hover:border-primary/30 font-semibold"
          max={max}
          min={min}
          onChange={(e) => onChange(Number(e.target.value))}
          placeholder={t("bmi-calculator.weight_placeholder")}
          step="0.1"
          type="number"
          value={value}
        />
        <span className="absolute right-4 top-1/2 -translate-y-1/2 text-sm text-base-content/60 dark:text-base-content/50 font-medium">
          {unitLabel}
        </span>
      </div>
    </div>
  );
}


================================================
FILE: app/[locale]/(app)/tools/bmi-calculator/shared/components/MathEquation.tsx
================================================
"use client";

interface MathEquationProps {
  equation: string;
  display?: boolean;
  className?: string;
}

export function MathEquation({ equation, display = false, className = "" }: MathEquationProps) {
  return (
    <div className={`math-equation ${display ? "block" : "inline-block"} ${className}`}>
      <span
        className="font-mono text-lg"
        dangerouslySetInnerHTML={{ __html: equation }}
        style={{ fontFamily: "KaTeX_Math, Times New Roman, serif" }}
      />
    </div>
  );
}

interface FormulaCardProps {
  title: string;
  equation: string;
  example?: string;
  description?: string;
  className?: string;
}

export function FormulaCard({ title, equation, example, description, className = "" }: FormulaCardProps) {
  return (
    <div className={`bg-base-200 p-6 rounded-lg space-y-4 ${className}`}>
      <h4 className="text-lg font-semibold text-base-content text-center">{title}</h4>

      <div className="text-center py-4">
        <div className="text-xl font-mono" style={{ fontFamily: "KaTeX_Math, Times New Roman, serif" }}>
          <div dangerouslySetInnerHTML={{ __html: equation }} />
        </div>
      </div>

      {description && <p className="text-sm text-base-content/70 text-center">{description}</p>}

      {example && (
        <div className="text-center border-t border-base-content/10 pt-4">
          <p className="text-sm text-base-content/60 mb-2">Example:</p>
          <div className="text-lg font-mono" style={{ fontFamily: "KaTeX_Math, Times New Roman, serif" }}>
            <div dangerouslySetInnerHTML={{ __html: example }} />
          </div>
        </div>
      )}
    </div>
  );
}

// Helper function to create fraction notation
export function createFraction(numerator: string, denominator: string): string {
  return `
    <div style="display: inline-block; text-align: center; vertical-align: middle;">
      <div style="border-bottom: 1px solid currentColor; padding: 0 4px;">${numerator}</div>
      <div style="padding: 0 4px;">${denominator}</div>
    </div>
  `;
}

// Helper function for superscript
export function createSuperscript(base: string, exponent: string): string {
  return `${base}<sup style="font-size: 0.8em;">${exponent}</sup>`;
}

// Helper function for subscript
export function createSubscript(base: string, subscript: string): string {
  return `${base}<sub style="font-size: 0.8em;">${subscript}</sub>`;
}


================================================
FILE: app/[locale]/(app)/tools/calorie-calculator/CalorieCalculatorHub.tsx
================================================
"use client";

import React from "react";
import Link from "next/link";
import { TrendingUpIcon, AwardIcon, TargetIcon, BrainIcon, GlobeIcon, ChartBarIcon } from "lucide-react";

import { useI18n } from "locales/client";
import { env } from "@/env";
import { HorizontalBottomBanner, HorizontalTopBanner } from "@/components/ads";

interface CalculatorFormula {
  id: string;
  href: string;
  icon: React.ReactNode;
  year: string;
  popularity: number; // 1-5
  accuracy: "high" | "medium" | "good";
  bestFor: string;
  gradient: {
    from: string;
    to: string;
  };
}

const calculatorFormulas: CalculatorFormula[] = [
  {
    id: "mifflin-st-jeor",
    href: "/tools/calorie-calculator/mifflin-st-jeor-calculator",
    icon: <AwardIcon className="w-6 h-6" />,
    year: "1990",
    popularity: 5,
    accuracy: "high",
    bestFor: "general",
    gradient: {
      from: "from-[#4F8EF7]",
      to: "to-[#238BE6]",
    },
  },
  {
    id: "harris-benedict",
    href: "/tools/calorie-calculator/harris-benedict-calculator",
    icon: <TrendingUpIcon className="w-6 h-6" />,
    year: "1984",
    popularity: 5,
    accuracy: "good",
    bestFor: "traditional",
    gradient: {
      from: "from-[#25CB78]",
      to: "to-[#22C55E]",
    },
  },
  {
    id: "katch-mcardle",
    href: "/tools/calorie-calculator/katch-mcardle-calculator",
    icon: <TargetIcon className="w-6 h-6" />,
    year: "1996",
    popularity: 3,
    accuracy: "high",
    bestFor: "athletes",
    gradient: {
      from: "from-[#FF5722]",
      to: "to-[#EF4444]",
    },
  },
  {
    id: "cunningham",
    href: "/tools/calorie-calculator/cunningham-calculator",
    icon: <BrainIcon className="w-6 h-6" />,
    year: "1980",
    popularity: 2,
    accuracy: "high",
    bestFor: "bodybuilders",
    gradient: {
      from: "from-[#8B5CF6]",
      to: "to-[#7C3AED]",
    },
  },
  {
    id: "oxford",
    href: "/tools/calorie-calculator/oxford-calculator",
    icon: <GlobeIcon className="w-6 h-6" />,
    year: "2005",
    popularity: 3,
    accuracy: "good",
    bestFor: "european",
    gradient: {
      from: "from-[#F59E0B]",
      to: "to-[#EF4444]",
    },
  },
  {
    id: "comparison",
    href: "/tools/calorie-calculator/calorie-calculator-comparison",
    icon: <ChartBarIcon className="w-6 h-6" />,
    year: "all",
    popularity: 4,
    accuracy: "high",
    bestFor: "comparison",
    gradient: {
      from: "from-[#06B6D4]",
      to: "to-[#3B82F6]",
    },
  },
];

export function CalorieCalculatorHub() {
  const t = useI18n();

  const renderStars = (count: number) => {
    return Array.from({ length: 5 }, (_, i) => (
      <span className={i < count ? "text-yellow-500" : "text-base-content/20"} key={i}>
        ★
      </span>
    ));
  };

  const getAccuracyColor = (accuracy: string) => {
    switch (accuracy) {
      case "high":
        return "text-green-600 dark:text-green-400";
      case "good":
        return "text-blue-600 dark:text-blue-400";
      default:
        return "text-orange-600 dark:text-orange-400";
    }
  };

  return (
    <div className="space-y-8">
      {/* Introduction */}
      {env.NEXT_PUBLIC_TOP_CALCULATOR_HUB_BANNER_AD_SLOT && (
        <HorizontalTopBanner adSlot={env.NEXT_PUBLIC_TOP_CALCULATOR_HUB_BANNER_AD_SLOT} />
      )}
      <div className="text-center max-w-3xl mx-auto">
        <h1 className="text-3xl sm:text-4xl font-bold mb-4 text-base-content dark:text-base-content/90">
          {t("tools.calorie-calculator-hub.title")}
        </h1>
        <p className="text-lg text-base-content/70 dark:text-base-content/60">{t("tools.calorie-calculator-hub.subtitle")}</p>
      </div>

      {/* Calculator Cards */}
      <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
        {calculatorFormulas.map((formula) => (
          <Link
            className="group relative overflow-hidden rounded-2xl border border-base-content/10 bg-base-100 dark:bg-base-200/30 transition-all duration-300 hover:scale-[1.02] hover:border-primary/50"
            href={formula.href}
            key={formula.id}
          >
            <div
              className={`absolute inset-0 bg-gradient-to-br ${formula.gradient.from} ${formula.gradient.to} opacity-0 transition-opacity duration-300 group-hover:opacity-5`}
            />

            <div className="relative p-6">
              {/* Header */}
              <div className="flex items-start justify-between mb-4">
                <div className={`p-3 rounded-xl bg-gradient-to-br ${formula.gradient.from} ${formula.gradient.to} text-white`}>
                  {formula.icon}
                </div>
              </div>

              {/* Title */}
              <h3 className="text-xl font-bold mb-2 text-base-content dark:text-base-content/90">
                {t(`tools.calorie-calculator-hub.${formula.id}.title` as keyof typeof t)}
              </h3>

              {/* Year Badge */}
              <div className="inline-flex items-center gap-2 text-xs mb-3">
                <span className="px-2 py-1 rounded-full bg-base-200 dark:bg-base-300/50 text-base-content/60">
                  {formula.year === "all"
                    ? t("tools.calorie-calculator-hub.all_formulas")
                    : `${t("tools.calorie-calculator-hub.since")} ${formula.year}`}
                </span>
              </div>

              {/* Description */}
              <p className="text-sm text-base-content/70 dark:text-base-content/60 mb-4">
                {t(`tools.calorie-calculator-hub.${formula.id}.description` as keyof typeof t)}
              </p>

              {/* Stats */}
              <div className="space-y-2">
                <div className="flex items-center justify-between text-sm">
                  <span className="text-base-content/60">{t("tools.calorie-calculator-hub.popularity")}</span>
                  <span>{renderStars(formula.popularity)}</span>
                </div>
                <div className="flex items-center justify-between text-sm">
                  <span className="text-base-content/60">{t("tools.calorie-calculator-hub.accuracy")}</span>
                  <span className={`font-medium ${getAccuracyColor(formula.accuracy)}`}>
                    {t(`tools.calorie-calculator-hub.accuracy_${formula.accuracy}`)}
                  </span>
                </div>
                <div className="flex items-center justify-between text-sm">
                  <span className="text-base-content/60">{t("tools.calorie-calculator-hub.best_for")}</span>
                  <span className="text-xs font-medium text-primary">
                    {t(`tools.calorie-calculator-hub.best_for_${formula.bestFor}` as keyof typeof t)}
                  </span>
                </div>
              </div>

              {/* Best For Badge */}
            </div>
          </Link>
        ))}
      </div>

      {env.NEXT_PUBLIC_BOTTOM_CALCULATOR_HUB_BANNER_AD_SLOT && (
        <HorizontalBottomBanner adSlot={env.NEXT_PUBLIC_BOTTOM_CALCULATOR_HUB_BANNER_AD_SLOT} />
      )}

      {/* Info Section */}
      <div className="mt-12 bg-gradient-to-br from-primary/5 to-primary/10 dark:from-primary/10 dark:to-primary/5 rounded-2xl p-6 sm:p-8 border border-primary/20">
        <h2 className="text-2xl font-bold mb-4 text-base-content dark:text-base-content/90">
          {t("tools.calorie-calculator-hub.which_formula")}
        </h2>
        <div className="prose prose-sm dark:prose-invert max-w-none">
          <p className="text-base-content/70 dark:text-base-content/60">{t("tools.calorie-calculator-hub.formula_explanation")}</p>
          <ul className="mt-4 space-y-2 text-base-content/70 dark:text-base-content/60">
            <li>
              <strong className="text-base-content dark:text-base-content/90">
                {t("tools.calorie-calculator-hub.mifflin-st-jeor.title")}:
              </strong>{" "}
              {t("tools.calorie-calculator-hub.recommendation_general")}
            </li>
            <li>
              <strong className="text-base-content dark:text-base-content/90">
                {t("tools.calorie-calculator-hub.harris-benedict.title")}:
              </strong>{" "}
              {t("tools.calorie-calculator-hub.recommendation_traditional")}
            </li>
            <li>
              <strong className="text-base-content dark:text-base-content/90">
                {t("tools.calorie-calculator-hub.katch-mcardle.title")}:
              </strong>{" "}
              {t("tools.calorie-calculator-hub.recommendation_bodyfat")}
            </li>
          </ul>
        </div>
      </div>
    </div>
  );
}


================================================
FILE: app/[locale]/(app)/tools/calorie-calculator/calorie-calculator-comparison/CalorieCalculatorComparison.tsx
================================================
"use client";

import React, { useState } from "react";

import { useI18n } from "locales/client";
import { BodyFatInput } from "app/[locale]/(app)/tools/calorie-calculator/shared/components/BodyFatInput";
import {
  ActivityLevelSelector,
  AgeInput,
  GenderSelector,
  GoalSelector,
  HeightInput,
  UnitSelector,
  WeightInput,
} from "app/[locale]/(app)/tools/calorie-calculator/shared/components";
import {
  calculateCalories,
  CalorieCalculatorInputs,
  CalorieResults,
} from "app/[locale]/(app)/tools/calorie-calculator/shared/calorie-formulas.utils";
import { env } from "@/env";
import { HorizontalBottomBanner } from "@/components/ads";

interface FormulaResult {
  name: string;
  formula: "mifflin" | "harris" | "katch" | "cunningham" | "oxford";
  results: CalorieResults | null;
  error?: string;
  gradient: {
    from: string;
    to: string;
  };
}

export function CalorieCalculatorComparison() {
  const t = useI18n();

  const [inputs, setInputs] = useState<CalorieCalculatorInputs>({
    gender: "male",
    unit: "metric",
    age: 25,
    height: 170,
    weight: 70,
    activityLevel: "moderate",
    goal: "maintain",
    bodyFatPercentage: 15,
  });

  const [isCalculating, setIsCalculating] = useState(false);
  const [formulaResults, setFormulaResults] = useState<FormulaResult[]>([]);

  const formulas: FormulaResult[] = [
    {
      name: t("tools.calorie-calculator-hub.mifflin-st-jeor.title"),
      formula: "mifflin",
      results: null,
      gradient: { from: "from-[#4F8EF7]", to: "to-[#238BE6]" },
    },
    {
      name: t("tools.calorie-calculator-hub.harris-benedict.title"),
      formula: "harris",
      results: null,
      gradient: { from: "from-[#25CB78]", to: "to-[#22C55E]" },
    },
    {
      name: t("tools.calorie-calculator-hub.katch-mcardle.title"),
      formula: "katch",
      results: null,
      gradient: { from: "from-[#FF5722]", to: "to-[#EF4444]" },
    },
    {
      name: t("tools.calorie-calculator-hub.cunningham.title"),
      formula: "cunningham",
      results: null,
      gradient: { from: "from-[#8B5CF6]", to: "to-[#7C3AED]" },
    },
    {
      name: t("tools.calorie-calculator-hub.oxford.title"),
      formula: "oxford",
      results: null,
      g
Download .txt
gitextract_ajccn8ug/

├── .cursorrules
├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── config.yml
│   │   └── feature_request.md
│   ├── pull_request_template.md
│   └── workflows/
│       ├── ci.yml
│       ├── notify-discord-issues.yml
│       ├── notify-discord-pr.yml
│       ├── notify-discord.yml
│       └── publish-ghcr-image.yml
├── .gitignore
├── .npmrc
├── .prettierrc
├── .vscode/
│   └── settings.json
├── AGENTS.md
├── CLAUDE.md
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── app/
│   ├── [locale]/
│   │   ├── (admin)/
│   │   │   └── admin/
│   │   │       ├── [...catchAll]/
│   │   │       │   ├── not-found.tsx
│   │   │       │   └── page.tsx
│   │   │       ├── dashboard/
│   │   │       │   └── page.tsx
│   │   │       ├── layout.tsx
│   │   │       ├── not-found.tsx
│   │   │       ├── programs/
│   │   │       │   ├── [id]/
│   │   │       │   │   └── edit/
│   │   │       │   │       └── page.tsx
│   │   │       │   └── page.tsx
│   │   │       ├── settings/
│   │   │       │   └── page.tsx
│   │   │       └── users/
│   │   │           └── page.tsx
│   │   ├── (app)/
│   │   │   ├── (legal-and-payment)/
│   │   │   │   ├── layout.tsx
│   │   │   │   └── legal/
│   │   │   │       ├── privacy/
│   │   │   │       │   └── page.tsx
│   │   │   │       ├── sales-terms/
│   │   │   │       │   └── page.tsx
│   │   │   │       └── terms/
│   │   │   │           └── page.tsx
│   │   │   ├── [slug]/
│   │   │   │   └── layout.tsx
│   │   │   ├── about/
│   │   │   │   └── page.tsx
│   │   │   ├── auth/
│   │   │   │   ├── (auth-layout)/
│   │   │   │   │   ├── forgot-password/
│   │   │   │   │   │   └── page.tsx
│   │   │   │   │   ├── layout.tsx
│   │   │   │   │   ├── reset-password/
│   │   │   │   │   │   └── page.tsx
│   │   │   │   │   ├── signin/
│   │   │   │   │   │   └── page.tsx
│   │   │   │   │   └── signup/
│   │   │   │   │       └── page.tsx
│   │   │   │   ├── error/
│   │   │   │   │   └── page.tsx
│   │   │   │   ├── error.tsx
│   │   │   │   ├── layout.tsx
│   │   │   │   ├── signout/
│   │   │   │   │   └── page.tsx
│   │   │   │   ├── verify-email/
│   │   │   │   │   ├── layout.tsx
│   │   │   │   │   └── page.tsx
│   │   │   │   └── verify-request/
│   │   │   │       └── page.tsx
│   │   │   ├── layout.tsx
│   │   │   ├── leaderboard/
│   │   │   │   └── page.tsx
│   │   │   ├── onboarding/
│   │   │   │   ├── layout.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── page.tsx
│   │   │   ├── premium/
│   │   │   │   └── page.tsx
│   │   │   ├── profile/
│   │   │   │   └── page.tsx
│   │   │   ├── programs/
│   │   │   │   ├── [slug]/
│   │   │   │   │   ├── page.tsx
│   │   │   │   │   └── session/
│   │   │   │   │       └── [sessionSlug]/
│   │   │   │   │           ├── ProgramSessionClient.tsx
│   │   │   │   │           └── page.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── statistics/
│   │   │   │   └── page.tsx
│   │   │   └── tools/
│   │   │       ├── bmi-calculator/
│   │   │       │   ├── bmi-calculator.utils.ts
│   │   │       │   ├── page.tsx
│   │   │       │   └── shared/
│   │   │       │       ├── BmiCalculatorClient.tsx
│   │   │       │       └── components/
│   │   │       │           ├── BmiEducationalContent.tsx
│   │   │       │           ├── BmiHeightInput.tsx
│   │   │       │           ├── BmiResultsDisplay.tsx
│   │   │       │           ├── BmiUnitSelector.tsx
│   │   │       │           ├── BmiWeightInput.tsx
│   │   │       │           └── MathEquation.tsx
│   │   │       ├── calorie-calculator/
│   │   │       │   ├── CalorieCalculatorHub.tsx
│   │   │       │   ├── calorie-calculator-comparison/
│   │   │       │   │   ├── CalorieCalculatorComparison.tsx
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── calorie-calculator.utils.ts
│   │   │       │   ├── cunningham-calculator/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── harris-benedict-calculator/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── katch-mcardle-calculator/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── mifflin-st-jeor-calculator/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── oxford-calculator/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── page.tsx
│   │   │       │   ├── shared/
│   │   │       │   │   ├── CalorieCalculatorClient.tsx
│   │   │       │   │   ├── calculator-configs.ts
│   │   │       │   │   ├── calorie-formulas.utils.ts
│   │   │       │   │   ├── components/
│   │   │       │   │   │   ├── ActivityLevelSelector.tsx
│   │   │       │   │   │   ├── AgeInput.tsx
│   │   │       │   │   │   ├── BodyFatInput.tsx
│   │   │       │   │   │   ├── FAQSection.tsx
│   │   │       │   │   │   ├── GenderSelector.tsx
│   │   │       │   │   │   ├── GoalSelector.tsx
│   │   │       │   │   │   ├── HeightInput.tsx
│   │   │       │   │   │   ├── InfoButton.tsx
│   │   │       │   │   │   ├── InfoModal.tsx
│   │   │       │   │   │   ├── ResultsDisplay.tsx
│   │   │       │   │   │   ├── UnitSelector.tsx
│   │   │       │   │   │   ├── WeightInput.tsx
│   │   │       │   │   │   └── index.ts
│   │   │       │   │   └── types/
│   │   │       │   │       └── index.ts
│   │   │       │   └── styles.css
│   │   │       ├── heart-rate-zones/
│   │   │       │   ├── lib/
│   │   │       │   │   └── utils.ts
│   │   │       │   ├── page.tsx
│   │   │       │   ├── seo/
│   │   │       │   │   ├── config.ts
│   │   │       │   │   └── page-content.ts
│   │   │       │   └── ui/
│   │   │       │       ├── HeartRateZonesCalculatorClient.tsx
│   │   │       │       ├── components/
│   │   │       │       │   ├── EducationalContent.tsx
│   │   │       │       │   ├── EducationalContentServer.tsx
│   │   │       │       │   ├── FAQAccordion.tsx
│   │   │       │       │   ├── SEOOptimizedContentServer.tsx
│   │   │       │       │   └── ScrollToTopButton.tsx
│   │   │       │       └── styles.css
│   │   │       └── page.tsx
│   │   ├── @modal/
│   │   │   └── (.)auth/
│   │   │       └── login/
│   │   │           └── page.tsx
│   │   ├── layout.tsx
│   │   ├── manifest.json/
│   │   │   └── route.ts
│   │   ├── not-found.tsx
│   │   └── providers.tsx
│   ├── ads.txt/
│   │   └── route.ts
│   ├── api/
│   │   ├── analytics/
│   │   │   └── premium/
│   │   │       └── route.ts
│   │   ├── auth/
│   │   │   ├── [...all]/
│   │   │   │   └── route.ts
│   │   │   └── signup/
│   │   │       └── route.ts
│   │   ├── billing/
│   │   │   └── status/
│   │   │       └── route.ts
│   │   ├── exercises/
│   │   │   ├── [exerciseId]/
│   │   │   │   └── statistics/
│   │   │   │       ├── one-rep-max/
│   │   │   │       │   └── route.ts
│   │   │   │       ├── route.ts
│   │   │   │       ├── volume/
│   │   │   │       │   └── route.ts
│   │   │   │       └── weight-progression/
│   │   │   │           └── route.ts
│   │   │   ├── all/
│   │   │   │   └── route.ts
│   │   │   ├── route.ts
│   │   │   └── shuffle/
│   │   │       └── route.ts
│   │   ├── premium/
│   │   │   ├── billing-portal/
│   │   │   │   └── route.ts
│   │   │   ├── checkout/
│   │   │   │   └── route.ts
│   │   │   ├── plans/
│   │   │   │   └── route.ts
│   │   │   └── status/
│   │   │       └── route.ts
│   │   ├── programs/
│   │   │   ├── [slug]/
│   │   │   │   ├── enroll/
│   │   │   │   │   └── route.ts
│   │   │   │   ├── progress/
│   │   │   │   │   └── route.ts
│   │   │   │   ├── route.ts
│   │   │   │   └── sessions/
│   │   │   │       └── [sessionSlug]/
│   │   │   │           └── route.ts
│   │   │   ├── route.ts
│   │   │   └── session-progress/
│   │   │       ├── [progressId]/
│   │   │       │   └── complete/
│   │   │       │       └── route.ts
│   │   │       └── start/
│   │   │           └── route.ts
│   │   ├── revenuecat/
│   │   │   ├── link-user/
│   │   │   │   └── route.ts
│   │   │   ├── sync-status/
│   │   │   │   └── route.ts
│   │   │   └── webhook/
│   │   │       └── route.ts
│   │   ├── user/
│   │   │   ├── password/
│   │   │   │   └── route.ts
│   │   │   └── profile/
│   │   │       └── route.ts
│   │   ├── webhooks/
│   │   │   ├── revenuecat/
│   │   │   │   └── route.ts
│   │   │   └── stripe/
│   │   │       └── route.ts
│   │   └── workout-sessions/
│   │       ├── [sessionId]/
│   │       │   ├── feedback/
│   │       │   │   └── route.ts
│   │       │   ├── rating/
│   │       │   │   └── route.ts
│   │       │   ├── route.ts
│   │       │   └── summary/
│   │       │       └── route.ts
│   │       ├── sync/
│   │       │   └── route.ts
│   │       └── user/
│   │           └── [userId]/
│   │               └── route.ts
│   ├── robots.txt
│   └── sitemap.ts
├── components.json
├── content/
│   ├── about/
│   │   ├── en.mdx
│   │   ├── es.mdx
│   │   ├── fr.mdx
│   │   ├── pt.mdx
│   │   ├── ru.mdx
│   │   └── zh-CN.mdx
│   ├── privacy-policy/
│   │   ├── en.mdx
│   │   ├── es.mdx
│   │   ├── fr.mdx
│   │   ├── pt.mdx
│   │   ├── ru.mdx
│   │   └── zh-CN.mdx
│   ├── sales-terms/
│   │   ├── en.mdx
│   │   ├── es.mdx
│   │   ├── fr.mdx
│   │   ├── pt.mdx
│   │   ├── ru.mdx
│   │   └── zh-CN.mdx
│   └── terms/
│       ├── en.mdx
│       ├── es.mdx
│       ├── fr.mdx
│       ├── pt.mdx
│       ├── ru.mdx
│       └── zh-CN.mdx
├── data/
│   └── sample-exercises.csv
├── docker-compose.yml
├── docs/
│   └── SELF-HOSTING.md
├── emails/
│   ├── ContactSupportEmail.tsx
│   ├── DeleteAccountEmail.tsx
│   ├── ResetPasswordEmail.tsx
│   ├── VerifyEmail.tsx
│   └── utils/
│       └── BaseEmailLayout.tsx
├── eslint.config.mjs
├── locales/
│   ├── client.ts
│   ├── en.ts
│   ├── es.ts
│   ├── fr.ts
│   ├── heart-rate-zones-translations.ts
│   ├── pt.ts
│   ├── ru.ts
│   ├── server.ts
│   ├── types.ts
│   └── zh-CN.ts
├── middleware.ts
├── next.config.ts
├── nextauth.d.ts
├── package.json
├── postcss.config.mjs
├── prisma/
│   ├── migrations/
│   │   └── 0_init/
│   │       └── migration.sql
│   ├── migrations_backup/
│   │   ├── 20240726_simplify_subscription_model/
│   │   │   └── migration.sql
│   │   ├── 20250101000000_baseline/
│   │   │   └── migration.sql
│   │   ├── 20250117000000_add_statistics_indexes/
│   │   │   └── migration.sql
│   │   ├── 20250414120436_init/
│   │   │   └── migration.sql
│   │   ├── 20250414170807_add_feedbacks/
│   │   │   └── migration.sql
│   │   ├── 20250414174246_rename_feedbacks/
│   │   │   └── migration.sql
│   │   ├── 20250414232816_add_first_name_and_last_name/
│   │   │   └── migration.sql
│   │   ├── 20250416160303_add_plans/
│   │   │   └── migration.sql
│   │   ├── 20250416160502_map/
│   │   │   └── migration.sql
│   │   ├── 20250505114841_add_user_role/
│   │   │   └── migration.sql
│   │   ├── 20250505191954_admin_and_user_lowercase/
│   │   │   └── migration.sql
│   │   ├── 20250610182024_add_exercises_and_attributes/
│   │   │   └── migration.sql
│   │   ├── 20250610182815_add_exercise_enums/
│   │   │   └── migration.sql
│   │   ├── 20250610184725_simplified_exercises/
│   │   │   └── migration.sql
│   │   ├── 20250611190228_convert_text_to_enums/
│   │   │   └── migration.sql
│   │   ├── 20250611210106_add_enum_values/
│   │   │   └── migration.sql
│   │   ├── 20250612213546_workout_session_sets/
│   │   │   └── migration.sql
│   │   ├── 20250613095031_add_multi_column_support/
│   │   │   └── migration.sql
│   │   ├── 20250614125347_add_table_maps/
│   │   │   └── migration.sql
│   │   ├── 20250614153656_remove_value_int_value_sec_unit_from_workoutset/
│   │   │   └── migration.sql
│   │   ├── 20250615160343_add_muscle_to_a_workout_session/
│   │   │   └── migration.sql
│   │   ├── 20250615170916_add_cascade_delete_workout_sessions/
│   │   │   └── migration.sql
│   │   ├── 20250623142458_add_billing_and_subscriptions/
│   │   │   └── migration.sql
│   │   ├── 20250623143952_remove_webhook_events/
│   │   │   └── migration.sql
│   │   ├── 20250623144324_add_webhook_events/
│   │   │   └── migration.sql
│   │   ├── 20250625155932_add_admin/
│   │   │   └── migration.sql
│   │   ├── 20250625195907_add_program_visibility/
│   │   │   └── migration.sql
│   │   ├── 20250626102058_add_i18n_slugs_on_program/
│   │   │   └── migration.sql
│   │   ├── 20250626134345_remove_emoji_on_program/
│   │   │   └── migration.sql
│   │   ├── 20250626182857_cleanup_billing_system/
│   │   │   └── migration.sql
│   │   ├── 20250626204136_remove_payment_table/
│   │   │   └── migration.sql
│   │   ├── 20250626205121_remove_legacy_premium_fields/
│   │   │   └── migration.sql
│   │   ├── 20250626205904_remove_payment_table_keep_ispremium/
│   │   │   └── migration.sql
│   │   ├── 20250707114920_add_user_favorite_exercises/
│   │   │   └── migration.sql
│   │   ├── 20250708214116_add_rating_to_wkt_sessions/
│   │   │   └── migration.sql
│   │   ├── 20250709_add_revenuecat_fields/
│   │   │   └── migration.sql
│   │   └── migration_lock.toml
│   └── schema.prisma
├── public/
│   ├── _ads.txt
│   ├── manifest.json
│   └── sw.js
├── scripts/
│   ├── check-pricing-config.ts
│   ├── import-exercises-with-attributes.prompt.md
│   ├── import-exercises-with-attributes.ts
│   ├── seed-leaderboard-data.ts
│   ├── seed-multi-region-plans.ts
│   ├── seed-subscription-plans-simple.ts
│   ├── seed-workout-data-advanced.ts
│   └── setup.sh
├── src/
│   ├── components/
│   │   ├── ads/
│   │   │   ├── AdBlockerForPremium.tsx
│   │   │   ├── AdPlaceholder.tsx
│   │   │   ├── AdSenseAutoAds.tsx
│   │   │   ├── AdWrapper.tsx
│   │   │   ├── EzoicAd.tsx
│   │   │   ├── GoogleAdSense.tsx
│   │   │   ├── HorizontalAdBanner.tsx
│   │   │   ├── HorizontalBottomBanner.tsx
│   │   │   ├── HorizontalTopBanner.tsx
│   │   │   ├── InArticle.tsx
│   │   │   ├── ResponsiveAdBanner.tsx
│   │   │   ├── VerticalAdBanner.tsx
│   │   │   ├── VerticalLeftBanner.tsx
│   │   │   ├── VerticalRightBanner.tsx
│   │   │   ├── index.ts
│   │   │   └── nutripure-affiliate-banner.tsx
│   │   ├── premium/
│   │   │   └── RemoveAdsText.tsx
│   │   ├── pwa/
│   │   │   └── ServiceWorkerRegistration.tsx
│   │   ├── seo/
│   │   │   ├── SEOHead.tsx
│   │   │   ├── breadcrumbs.tsx
│   │   │   ├── duration-badge.tsx
│   │   │   ├── rich-snippet-rating.tsx
│   │   │   └── session-rich-snippets.tsx
│   │   ├── svg/
│   │   │   ├── BrokenLink.tsx
│   │   │   ├── Calendly.tsx
│   │   │   ├── CircleSvg.tsx
│   │   │   ├── DiscordSvg.tsx
│   │   │   ├── DotPattern.tsx
│   │   │   ├── GoogleSvg.tsx
│   │   │   ├── IconCheckboxCheck.tsx
│   │   │   ├── LogoSvg.tsx
│   │   │   ├── UnderlineSvg.tsx
│   │   │   ├── VerifiedBadge.tsx
│   │   │   └── Youtube.tsx
│   │   ├── ui/
│   │   │   ├── 404-page-not-found.tsx
│   │   │   ├── Bento.tsx
│   │   │   ├── ToastSonner.tsx
│   │   │   ├── accordion.tsx
│   │   │   ├── alert-dialog.tsx
│   │   │   ├── alert.tsx
│   │   │   ├── animated-button/
│   │   │   │   └── ShinyButton.tsx
│   │   │   ├── aspect-ratio.tsx
│   │   │   ├── avatar.tsx
│   │   │   ├── badge.tsx
│   │   │   ├── bottom-sheet-vaul.tsx
│   │   │   ├── bottom-sheet.tsx
│   │   │   ├── button.tsx
│   │   │   ├── card-styled.tsx
│   │   │   ├── card.tsx
│   │   │   ├── collapsible.tsx
│   │   │   ├── dialog-stack.tsx
│   │   │   ├── dialog.tsx
│   │   │   ├── divider.tsx
│   │   │   ├── donation-alert.tsx
│   │   │   ├── dropdown-menu.tsx
│   │   │   ├── form.tsx
│   │   │   ├── hover-card.tsx
│   │   │   ├── input-password-strength.tsx
│   │   │   ├── input.tsx
│   │   │   ├── iphone-mockup.tsx
│   │   │   ├── label.tsx
│   │   │   ├── link.tsx
│   │   │   ├── loader.tsx
│   │   │   ├── local-alert.tsx
│   │   │   ├── moving-border.tsx
│   │   │   ├── navigation-menu.tsx
│   │   │   ├── next-top-loader.tsx
│   │   │   ├── pagination.tsx
│   │   │   ├── phone-frame-preview.tsx
│   │   │   ├── popover.tsx
│   │   │   ├── premium-gate.tsx
│   │   │   ├── premium-upsell-alert.tsx
│   │   │   ├── radio-group.tsx
│   │   │   ├── scroll-area.tsx
│   │   │   ├── select.tsx
│   │   │   ├── separator.tsx
│   │   │   ├── sheet.tsx
│   │   │   ├── shine-border.tsx
│   │   │   ├── simple-select.tsx
│   │   │   ├── skeleton.tsx
│   │   │   ├── slider.tsx
│   │   │   ├── sonner.tsx
│   │   │   ├── star-button.tsx
│   │   │   ├── switch.tsx
│   │   │   ├── table.tsx
│   │   │   ├── tabs.tsx
│   │   │   ├── textarea.tsx
│   │   │   ├── theme-provider.tsx
│   │   │   ├── timer.tsx
│   │   │   ├── title-with-dot.tsx
│   │   │   ├── toast.tsx
│   │   │   ├── toaster.tsx
│   │   │   ├── tooltip.tsx
│   │   │   ├── typography.tsx
│   │   │   ├── use-toast.ts
│   │   │   └── workout-lol.tsx
│   │   ├── utils/
│   │   │   ├── ErrorBoundaries.tsx
│   │   │   └── TailwindIndicator.tsx
│   │   └── version.tsx
│   ├── entities/
│   │   ├── exercise/
│   │   │   ├── shared/
│   │   │   │   └── muscles.tsx
│   │   │   └── types/
│   │   │       └── exercise.types.ts
│   │   ├── program/
│   │   │   └── types/
│   │   │       └── program.types.ts
│   │   ├── program-session/
│   │   │   └── types/
│   │   │       └── program-session.types.ts
│   │   └── user/
│   │       ├── lib/
│   │       │   └── display-name.ts
│   │       ├── model/
│   │       │   ├── get-server-session-user.ts
│   │       │   ├── get-users.actions.ts
│   │       │   ├── update-user-locale.ts
│   │       │   ├── update-user.action.ts
│   │       │   ├── use-auto-locale.ts
│   │       │   ├── useCurrentSession.ts
│   │       │   └── useCurrentUser.ts
│   │       ├── schemas/
│   │       │   ├── get-user.schema.ts
│   │       │   └── update-user.schema.ts
│   │       └── types/
│   │           └── session-user.ts
│   ├── env.ts
│   ├── features/
│   │   ├── admin/
│   │   │   ├── layout/
│   │   │   │   ├── admin-header.tsx
│   │   │   │   └── admin-sidebar/
│   │   │   │       └── ui/
│   │   │   │           ├── admin-header.tsx
│   │   │   │           └── admin-sidebar.tsx
│   │   │   ├── programs/
│   │   │   │   ├── actions/
│   │   │   │   │   ├── add-exercise.action.ts
│   │   │   │   │   ├── add-session.action.ts
│   │   │   │   │   ├── add-week.action.ts
│   │   │   │   │   ├── create-program.action.ts
│   │   │   │   │   ├── delete-program.action.ts
│   │   │   │   │   ├── get-programs.action.ts
│   │   │   │   │   ├── update-exercise-sets.action.ts
│   │   │   │   │   ├── update-program-visibility.action.ts
│   │   │   │   │   ├── update-program.action.ts
│   │   │   │   │   ├── update-session.action.ts
│   │   │   │   │   └── update-week.action.ts
│   │   │   │   ├── types/
│   │   │   │   │   └── program.types.ts
│   │   │   │   └── ui/
│   │   │   │       ├── add-exercise-modal.tsx
│   │   │   │       ├── add-session-modal.tsx
│   │   │   │       ├── add-week-modal.tsx
│   │   │   │       ├── create-program-button.tsx
│   │   │   │       ├── create-program-form.tsx
│   │   │   │       ├── create-program-modal.tsx
│   │   │   │       ├── delete-program-button.tsx
│   │   │   │       ├── edit-program-modal.tsx
│   │   │   │       ├── edit-session-modal.tsx
│   │   │   │       ├── edit-sets-modal.tsx
│   │   │   │       ├── edit-week-modal.tsx
│   │   │   │       ├── program-builder.tsx
│   │   │   │       ├── programs-list.tsx
│   │   │   │       ├── session-card.tsx
│   │   │   │       ├── visibility-badge.tsx
│   │   │   │       └── week-card.tsx
│   │   │   └── users/
│   │   │       └── list/
│   │   │           └── ui/
│   │   │               └── users-table.tsx
│   │   ├── ads/
│   │   │   └── hooks/
│   │   │       └── useUserSubscription.ts
│   │   ├── auth/
│   │   │   ├── forgot-password/
│   │   │   │   ├── forgot-password.schema.ts
│   │   │   │   ├── model/
│   │   │   │   │   └── useForgotPassword.tsx
│   │   │   │   └── ui/
│   │   │   │       └── forgot-password-form.tsx
│   │   │   ├── lib/
│   │   │   │   ├── auth-client.ts
│   │   │   │   └── better-auth.ts
│   │   │   ├── model/
│   │   │   │   └── useLogout.ts
│   │   │   ├── reset-password/
│   │   │   │   ├── model/
│   │   │   │   │   └── useResetPassword.ts
│   │   │   │   ├── schema/
│   │   │   │   │   └── reset-password.schema.ts
│   │   │   │   └── ui/
│   │   │   │       └── reset-password-form.tsx
│   │   │   ├── signin/
│   │   │   │   ├── model/
│   │   │   │   │   └── useSignIn.ts
│   │   │   │   ├── schema/
│   │   │   │   │   └── signin.schema.ts
│   │   │   │   └── ui/
│   │   │   │       └── CredentialsLoginForm.tsx
│   │   │   ├── signup/
│   │   │   │   ├── model/
│   │   │   │   │   ├── signup.action.ts
│   │   │   │   │   └── useSignUp.ts
│   │   │   │   ├── schema/
│   │   │   │   │   └── signup.schema.ts
│   │   │   │   └── ui/
│   │   │   │       └── signup-form.tsx
│   │   │   ├── ui/
│   │   │   │   ├── AuthButtonServer.tsx
│   │   │   │   ├── LoggedInButton.tsx
│   │   │   │   ├── ProviderButton.tsx
│   │   │   │   ├── SignInButton.tsx
│   │   │   │   └── SignUpButton.tsx
│   │   │   └── verify-email/
│   │   │       ├── constants.ts
│   │   │       ├── model/
│   │   │       │   └── useResendEmail.ts
│   │   │       └── ui/
│   │   │           └── verify-email-page.tsx
│   │   ├── consent-banner/
│   │   │   ├── model/
│   │   │   │   └── tracking-consent.action.ts
│   │   │   ├── schema/
│   │   │   │   └── tracking-consent.schema.ts
│   │   │   └── ui/
│   │   │       └── consent-banner.tsx
│   │   ├── contact/
│   │   │   └── support/
│   │   │       ├── ContactSupportDialog.tsx
│   │   │       ├── contact-support.action.ts
│   │   │       └── contact-support.schema.ts
│   │   ├── contact-feedback/
│   │   │   ├── model/
│   │   │   │   ├── contact-feedback.action.ts
│   │   │   │   └── contact-feedback.schema.ts
│   │   │   └── ui/
│   │   │       ├── ReviewInput.tsx
│   │   │       └── contact-feedback-popover.tsx
│   │   ├── dialogs-provider/
│   │   │   ├── DialogProvider.tsx
│   │   │   └── DialogProviderDialog.tsx
│   │   ├── email/
│   │   │   ├── EmailForm.tsx
│   │   │   ├── email.action.ts
│   │   │   └── email.schema.ts
│   │   ├── form/
│   │   │   └── SubmitButton.tsx
│   │   ├── layout/
│   │   │   ├── BottomNavigation.tsx
│   │   │   ├── Footer.tsx
│   │   │   ├── Header.tsx
│   │   │   ├── model/
│   │   │   │   └── use-sidebar.store.tsx
│   │   │   ├── nav-link.tsx
│   │   │   ├── page-heading.tsx
│   │   │   ├── useSidebarToggle.ts
│   │   │   └── workout-streak-header.tsx
│   │   ├── leaderboard/
│   │   │   ├── actions/
│   │   │   │   ├── get-top-workout-users.action.ts
│   │   │   │   └── get-user-position.action.ts
│   │   │   ├── hooks/
│   │   │   │   ├── use-top-workout-users.ts
│   │   │   │   └── use-user-position.ts
│   │   │   ├── lib/
│   │   │   │   └── utils.ts
│   │   │   ├── models/
│   │   │   │   └── types.ts
│   │   │   └── ui/
│   │   │       ├── leaderboard-item.tsx
│   │   │       ├── leaderboard-page.tsx
│   │   │       ├── leaderboard-skeleton.tsx
│   │   │       └── user-leaderboard-position.tsx
│   │   ├── page/
│   │   │   └── layout.tsx
│   │   ├── premium/
│   │   │   └── ui/
│   │   │       ├── README.md
│   │   │       ├── conversion-flow-notification.tsx
│   │   │       ├── feature-comparison-table.tsx
│   │   │       ├── index.ts
│   │   │       ├── premium-upgrade-card.tsx
│   │   │       ├── pricing-faq.tsx
│   │   │       ├── pricing-hero-section.tsx
│   │   │       └── pricing-testimonials.tsx
│   │   ├── programs/
│   │   │   ├── actions/
│   │   │   │   ├── complete-program-session.action.ts
│   │   │   │   ├── enroll-program.action.ts
│   │   │   │   ├── get-program-by-slug.action.ts
│   │   │   │   ├── get-program-progress-by-slug.action.ts
│   │   │   │   ├── get-program-progress.action.ts
│   │   │   │   ├── get-public-programs.action.ts
│   │   │   │   ├── get-session-by-slug.action.ts
│   │   │   │   ├── get-sitemap-data.action.ts
│   │   │   │   └── start-program-session.action.ts
│   │   │   ├── hooks/
│   │   │   │   └── use-program-share.ts
│   │   │   ├── lib/
│   │   │   │   ├── program-metadata.ts
│   │   │   │   ├── session-metadata.ts
│   │   │   │   ├── suggested-sets-helpers.ts
│   │   │   │   └── translations-mapper.ts
│   │   │   └── ui/
│   │   │       ├── program-card.tsx
│   │   │       ├── program-detail-page.tsx
│   │   │       ├── program-progress.tsx
│   │   │       ├── programs-page.tsx
│   │   │       ├── session-access-guard.tsx
│   │   │       ├── share-button.tsx
│   │   │       └── welcome-modal.tsx
│   │   ├── release-notes/
│   │   │   ├── hooks/
│   │   │   │   ├── index.ts
│   │   │   │   └── use-changelog-notification.ts
│   │   │   ├── index.ts
│   │   │   ├── lib/
│   │   │   │   └── date-utils.ts
│   │   │   ├── model/
│   │   │   │   ├── changelog-notification.local.ts
│   │   │   │   └── notes.ts
│   │   │   ├── types/
│   │   │   │   └── notification.ts
│   │   │   └── ui/
│   │   │       ├── changelog-notification-badge.tsx
│   │   │       ├── index.ts
│   │   │       └── release-notes-dialog.tsx
│   │   ├── statistics/
│   │   │   ├── components/
│   │   │   │   ├── ExerciseSelection.tsx
│   │   │   │   ├── ExerciseStatisticsTab.tsx
│   │   │   │   ├── ExercisesBrowser.tsx
│   │   │   │   ├── OneRepMaxChart.tsx
│   │   │   │   ├── StatisticsPreviewOverlay.tsx
│   │   │   │   ├── TimeframeSelector.tsx
│   │   │   │   ├── VolumeChart.tsx
│   │   │   │   ├── WeightProgressionChart.tsx
│   │   │   │   └── index.ts
│   │   │   ├── hooks/
│   │   │   │   ├── use-chart-theme.ts
│   │   │   │   └── use-exercise-statistics.ts
│   │   │   └── types/
│   │   │       └── index.ts
│   │   ├── theme/
│   │   │   ├── ThemeProviders.tsx
│   │   │   ├── ThemeToggle.tsx
│   │   │   └── ui/
│   │   │       └── ThemeSynchronizer.tsx
│   │   ├── update-password/
│   │   │   ├── lib/
│   │   │   │   ├── hash.ts
│   │   │   │   └── validate-password.ts
│   │   │   ├── model/
│   │   │   │   ├── update-password.action.ts
│   │   │   │   └── update-password.schema.ts
│   │   │   └── ui/
│   │   │       └── password-form.tsx
│   │   ├── user/
│   │   │   └── ui/
│   │   │       └── UserDropdown.tsx
│   │   ├── workout-builder/
│   │   │   ├── actions/
│   │   │   │   ├── get-exercises-by-muscle.action.ts
│   │   │   │   ├── get-exercises.action.ts
│   │   │   │   ├── get-favorite-exercises.action.ts
│   │   │   │   ├── pick-exercise.action.ts
│   │   │   │   ├── shuffle-exercise.action.ts
│   │   │   │   └── sync-favorite-exercises.action.ts
│   │   │   ├── hooks/
│   │   │   │   ├── use-exercises.ts
│   │   │   │   ├── use-favorite-exercises.service.ts
│   │   │   │   ├── use-favorites-modal.ts
│   │   │   │   ├── use-sync-favorite-exercises.ts
│   │   │   │   ├── use-workout-session.ts
│   │   │   │   └── use-workout-stepper.ts
│   │   │   ├── index.ts
│   │   │   ├── model/
│   │   │   │   ├── equipment-config.ts
│   │   │   │   ├── favorite-exercises-synchronizer.tsx
│   │   │   │   ├── favorite-exercises.local.ts
│   │   │   │   └── workout-builder.store.ts
│   │   │   ├── schema/
│   │   │   │   └── get-exercises.schema.ts
│   │   │   ├── types/
│   │   │   │   └── index.ts
│   │   │   └── ui/
│   │   │       ├── add-exercise-modal.tsx
│   │   │       ├── equipment-selection.tsx
│   │   │       ├── exercise-card.tsx
│   │   │       ├── exercise-list-item.tsx
│   │   │       ├── exercise-pick-modal.tsx
│   │   │       ├── exercise-video-modal.tsx
│   │   │       ├── exercises-selection.tsx
│   │   │       ├── favorite-button.tsx
│   │   │       ├── favorite-exercise-button.tsx
│   │   │       ├── muscle-selection.tsx
│   │   │       ├── muscles/
│   │   │       │   ├── abdominals-group.tsx
│   │   │       │   ├── back-group.tsx
│   │   │       │   ├── biceps-group.tsx
│   │   │       │   ├── calves-group.tsx
│   │   │       │   ├── chest-group.tsx
│   │   │       │   ├── forearms-group.tsx
│   │   │       │   ├── glutes-group.tsx
│   │   │       │   ├── hamstrings-group.tsx
│   │   │       │   ├── obliques-group.tsx
│   │   │       │   ├── quadriceps-group.tsx
│   │   │       │   ├── shoulders-group.tsx
│   │   │       │   ├── traps-group.tsx
│   │   │       │   └── triceps-group.tsx
│   │   │       ├── muscles.module.css
│   │   │       ├── quit-workout-dialog.tsx
│   │   │       ├── stepper-header.tsx
│   │   │       ├── workout-stepper-footer.tsx
│   │   │       └── workout-stepper.tsx
│   │   └── workout-session/
│   │       ├── actions/
│   │       │   ├── delete-workout-session.action.ts
│   │       │   ├── get-workout-sessions.action.ts
│   │       │   └── sync-workout-sessions.action.ts
│   │       ├── hooks/
│   │       │   └── use-donation-modal.ts
│   │       ├── lib/
│   │       │   └── workout-set-labels.ts
│   │       ├── model/
│   │       │   ├── use-sync-workout-sessions.ts
│   │       │   ├── use-workout-session.ts
│   │       │   ├── use-workout-sessions.ts
│   │       │   └── workout-session.store.ts
│   │       ├── types/
│   │       │   └── workout-set.ts
│   │       └── ui/
│   │           ├── donation-modal.tsx
│   │           ├── workout-session-header.tsx
│   │           ├── workout-session-heatmap.tsx
│   │           ├── workout-session-list.tsx
│   │           ├── workout-session-set.tsx
│   │           ├── workout-session-sets.tsx
│   │           ├── workout-session-timer.tsx
│   │           └── workout-sessions-synchronizer.tsx
│   ├── index.d.ts
│   ├── shared/
│   │   ├── api/
│   │   │   ├── README.md
│   │   │   ├── createHandler.ts
│   │   │   ├── handlers.ts
│   │   │   ├── mobile-auth.ts
│   │   │   ├── mobile-cookie-utils.ts
│   │   │   ├── mobile-safe-actions.ts
│   │   │   └── safe-actions.ts
│   │   ├── config/
│   │   │   ├── localized-metadata.ts
│   │   │   └── site-config.ts
│   │   ├── constants/
│   │   │   ├── cookies.ts
│   │   │   ├── errors.ts
│   │   │   ├── paths.ts
│   │   │   ├── placeholders.ts
│   │   │   ├── regexs.ts
│   │   │   ├── screen.ts
│   │   │   ├── social-platforms.tsx
│   │   │   ├── statistics.ts
│   │   │   ├── success.ts
│   │   │   └── workout-set-types.ts
│   │   ├── hooks/
│   │   │   ├── use-clipboard.ts
│   │   │   ├── use-premium-plans.ts
│   │   │   ├── useBoolean.ts
│   │   │   ├── useIsMobile.ts
│   │   │   └── useScrollToTop.ts
│   │   ├── lib/
│   │   │   ├── access-control.ts
│   │   │   ├── analytics/
│   │   │   │   ├── client.tsx
│   │   │   │   ├── events.ts
│   │   │   │   └── server.ts
│   │   │   ├── attribute-value-translation.ts
│   │   │   ├── date.ts
│   │   │   ├── format.ts
│   │   │   ├── guards.ts
│   │   │   ├── i18n-mapper.ts
│   │   │   ├── locale-slug.ts
│   │   │   ├── location/
│   │   │   │   ├── eu-countries.ts
│   │   │   │   └── location.ts
│   │   │   ├── logger.ts
│   │   │   ├── mail/
│   │   │   │   └── sendEmail.ts
│   │   │   ├── mdx/
│   │   │   │   └── load-mdx.ts
│   │   │   ├── network/
│   │   │   │   └── use-network-status.ts
│   │   │   ├── premium/
│   │   │   │   ├── premium.manager.ts
│   │   │   │   ├── premium.service.ts
│   │   │   │   ├── providers/
│   │   │   │   │   ├── base-provider.ts
│   │   │   │   │   └── stripe-provider.ts
│   │   │   │   ├── use-pending-checkout.ts
│   │   │   │   ├── use-premium-redirect.ts
│   │   │   │   └── use-premium.ts
│   │   │   ├── prisma.ts
│   │   │   ├── revenuecat/
│   │   │   │   ├── index.ts
│   │   │   │   ├── revenuecat.api.ts
│   │   │   │   ├── revenuecat.config.ts
│   │   │   │   └── revenuecat.mapping.ts
│   │   │   ├── server-url.ts
│   │   │   ├── slug.ts
│   │   │   ├── structured-data.ts
│   │   │   ├── utils.ts
│   │   │   ├── version.ts
│   │   │   ├── web-share.ts
│   │   │   ├── weight-conversion.ts
│   │   │   ├── workout-session/
│   │   │   │   ├── equipments.ts
│   │   │   │   ├── types/
│   │   │   │   │   └── workout-session.ts
│   │   │   │   ├── use-workout-session.service.ts
│   │   │   │   ├── workout-session.api.ts
│   │   │   │   └── workout-session.local.ts
│   │   │   └── youtube.ts
│   │   ├── schemas/
│   │   │   └── url.ts
│   │   ├── styles/
│   │   │   ├── additional-styles/
│   │   │   │   ├── highlights.css
│   │   │   │   └── utility-patterns.css
│   │   │   └── globals.css
│   │   └── types/
│   │       ├── i18n.types.ts
│   │       ├── next.ts
│   │       ├── premium.types.ts
│   │       ├── statistics.types.ts
│   │       └── storage.ts
│   └── widgets/
│       ├── 404.tsx
│       └── language-selector/
│           └── language-selector.tsx
├── tailwind.config.ts
├── tsconfig.json
└── workout-cool.code-workspace
Download .txt
SYMBOL INDEX (1182 symbols across 450 files)

FILE: app/[locale]/(admin)/admin/[...catchAll]/not-found.tsx
  function NotFoundPage (line 3) | function NotFoundPage() {

FILE: app/[locale]/(admin)/admin/[...catchAll]/page.tsx
  function AdminCatchAll (line 3) | function AdminCatchAll() {

FILE: app/[locale]/(admin)/admin/dashboard/page.tsx
  function getDashboardStats (line 8) | async function getDashboardStats() {
  function DashboardStats (line 60) | async function DashboardStats() {
  function DashboardStatsLoading (line 172) | function DashboardStatsLoading() {
  function AdminDashboard (line 207) | function AdminDashboard() {

FILE: app/[locale]/(admin)/admin/layout.tsx
  type AdminLayoutProps (line 9) | interface AdminLayoutProps {
  function AdminLayout (line 14) | async function AdminLayout({ children }: AdminLayoutProps) {

FILE: app/[locale]/(admin)/admin/not-found.tsx
  function NotFoundPage (line 3) | function NotFoundPage() {

FILE: app/[locale]/(admin)/admin/programs/[id]/edit/page.tsx
  type ProgramEditPageProps (line 6) | interface ProgramEditPageProps {
  function ProgramEditPage (line 10) | async function ProgramEditPage({ params }: ProgramEditPageProps) {

FILE: app/[locale]/(admin)/admin/programs/page.tsx
  function AdminPrograms (line 6) | function AdminPrograms() {

FILE: app/[locale]/(admin)/admin/settings/page.tsx
  function AdminSettings (line 1) | function AdminSettings() {

FILE: app/[locale]/(admin)/admin/users/page.tsx
  function AdminUsersPage (line 8) | async function AdminUsersPage() {

FILE: app/[locale]/(app)/(legal-and-payment)/layout.tsx
  type LocaleParams (line 3) | type LocaleParams = Record<string, string> & {
  function RouteLayout (line 7) | function RouteLayout({ children, params: _ }: LayoutParams<LocaleParams>) {

FILE: app/[locale]/(app)/(legal-and-payment)/legal/privacy/page.tsx
  type PageProps (line 4) | type PageProps = {
  function PrivacyPolicyPage (line 8) | async function PrivacyPolicyPage({ params }: PageProps) {

FILE: app/[locale]/(app)/(legal-and-payment)/legal/sales-terms/page.tsx
  type PageProps (line 5) | type PageProps = {
  function SalesTermsPage (line 9) | async function SalesTermsPage({ params }: PageProps) {

FILE: app/[locale]/(app)/(legal-and-payment)/legal/terms/page.tsx
  type PageProps (line 5) | type PageProps = {
  function TermsPage (line 9) | async function TermsPage({ params }: PageProps) {

FILE: app/[locale]/(app)/[slug]/layout.tsx
  type RootLayoutProps (line 3) | interface RootLayoutProps {
  function RootLayout (line 8) | async function RootLayout({ children }: RootLayoutProps) {

FILE: app/[locale]/(app)/about/page.tsx
  type PageProps (line 3) | type PageProps = {
  function AboutPage (line 7) | async function AboutPage({ params }: PageProps) {

FILE: app/[locale]/(app)/auth/(auth-layout)/forgot-password/page.tsx
  function ForgotPasswordPage (line 3) | async function ForgotPasswordPage() {

FILE: app/[locale]/(app)/auth/(auth-layout)/layout.tsx
  function AuthLayout (line 11) | async function AuthLayout(props: LayoutParams<{}>) {

FILE: app/[locale]/(app)/auth/(auth-layout)/reset-password/page.tsx
  function ResetPasswordPage (line 3) | function ResetPasswordPage() {

FILE: app/[locale]/(app)/auth/(auth-layout)/signin/page.tsx
  function AuthSignInPage (line 3) | async function AuthSignInPage() {

FILE: app/[locale]/(app)/auth/(auth-layout)/signup/page.tsx
  function AuthSignUpPage (line 12) | async function AuthSignUpPage() {

FILE: app/[locale]/(app)/auth/error.tsx
  function RouteError (line 11) | function RouteError({ error, reset }: ErrorParams) {

FILE: app/[locale]/(app)/auth/error/page.tsx
  function AuthErrorPage (line 6) | async function AuthErrorPage({ params }: { params: Promise<{ error: stri...

FILE: app/[locale]/(app)/auth/layout.tsx
  function AuthLayout (line 1) | function AuthLayout({ children }: { children: React.ReactNode }) {

FILE: app/[locale]/(app)/auth/signout/page.tsx
  function AuthSignOutPage (line 1) | function AuthSignOutPage() {

FILE: app/[locale]/(app)/auth/verify-email/layout.tsx
  type RootLayoutProps (line 8) | interface RootLayoutProps {
  function RootLayout (line 13) | async function RootLayout({ children }: RootLayoutProps) {

FILE: app/[locale]/(app)/auth/verify-email/page.tsx
  function VerifyEmailRootPage (line 5) | function VerifyEmailRootPage() {

FILE: app/[locale]/(app)/auth/verify-request/page.tsx
  type VerifyRequestPageParams (line 7) | interface VerifyRequestPageParams {
  function AuthVerifyRequestPage (line 12) | async function AuthVerifyRequestPage({ params: _p, searchParams: _s }: V...

FILE: app/[locale]/(app)/layout.tsx
  type RootLayoutProps (line 7) | interface RootLayoutProps {
  function RootLayout (line 12) | async function RootLayout({ children }: RootLayoutProps) {

FILE: app/[locale]/(app)/leaderboard/page.tsx
  function LeaderboardRootPage (line 13) | async function LeaderboardRootPage({ params }: { params: Promise<{ local...

FILE: app/[locale]/(app)/onboarding/layout.tsx
  function OnboardingLayout (line 3) | async function OnboardingLayout(props: LayoutParams<{}>) {

FILE: app/[locale]/(app)/onboarding/page.tsx
  function OnboardingPage (line 1) | async function OnboardingPage() {

FILE: app/[locale]/(app)/page.tsx
  function generateMetadata (line 9) | async function generateMetadata({ params }: { params: Promise<{ locale: ...
  function HomePage (line 51) | async function HomePage() {

FILE: app/[locale]/(app)/premium/page.tsx
  function PremiumPage (line 22) | function PremiumPage() {

FILE: app/[locale]/(app)/profile/page.tsx
  function ProfilePage (line 14) | function ProfilePage() {

FILE: app/[locale]/(app)/programs/[slug]/page.tsx
  type ProgramDetailPageProps (line 15) | interface ProgramDetailPageProps {
  function generateMetadata (line 19) | async function generateMetadata({ params }: ProgramDetailPageProps): Pro...
  function ProgramDetailPageRoute (line 58) | async function ProgramDetailPageRoute({ params }: ProgramDetailPageProps) {

FILE: app/[locale]/(app)/programs/[slug]/session/[sessionSlug]/ProgramSessionClient.tsx
  type ProgramSessionClientProps (line 23) | interface ProgramSessionClientProps {
  function ProgramSessionClient (line 31) | function ProgramSessionClient({ program, week, session, isAuthenticated,...

FILE: app/[locale]/(app)/programs/[slug]/session/[sessionSlug]/page.tsx
  type SessionDetailPageProps (line 17) | interface SessionDetailPageProps {
  function generateMetadata (line 21) | async function generateMetadata({ params }: SessionDetailPageProps): Pro...
  function SessionDetailPage (line 86) | async function SessionDetailPage({ params }: SessionDetailPageProps) {

FILE: app/[locale]/(app)/programs/page.tsx
  function ProgramsRootPage (line 13) | async function ProgramsRootPage({ params }: { params: Promise<{ locale: ...

FILE: app/[locale]/(app)/statistics/page.tsx
  function StatisticsPage (line 7) | async function StatisticsPage() {

FILE: app/[locale]/(app)/tools/bmi-calculator/bmi-calculator.utils.ts
  type UnitSystem (line 3) | type UnitSystem = "metric" | "imperial";
  type BmiData (line 5) | interface BmiData {
  type BmiResult (line 11) | interface BmiResult {
  type BmiCategory (line 26) | type BmiCategory =
  type HealthRisk (line 36) | type HealthRisk = "low" | "normal" | "increased" | "high" | "very_high" ...
  function calculateBmi (line 38) | function calculateBmi(data: BmiData, t: TFunction): BmiResult {
  function getBmiCategory (line 92) | function getBmiCategory(bmi: number): BmiCategory {
  function getHealthRisk (line 103) | function getHealthRisk(category: BmiCategory): HealthRisk {
  function getRecommendations (line 126) | function getRecommendations(category: BmiCategory, t: TFunction): string...
  function getBmiRange (line 197) | function getBmiRange(category: BmiCategory): { min: number; max: number } {
  function calculateIdealWeight (line 220) | function calculateIdealWeight(heightInMeters: number): { min: number; ma...
  function convertHeight (line 231) | function convertHeight(height: number, fromUnit: UnitSystem, toUnit: Uni...
  function convertWeight (line 241) | function convertWeight(weight: number, fromUnit: UnitSystem, toUnit: Uni...
  function getBmiPrimeCategory (line 253) | function getBmiPrimeCategory(bmiPrime: number): string {
  function getPonderalIndexCategory (line 264) | function getPonderalIndexCategory(pi: number): string {
  function getHealthRisks (line 271) | function getHealthRisks(_category: BmiCategory, t: TFunction): { overwei...

FILE: app/[locale]/(app)/tools/bmi-calculator/page.tsx
  function generateMetadata (line 12) | async function generateMetadata({ params }: { params: Promise<{ locale: ...
  function BmiCalculatorPage (line 69) | async function BmiCalculatorPage({ params }: { params: Promise<{ locale:...

FILE: app/[locale]/(app)/tools/bmi-calculator/shared/BmiCalculatorClient.tsx
  function BmiCalculatorClient (line 20) | function BmiCalculatorClient() {

FILE: app/[locale]/(app)/tools/bmi-calculator/shared/components/BmiEducationalContent.tsx
  function BmiEducationalContent (line 9) | function BmiEducationalContent() {

FILE: app/[locale]/(app)/tools/bmi-calculator/shared/components/BmiHeightInput.tsx
  type BmiHeightInputProps (line 8) | interface BmiHeightInputProps {
  function BmiHeightInput (line 14) | function BmiHeightInput({ value, unit, onChange }: BmiHeightInputProps) {

FILE: app/[locale]/(app)/tools/bmi-calculator/shared/components/BmiResultsDisplay.tsx
  type BmiResultsDisplayProps (line 9) | interface BmiResultsDisplayProps {
  function BmiResultsDisplay (line 13) | function BmiResultsDisplay({ result }: BmiResultsDisplayProps) {

FILE: app/[locale]/(app)/tools/bmi-calculator/shared/components/BmiUnitSelector.tsx
  type BmiUnitSelectorProps (line 8) | interface BmiUnitSelectorProps {
  function BmiUnitSelector (line 13) | function BmiUnitSelector({ value, onChange }: BmiUnitSelectorProps) {

FILE: app/[locale]/(app)/tools/bmi-calculator/shared/components/BmiWeightInput.tsx
  type BmiWeightInputProps (line 8) | interface BmiWeightInputProps {
  function BmiWeightInput (line 14) | function BmiWeightInput({ value, unit, onChange }: BmiWeightInputProps) {

FILE: app/[locale]/(app)/tools/bmi-calculator/shared/components/MathEquation.tsx
  type MathEquationProps (line 3) | interface MathEquationProps {
  function MathEquation (line 9) | function MathEquation({ equation, display = false, className = "" }: Mat...
  type FormulaCardProps (line 21) | interface FormulaCardProps {
  function FormulaCard (line 29) | function FormulaCard({ title, equation, example, description, className ...
  function createFraction (line 55) | function createFraction(numerator: string, denominator: string): string {
  function createSuperscript (line 65) | function createSuperscript(base: string, exponent: string): string {
  function createSubscript (line 70) | function createSubscript(base: string, subscript: string): string {

FILE: app/[locale]/(app)/tools/calorie-calculator/CalorieCalculatorHub.tsx
  type CalculatorFormula (line 11) | interface CalculatorFormula {
  function CalorieCalculatorHub (line 106) | function CalorieCalculatorHub() {

FILE: app/[locale]/(app)/tools/calorie-calculator/calorie-calculator-comparison/CalorieCalculatorComparison.tsx
  type FormulaResult (line 24) | interface FormulaResult {
  function CalorieCalculatorComparison (line 35) | function CalorieCalculatorComparison() {

FILE: app/[locale]/(app)/tools/calorie-calculator/calorie-calculator-comparison/page.tsx
  function generateMetadata (line 14) | async function generateMetadata({ params }: { params: Promise<{ locale: ...
  function CalorieCalculatorComparisonPage (line 46) | async function CalorieCalculatorComparisonPage({ params }: { params: Pro...

FILE: app/[locale]/(app)/tools/calorie-calculator/calorie-calculator.utils.ts
  type Gender (line 2) | type Gender = "male" | "female";
  type UnitSystem (line 3) | type UnitSystem = "metric" | "imperial";
  type ActivityLevel (line 4) | type ActivityLevel = "sedentary" | "light" | "moderate" | "active" | "ve...
  type Goal (line 5) | type Goal = "lose_fast" | "lose_slow" | "maintain" | "gain_slow" | "gain...
  type CalorieCalculatorInputs (line 7) | interface CalorieCalculatorInputs {
  type CalorieResults (line 17) | interface CalorieResults {
  constant ACTIVITY_MULTIPLIERS (line 27) | const ACTIVITY_MULTIPLIERS: Record<ActivityLevel, number> = {
  constant GOAL_ADJUSTMENTS (line 36) | const GOAL_ADJUSTMENTS: Record<Goal, number> = {
  function convertToMetric (line 47) | function convertToMetric(inputs: CalorieCalculatorInputs): {
  function calculateBMR (line 70) | function calculateBMR(inputs: CalorieCalculatorInputs): number {
  function calculateMacros (line 86) | function calculateMacros(targetCalories: number): {
  function calculateTDEE (line 106) | function calculateTDEE(inputs: CalorieCalculatorInputs): CalorieResults {

FILE: app/[locale]/(app)/tools/calorie-calculator/cunningham-calculator/page.tsx
  function generateMetadata (line 15) | async function generateMetadata({ params }: { params: Promise<{ locale: ...
  function CunninghamCalculatorPage (line 46) | async function CunninghamCalculatorPage({ params }: { params: Promise<{ ...

FILE: app/[locale]/(app)/tools/calorie-calculator/harris-benedict-calculator/page.tsx
  function generateMetadata (line 15) | async function generateMetadata({ params }: { params: Promise<{ locale: ...
  function HarrisBenedictCalculatorPage (line 45) | async function HarrisBenedictCalculatorPage({ params }: { params: Promis...

FILE: app/[locale]/(app)/tools/calorie-calculator/katch-mcardle-calculator/page.tsx
  function generateMetadata (line 15) | async function generateMetadata({ params }: { params: Promise<{ locale: ...
  function KatchMcArdleCalculatorPage (line 46) | async function KatchMcArdleCalculatorPage({ params }: { params: Promise<...

FILE: app/[locale]/(app)/tools/calorie-calculator/mifflin-st-jeor-calculator/page.tsx
  function generateMetadata (line 16) | async function generateMetadata({ params }: { params: Promise<{ locale: ...
  function MifflinStJeorCalculatorPage (line 46) | async function MifflinStJeorCalculatorPage({ params }: { params: Promise...

FILE: app/[locale]/(app)/tools/calorie-calculator/oxford-calculator/page.tsx
  function generateMetadata (line 15) | async function generateMetadata({ params }: { params: Promise<{ locale: ...
  function OxfordCalculatorPage (line 46) | async function OxfordCalculatorPage({ params }: { params: Promise<{ loca...

FILE: app/[locale]/(app)/tools/calorie-calculator/page.tsx
  function generateMetadata (line 10) | async function generateMetadata({ params }: { params: Promise<{ locale: ...
  function CalorieCalculatorPage (line 41) | async function CalorieCalculatorPage({ params }: { params: Promise<{ loc...

FILE: app/[locale]/(app)/tools/calorie-calculator/shared/CalorieCalculatorClient.tsx
  type CalorieCalculatorClientProps (line 25) | interface CalorieCalculatorClientProps {
  function CalorieCalculatorClient (line 29) | function CalorieCalculatorClient({ config }: CalorieCalculatorClientProp...

FILE: app/[locale]/(app)/tools/calorie-calculator/shared/calorie-formulas.utils.ts
  type Gender (line 3) | type Gender = "male" | "female";
  type UnitSystem (line 4) | type UnitSystem = "metric" | "imperial";
  type ActivityLevel (line 5) | type ActivityLevel = "sedentary" | "light" | "moderate" | "active" | "ve...
  type Goal (line 6) | type Goal = "lose_fast" | "lose_slow" | "maintain" | "gain_slow" | "gain...
  type CalorieCalculatorInputs (line 8) | interface CalorieCalculatorInputs {
  type CalorieResults (line 19) | interface CalorieResults {
  constant ACTIVITY_MULTIPLIERS (line 29) | const ACTIVITY_MULTIPLIERS: Record<ActivityLevel, number> = {
  constant GOAL_ADJUSTMENTS (line 38) | const GOAL_ADJUSTMENTS: Record<Goal, number> = {
  function convertToMetric (line 47) | function convertToMetric(inputs: CalorieCalculatorInputs): {
  function calculateMacros (line 61) | function calculateMacros(targetCalories: number): {
  function calculateMifflinStJeor (line 78) | function calculateMifflinStJeor(inputs: CalorieCalculatorInputs): number {
  function calculateHarrisBenedict (line 86) | function calculateHarrisBenedict(inputs: CalorieCalculatorInputs): number {
  function calculateKatchMcArdle (line 97) | function calculateKatchMcArdle(inputs: CalorieCalculatorInputs): number {
  function calculateCunningham (line 109) | function calculateCunningham(inputs: CalorieCalculatorInputs): number {
  function calculateOxford (line 121) | function calculateOxford(inputs: CalorieCalculatorInputs): number {
  function calculateCalories (line 132) | function calculateCalories(

FILE: app/[locale]/(app)/tools/calorie-calculator/shared/components/ActivityLevelSelector.tsx
  type ActivityLevelSelectorProps (line 9) | interface ActivityLevelSelectorProps {
  constant ACTIVITY_LEVELS (line 14) | const ACTIVITY_LEVELS: ActivityLevel[] = ["sedentary", "light", "moderat...
  function ActivityLevelSelector (line 16) | function ActivityLevelSelector({ value, onChange }: ActivityLevelSelecto...

FILE: app/[locale]/(app)/tools/calorie-calculator/shared/components/AgeInput.tsx
  type AgeInputProps (line 7) | interface AgeInputProps {
  function AgeInput (line 12) | function AgeInput({ value, onChange }: AgeInputProps) {

FILE: app/[locale]/(app)/tools/calorie-calculator/shared/components/BodyFatInput.tsx
  type BodyFatInputProps (line 7) | interface BodyFatInputProps {
  function BodyFatInput (line 12) | function BodyFatInput({ value, onChange }: BodyFatInputProps) {

FILE: app/[locale]/(app)/tools/calorie-calculator/shared/components/FAQSection.tsx
  function FAQSection (line 8) | function FAQSection() {

FILE: app/[locale]/(app)/tools/calorie-calculator/shared/components/GenderSelector.tsx
  type GenderSelectorProps (line 9) | interface GenderSelectorProps {
  function GenderSelector (line 14) | function GenderSelector({ value, onChange }: GenderSelectorProps) {

FILE: app/[locale]/(app)/tools/calorie-calculator/shared/components/GoalSelector.tsx
  type GoalSelectorProps (line 9) | interface GoalSelectorProps {
  constant GOALS (line 14) | const GOALS: Goal[] = ["lose_fast", "lose_slow", "maintain", "gain_slow"...
  function GoalSelector (line 16) | function GoalSelector({ value, onChange }: GoalSelectorProps) {

FILE: app/[locale]/(app)/tools/calorie-calculator/shared/components/HeightInput.tsx
  type HeightInputProps (line 8) | interface HeightInputProps {
  function HeightInput (line 14) | function HeightInput({ value, unit, onChange }: HeightInputProps) {

FILE: app/[locale]/(app)/tools/calorie-calculator/shared/components/InfoButton.tsx
  type InfoButtonProps (line 8) | interface InfoButtonProps {
  function InfoButton (line 13) | function InfoButton({ onClick, tooltip }: InfoButtonProps) {

FILE: app/[locale]/(app)/tools/calorie-calculator/shared/components/InfoModal.tsx
  type InfoModalProps (line 6) | interface InfoModalProps {
  function InfoModal (line 13) | function InfoModal({ isOpen, onClose, title, content }: InfoModalProps) {

FILE: app/[locale]/(app)/tools/calorie-calculator/shared/components/ResultsDisplay.tsx
  type ResultsDisplayProps (line 13) | interface ResultsDisplayProps {
  function ResultsDisplay (line 17) | function ResultsDisplay({ results }: ResultsDisplayProps) {

FILE: app/[locale]/(app)/tools/calorie-calculator/shared/components/UnitSelector.tsx
  type UnitSelectorProps (line 9) | interface UnitSelectorProps {
  function UnitSelector (line 14) | function UnitSelector({ value, onChange }: UnitSelectorProps) {

FILE: app/[locale]/(app)/tools/calorie-calculator/shared/components/WeightInput.tsx
  type WeightInputProps (line 8) | interface WeightInputProps {
  function WeightInput (line 14) | function WeightInput({ value, unit, onChange }: WeightInputProps) {

FILE: app/[locale]/(app)/tools/calorie-calculator/shared/types/index.ts
  type CalculatorFormula (line 2) | type CalculatorFormula = "mifflin" | "harris" | "katch" | "cunningham" |...
  type CalculatorConfig (line 4) | interface CalculatorConfig {

FILE: app/[locale]/(app)/tools/heart-rate-zones/lib/utils.ts
  type HeartRateZone (line 3) | interface HeartRateZone {
  type HeartRateResults (line 13) | interface HeartRateResults {
  function calculateHeartRateZones (line 18) | function calculateHeartRateZones(age: number, t: TFunction): HeartRateRe...

FILE: app/[locale]/(app)/tools/heart-rate-zones/page.tsx
  function generateMetadata (line 17) | async function generateMetadata({ params }: { params: Promise<{ locale: ...
  constant DEFAULT_AGE (line 53) | const DEFAULT_AGE = 30;
  function HeartRateZonesPage (line 54) | async function HeartRateZonesPage({ params }: { params: Promise<{ locale...

FILE: app/[locale]/(app)/tools/heart-rate-zones/seo/config.ts
  constant HEART_RATE_ZONES_SEO (line 4) | const HEART_RATE_ZONES_SEO: Record<

FILE: app/[locale]/(app)/tools/heart-rate-zones/seo/page-content.ts
  type PageContent (line 3) | interface PageContent {
  constant HEART_RATE_ZONES_CONTENT (line 8) | const HEART_RATE_ZONES_CONTENT: Record<Locale, PageContent> = {

FILE: app/[locale]/(app)/tools/heart-rate-zones/ui/HeartRateZonesCalculatorClient.tsx
  type SimpleHeartRateFormData (line 17) | type SimpleHeartRateFormData = z.infer<typeof simpleHeartRateSchema>;
  type HeartRateZone (line 19) | interface HeartRateZone {
  type HeartRateResults (line 29) | interface HeartRateResults {
  type SEOFriendlyHeartRateCalculatorProps (line 34) | interface SEOFriendlyHeartRateCalculatorProps {
  function HeartRateZonesCalculatorClient (line 39) | function HeartRateZonesCalculatorClient({ defaultAge = 30, defaultResult...

FILE: app/[locale]/(app)/tools/heart-rate-zones/ui/components/EducationalContent.tsx
  function EducationalContent (line 8) | function EducationalContent() {

FILE: app/[locale]/(app)/tools/heart-rate-zones/ui/components/EducationalContentServer.tsx
  function EducationalContentServer (line 6) | async function EducationalContentServer() {

FILE: app/[locale]/(app)/tools/heart-rate-zones/ui/components/FAQAccordion.tsx
  type FAQItem (line 5) | interface FAQItem {
  type FAQAccordionProps (line 10) | interface FAQAccordionProps {
  function FAQAccordion (line 14) | function FAQAccordion({ items }: FAQAccordionProps) {

FILE: app/[locale]/(app)/tools/heart-rate-zones/ui/components/SEOOptimizedContentServer.tsx
  function SEOOptimizedContentServer (line 10) | async function SEOOptimizedContentServer() {

FILE: app/[locale]/(app)/tools/heart-rate-zones/ui/components/ScrollToTopButton.tsx
  type ScrollToTopButtonProps (line 7) | interface ScrollToTopButtonProps {
  function ScrollToTopButton (line 11) | function ScrollToTopButton({ text }: ScrollToTopButtonProps) {

FILE: app/[locale]/(app)/tools/page.tsx
  type FitnessTool (line 11) | interface FitnessTool {
  function generateMetadata (line 66) | async function generateMetadata(): Promise<Metadata> {
  function ToolsPage (line 76) | async function ToolsPage() {

FILE: app/[locale]/@modal/(.)auth/login/page.tsx
  function LoginModal (line 4) | function LoginModal() {

FILE: app/[locale]/layout.tsx
  function generateMetadata (line 26) | async function generateMetadata({ params }: { params: Promise<{ locale: ...
  type RootLayoutProps (line 220) | interface RootLayoutProps {
  function RootLayout (line 225) | async function RootLayout({ params, children }: RootLayoutProps) {

FILE: app/[locale]/manifest.json/route.ts
  function GET (line 5) | async function GET(request: NextRequest, { params }: { params: Promise<{...

FILE: app/[locale]/not-found.tsx
  function NotFoundPage (line 3) | function NotFoundPage() {

FILE: app/[locale]/providers.tsx
  function LocaleDetector (line 19) | function LocaleDetector() {

FILE: app/ads.txt/route.ts
  function GET (line 3) | async function GET() {

FILE: app/api/analytics/premium/route.ts
  function POST (line 11) | async function POST(request: NextRequest) {

FILE: app/api/auth/signup/route.ts
  function POST (line 15) | async function POST(req: NextRequest) {

FILE: app/api/billing/status/route.ts
  function GET (line 7) | async function GET(request: NextRequest) {

FILE: app/api/exercises/[exerciseId]/statistics/one-rep-max/route.ts
  function calculateOneRepMax (line 18) | function calculateOneRepMax(weight: number, reps: number): number {
  function GET (line 22) | async function GET(request: NextRequest, { params }: { params: Promise<{...

FILE: app/api/exercises/[exerciseId]/statistics/route.ts
  function GET (line 21) | async function GET(

FILE: app/api/exercises/[exerciseId]/statistics/volume/route.ts
  function getWeekNumber (line 18) | function getWeekNumber(date: Date): string {
  function getWeekStartDate (line 28) | function getWeekStartDate(date: Date): Date {
  function GET (line 35) | async function GET(request: NextRequest, { params }: { params: Promise<{...

FILE: app/api/exercises/[exerciseId]/statistics/weight-progression/route.ts
  function GET (line 17) | async function GET(request: NextRequest, { params }: { params: Promise<{...

FILE: app/api/exercises/all/route.ts
  function GET (line 14) | async function GET(request: NextRequest) {

FILE: app/api/exercises/route.ts
  function GET (line 6) | async function GET(req: NextRequest) {

FILE: app/api/exercises/shuffle/route.ts
  function POST (line 13) | async function POST(req: NextRequest) {

FILE: app/api/premium/billing-portal/route.ts
  function POST (line 12) | async function POST(request: NextRequest) {

FILE: app/api/premium/checkout/route.ts
  function POST (line 12) | async function POST(request: NextRequest) {

FILE: app/api/premium/plans/route.ts
  function detectUserRegion (line 9) | function detectUserRegion(request: NextRequest): string {
  function GET (line 212) | async function GET(request: NextRequest) {

FILE: app/api/premium/status/route.ts
  function GET (line 15) | async function GET(request: NextRequest) {

FILE: app/api/programs/[slug]/enroll/route.ts
  function POST (line 8) | async function POST(req: NextRequest, { params }: { params: Promise<{ sl...
  function GET (line 52) | async function GET(req: NextRequest, { params }: { params: Promise<{ slu...

FILE: app/api/programs/[slug]/progress/route.ts
  function GET (line 5) | async function GET(request: Request, { params }: { params: Promise<{ slu...

FILE: app/api/programs/[slug]/route.ts
  function GET (line 5) | async function GET(request: Request, { params }: { params: Promise<{ slu...

FILE: app/api/programs/[slug]/sessions/[sessionSlug]/route.ts
  function GET (line 5) | async function GET(request: Request, { params }: { params: Promise<{ slu...

FILE: app/api/programs/route.ts
  function GET (line 5) | async function GET() {

FILE: app/api/programs/session-progress/[progressId]/complete/route.ts
  function POST (line 5) | async function POST(request: NextRequest, { params }: { params: Promise<...

FILE: app/api/programs/session-progress/start/route.ts
  function POST (line 5) | async function POST(request: NextRequest) {

FILE: app/api/revenuecat/link-user/route.ts
  function GET (line 11) | async function GET(request: NextRequest) {

FILE: app/api/revenuecat/sync-status/route.ts
  function POST (line 26) | async function POST(request: NextRequest) {

FILE: app/api/revenuecat/webhook/route.ts
  function verifyWebhookSignature (line 52) | function verifyWebhookSignature(request: Request, body: string, secret: ...
  function POST (line 65) | async function POST(request: NextRequest) {
  function handleSubscriptionActive (line 128) | async function handleSubscriptionActive(event: z.infer<typeof webhookEve...
  function handleSubscriptionInactive (line 181) | async function handleSubscriptionInactive(event: z.infer<typeof webhookE...
  function handleBillingIssue (line 206) | async function handleBillingIssue(event: z.infer<typeof webhookEventSche...
  function handleProductChange (line 224) | async function handleProductChange(event: z.infer<typeof webhookEventSch...

FILE: app/api/user/password/route.ts
  function PUT (line 14) | async function PUT(req: NextRequest) {

FILE: app/api/user/profile/route.ts
  function GET (line 14) | async function GET(req: NextRequest) {
  function PUT (line 39) | async function PUT(req: NextRequest) {

FILE: app/api/webhooks/revenuecat/route.ts
  type RevenueCatWebhookEvent (line 17) | interface RevenueCatWebhookEvent {
  function verifyWebhookAuthorization (line 53) | function verifyWebhookAuthorization(authHeader: string, expectedSecret: ...
  function logWebhookEvent (line 67) | async function logWebhookEvent(event: RevenueCatWebhookEvent, success: b...
  function processSubscriptionEvent (line 104) | async function processSubscriptionEvent(webhook: RevenueCatWebhookEvent) {
  function handleCancellationEvent (line 175) | async function handleCancellationEvent(webhook: RevenueCatWebhookEvent) {
  function POST (line 224) | async function POST(request: NextRequest) {
  function GET (line 277) | async function GET() {
  function handlePurchaseEvent (line 285) | async function handlePurchaseEvent(webhook: RevenueCatWebhookEvent) {

FILE: app/api/webhooks/stripe/route.ts
  function POST (line 11) | async function POST(request: NextRequest) {

FILE: app/api/workout-sessions/[sessionId]/feedback/route.ts
  function POST (line 13) | async function POST(request: NextRequest, { params }: { params: Promise<...

FILE: app/api/workout-sessions/[sessionId]/rating/route.ts
  function POST (line 13) | async function POST(request: NextRequest, { params }: { params: Promise<...

FILE: app/api/workout-sessions/[sessionId]/route.ts
  function DELETE (line 6) | async function DELETE(request: NextRequest, { params }: { params: Promis...

FILE: app/api/workout-sessions/[sessionId]/summary/route.ts
  function GET (line 6) | async function GET(request: NextRequest, { params }: { params: Promise<{...

FILE: app/api/workout-sessions/sync/route.ts
  function POST (line 6) | async function POST(request: NextRequest) {

FILE: app/api/workout-sessions/user/[userId]/route.ts
  function GET (line 6) | async function GET(request: NextRequest) {

FILE: app/sitemap.ts
  function sitemap (line 5) | async function sitemap(): Promise<MetadataRoute.Sitemap> {

FILE: emails/ContactSupportEmail.tsx
  type ContactSupportEmailProps (line 4) | interface ContactSupportEmailProps {

FILE: emails/DeleteAccountEmail.tsx
  function DeleteAccountEmail (line 7) | function DeleteAccountEmail({ email }: { email: string }) {

FILE: emails/ResetPasswordEmail.tsx
  type ResetPasswordEmailProps (line 8) | interface ResetPasswordEmailProps {

FILE: emails/VerifyEmail.tsx
  type VerifyEmailProps (line 8) | interface VerifyEmailProps {

FILE: emails/utils/BaseEmailLayout.tsx
  type BaseEmailLayoutProps (line 6) | interface BaseEmailLayoutProps {

FILE: locales/client.ts
  type TFunction (line 45) | type TFunction = Awaited<ReturnType<typeof useI18n>>;

FILE: locales/types.ts
  type Locale (line 2) | type Locale = (typeof locales)[number];

FILE: middleware.ts
  function detectUserLocale (line 6) | function detectUserLocale(request: NextRequest): string {
  function middleware (line 50) | async function middleware(request: NextRequest) {

FILE: nextauth.d.ts
  type Session (line 5) | interface Session {

FILE: prisma/migrations/0_init/migration.sql
  type "user" (line 35) | CREATE TABLE "user" (
  type "user_favorite_exercises" (line 56) | CREATE TABLE "user_favorite_exercises" (
  type "session" (line 66) | CREATE TABLE "session" (
  type "account" (line 81) | CREATE TABLE "account" (
  type "verification" (line 100) | CREATE TABLE "verification" (
  type "feedbacks" (line 112) | CREATE TABLE "feedbacks" (
  type "exercises" (line 125) | CREATE TABLE "exercises" (
  type "exercise_attribute_names" (line 144) | CREATE TABLE "exercise_attribute_names" (
  type "exercise_attribute_values" (line 154) | CREATE TABLE "exercise_attribute_values" (
  type "exercise_attributes" (line 165) | CREATE TABLE "exercise_attributes" (
  type "workout_sessions" (line 177) | CREATE TABLE "workout_sessions" (
  type "workout_session_exercises" (line 191) | CREATE TABLE "workout_session_exercises" (
  type "workout_sets" (line 201) | CREATE TABLE "workout_sets" (
  type "subscription_plans" (line 216) | CREATE TABLE "subscription_plans" (
  type "plan_provider_mappings" (line 231) | CREATE TABLE "plan_provider_mappings" (
  type "subscriptions" (line 246) | CREATE TABLE "subscriptions" (
  type "licenses" (line 263) | CREATE TABLE "licenses" (
  type "revenuecat_webhook_events" (line 279) | CREATE TABLE "revenuecat_webhook_events" (
  type "programs" (line 300) | CREATE TABLE "programs" (
  type "program_coaches" (line 339) | CREATE TABLE "program_coaches" (
  type "program_weeks" (line 350) | CREATE TABLE "program_weeks" (
  type "program_sessions" (line 371) | CREATE TABLE "program_sessions" (
  type "program_session_exercises" (line 401) | CREATE TABLE "program_session_exercises" (
  type "program_suggested_sets" (line 417) | CREATE TABLE "program_suggested_sets" (
  type "user_program_enrollments" (line 430) | CREATE TABLE "user_program_enrollments" (
  type "user_session_progress" (line 445) | CREATE TABLE "user_session_progress" (
  type "user" (line 457) | CREATE UNIQUE INDEX "user_email_key" ON "user"("email")
  type "user_favorite_exercises" (line 460) | CREATE UNIQUE INDEX "user_favorite_exercises_userId_exerciseId_key" ON "...
  type "session" (line 463) | CREATE UNIQUE INDEX "session_token_key" ON "session"("token")
  type "exercises" (line 466) | CREATE UNIQUE INDEX "exercises_slug_key" ON "exercises"("slug")
  type "exercises" (line 469) | CREATE UNIQUE INDEX "exercises_slugEn_key" ON "exercises"("slugEn")
  type "exercise_attribute_names" (line 472) | CREATE UNIQUE INDEX "exercise_attribute_names_name_key" ON "exercise_att...
  type "exercise_attribute_values" (line 475) | CREATE UNIQUE INDEX "exercise_attribute_values_attributeNameId_value_key...
  type "exercise_attributes" (line 478) | CREATE UNIQUE INDEX "exercise_attributes_exerciseId_attributeNameId_attr...
  type "plan_provider_mappings" (line 481) | CREATE INDEX "plan_provider_mappings_provider_externalId_idx" ON "plan_p...
  type "plan_provider_mappings" (line 484) | CREATE UNIQUE INDEX "plan_provider_mappings_planId_provider_region_key" ...
  type "subscriptions" (line 487) | CREATE UNIQUE INDEX "subscriptions_userId_platform_key" ON "subscription...
  type "licenses" (line 490) | CREATE UNIQUE INDEX "licenses_key_key" ON "licenses"("key")
  type "revenuecat_webhook_events" (line 493) | CREATE INDEX "revenuecat_webhook_events_appUserId_idx" ON "revenuecat_we...
  type "revenuecat_webhook_events" (line 496) | CREATE INDEX "revenuecat_webhook_events_eventType_idx" ON "revenuecat_we...
  type "revenuecat_webhook_events" (line 499) | CREATE INDEX "revenuecat_webhook_events_processed_idx" ON "revenuecat_we...
  type "revenuecat_webhook_events" (line 502) | CREATE INDEX "revenuecat_webhook_events_eventTimestamp_idx" ON "revenuec...
  type "programs" (line 505) | CREATE UNIQUE INDEX "programs_slug_key" ON "programs"("slug")
  type "programs" (line 508) | CREATE UNIQUE INDEX "programs_slugEn_key" ON "programs"("slugEn")
  type "programs" (line 511) | CREATE UNIQUE INDEX "programs_slugEs_key" ON "programs"("slugEs")
  type "programs" (line 514) | CREATE UNIQUE INDEX "programs_slugPt_key" ON "programs"("slugPt")
  type "programs" (line 517) | CREATE UNIQUE INDEX "programs_slugRu_key" ON "programs"("slugRu")
  type "programs" (line 520) | CREATE UNIQUE INDEX "programs_slugZhCn_key" ON "programs"("slugZhCn")
  type "program_weeks" (line 523) | CREATE UNIQUE INDEX "program_weeks_programId_weekNumber_key" ON "program...
  type "program_sessions" (line 526) | CREATE UNIQUE INDEX "program_sessions_weekId_sessionNumber_key" ON "prog...
  type "program_sessions" (line 529) | CREATE UNIQUE INDEX "program_sessions_weekId_slug_key" ON "program_sessi...
  type "program_sessions" (line 532) | CREATE UNIQUE INDEX "program_sessions_weekId_slugEn_key" ON "program_ses...
  type "program_sessions" (line 535) | CREATE UNIQUE INDEX "program_sessions_weekId_slugEs_key" ON "program_ses...
  type "program_sessions" (line 538) | CREATE UNIQUE INDEX "program_sessions_weekId_slugPt_key" ON "program_ses...
  type "program_sessions" (line 541) | CREATE UNIQUE INDEX "program_sessions_weekId_slugRu_key" ON "program_ses...
  type "program_sessions" (line 544) | CREATE UNIQUE INDEX "program_sessions_weekId_slugZhCn_key" ON "program_s...
  type "program_session_exercises" (line 547) | CREATE UNIQUE INDEX "program_session_exercises_sessionId_order_key" ON "...
  type "program_suggested_sets" (line 550) | CREATE UNIQUE INDEX "program_suggested_sets_programSessionExerciseId_set...
  type "user_program_enrollments" (line 553) | CREATE UNIQUE INDEX "user_program_enrollments_userId_programId_key" ON "...
  type "user_session_progress" (line 556) | CREATE UNIQUE INDEX "user_session_progress_workoutSessionId_key" ON "use...
  type "user_session_progress" (line 559) | CREATE UNIQUE INDEX "user_session_progress_enrollmentId_sessionId_key" O...

FILE: prisma/migrations_backup/20250101000000_baseline/migration.sql
  type "user" (line 35) | CREATE TABLE "user" (
  type "user_favorite_exercises" (line 56) | CREATE TABLE "user_favorite_exercises" (
  type "session" (line 66) | CREATE TABLE "session" (
  type "account" (line 81) | CREATE TABLE "account" (
  type "verification" (line 100) | CREATE TABLE "verification" (
  type "feedbacks" (line 112) | CREATE TABLE "feedbacks" (
  type "exercises" (line 125) | CREATE TABLE "exercises" (
  type "exercise_attribute_names" (line 144) | CREATE TABLE "exercise_attribute_names" (
  type "exercise_attribute_values" (line 154) | CREATE TABLE "exercise_attribute_values" (
  type "exercise_attributes" (line 165) | CREATE TABLE "exercise_attributes" (
  type "workout_sessions" (line 177) | CREATE TABLE "workout_sessions" (
  type "workout_session_exercises" (line 191) | CREATE TABLE "workout_session_exercises" (
  type "workout_sets" (line 201) | CREATE TABLE "workout_sets" (
  type "subscription_plans" (line 216) | CREATE TABLE "subscription_plans" (
  type "plan_provider_mappings" (line 231) | CREATE TABLE "plan_provider_mappings" (
  type "subscriptions" (line 246) | CREATE TABLE "subscriptions" (
  type "licenses" (line 263) | CREATE TABLE "licenses" (
  type "revenuecat_webhook_events" (line 279) | CREATE TABLE "revenuecat_webhook_events" (
  type "programs" (line 300) | CREATE TABLE "programs" (
  type "program_coaches" (line 339) | CREATE TABLE "program_coaches" (
  type "program_weeks" (line 350) | CREATE TABLE "program_weeks" (
  type "program_sessions" (line 371) | CREATE TABLE "program_sessions" (
  type "program_session_exercises" (line 401) | CREATE TABLE "program_session_exercises" (
  type "program_suggested_sets" (line 417) | CREATE TABLE "program_suggested_sets" (
  type "user_program_enrollments" (line 430) | CREATE TABLE "user_program_enrollments" (
  type "user_session_progress" (line 445) | CREATE TABLE "user_session_progress" (
  type "user" (line 457) | CREATE UNIQUE INDEX "user_email_key" ON "user"("email")
  type "user_favorite_exercises" (line 460) | CREATE UNIQUE INDEX "user_favorite_exercises_userId_exerciseId_key" ON "...
  type "session" (line 463) | CREATE UNIQUE INDEX "session_token_key" ON "session"("token")
  type "exercises" (line 466) | CREATE UNIQUE INDEX "exercises_slug_key" ON "exercises"("slug")
  type "exercises" (line 469) | CREATE UNIQUE INDEX "exercises_slugEn_key" ON "exercises"("slugEn")
  type "exercise_attribute_names" (line 472) | CREATE UNIQUE INDEX "exercise_attribute_names_name_key" ON "exercise_att...
  type "exercise_attribute_values" (line 475) | CREATE UNIQUE INDEX "exercise_attribute_values_attributeNameId_value_key...
  type "exercise_attributes" (line 478) | CREATE UNIQUE INDEX "exercise_attributes_exerciseId_attributeNameId_attr...
  type "plan_provider_mappings" (line 481) | CREATE INDEX "plan_provider_mappings_provider_externalId_idx" ON "plan_p...
  type "plan_provider_mappings" (line 484) | CREATE UNIQUE INDEX "plan_provider_mappings_planId_provider_region_key" ...
  type "subscriptions" (line 487) | CREATE UNIQUE INDEX "subscriptions_userId_platform_key" ON "subscription...
  type "licenses" (line 490) | CREATE UNIQUE INDEX "licenses_key_key" ON "licenses"("key")
  type "revenuecat_webhook_events" (line 493) | CREATE INDEX "revenuecat_webhook_events_appUserId_idx" ON "revenuecat_we...
  type "revenuecat_webhook_events" (line 496) | CREATE INDEX "revenuecat_webhook_events_eventType_idx" ON "revenuecat_we...
  type "revenuecat_webhook_events" (line 499) | CREATE INDEX "revenuecat_webhook_events_processed_idx" ON "revenuecat_we...
  type "revenuecat_webhook_events" (line 502) | CREATE INDEX "revenuecat_webhook_events_eventTimestamp_idx" ON "revenuec...
  type "programs" (line 505) | CREATE UNIQUE INDEX "programs_slug_key" ON "programs"("slug")
  type "programs" (line 508) | CREATE UNIQUE INDEX "programs_slugEn_key" ON "programs"("slugEn")
  type "programs" (line 511) | CREATE UNIQUE INDEX "programs_slugEs_key" ON "programs"("slugEs")
  type "programs" (line 514) | CREATE UNIQUE INDEX "programs_slugPt_key" ON "programs"("slugPt")
  type "programs" (line 517) | CREATE UNIQUE INDEX "programs_slugRu_key" ON "programs"("slugRu")
  type "programs" (line 520) | CREATE UNIQUE INDEX "programs_slugZhCn_key" ON "programs"("slugZhCn")
  type "program_weeks" (line 523) | CREATE UNIQUE INDEX "program_weeks_programId_weekNumber_key" ON "program...
  type "program_sessions" (line 526) | CREATE UNIQUE INDEX "program_sessions_weekId_sessionNumber_key" ON "prog...
  type "program_sessions" (line 529) | CREATE UNIQUE INDEX "program_sessions_weekId_slug_key" ON "program_sessi...
  type "program_sessions" (line 532) | CREATE UNIQUE INDEX "program_sessions_weekId_slugEn_key" ON "program_ses...
  type "program_sessions" (line 535) | CREATE UNIQUE INDEX "program_sessions_weekId_slugEs_key" ON "program_ses...
  type "program_sessions" (line 538) | CREATE UNIQUE INDEX "program_sessions_weekId_slugPt_key" ON "program_ses...
  type "program_sessions" (line 541) | CREATE UNIQUE INDEX "program_sessions_weekId_slugRu_key" ON "program_ses...
  type "program_sessions" (line 544) | CREATE UNIQUE INDEX "program_sessions_weekId_slugZhCn_key" ON "program_s...
  type "program_session_exercises" (line 547) | CREATE UNIQUE INDEX "program_session_exercises_sessionId_order_key" ON "...
  type "program_suggested_sets" (line 550) | CREATE UNIQUE INDEX "program_suggested_sets_programSessionExerciseId_set...
  type "user_program_enrollments" (line 553) | CREATE UNIQUE INDEX "user_program_enrollments_userId_programId_key" ON "...
  type "user_session_progress" (line 556) | CREATE UNIQUE INDEX "user_session_progress_workoutSessionId_key" ON "use...
  type "user_session_progress" (line 559) | CREATE UNIQUE INDEX "user_session_progress_enrollmentId_sessionId_key" O...

FILE: prisma/migrations_backup/20250117000000_add_statistics_indexes/migration.sql
  type "workout_session_exercises" (line 3) | CREATE INDEX "workout_session_exercises_exerciseId_idx" ON "workout_sess...
  type "workout_sessions" (line 6) | CREATE INDEX "workout_sessions_userId_createdAt_idx" ON "workout_session...
  type "workout_sets" (line 9) | CREATE INDEX "workout_sets_completed_idx" ON "workout_sets"("completed")
  type "workout_sessions" (line 12) | CREATE INDEX "workout_sessions_userId_createdAt_desc_idx" ON "workout_se...
  type "workout_sets" (line 15) | CREATE INDEX "workout_sets_workoutSessionExerciseId_idx" ON "workout_set...

FILE: prisma/migrations_backup/20250414120436_init/migration.sql
  type "user" (line 2) | CREATE TABLE "user" (
  type "session" (line 15) | CREATE TABLE "session" (
  type "account" (line 29) | CREATE TABLE "account" (
  type "verification" (line 48) | CREATE TABLE "verification" (
  type "user" (line 60) | CREATE UNIQUE INDEX "user_email_key" ON "user"("email")
  type "session" (line 63) | CREATE UNIQUE INDEX "session_token_key" ON "session"("token")

FILE: prisma/migrations_backup/20250414170807_add_feedbacks/migration.sql
  type "Feedback" (line 2) | CREATE TABLE "Feedback" (

FILE: prisma/migrations_backup/20250414174246_rename_feedbacks/migration.sql
  type "feedbacks" (line 14) | CREATE TABLE "feedbacks" (

FILE: prisma/migrations_backup/20250416160303_add_plans/migration.sql
  type "Plan" (line 2) | CREATE TABLE "Plan" (
  type "PlanVariant" (line 12) | CREATE TABLE "PlanVariant" (
  type "Subscription" (line 25) | CREATE TABLE "Subscription" (
  type "Subscription" (line 40) | CREATE UNIQUE INDEX "Subscription_userId_key" ON "Subscription"("userId")

FILE: prisma/migrations_backup/20250416160502_map/migration.sql
  type "plan" (line 28) | CREATE TABLE "plan" (
  type "plan_variant" (line 38) | CREATE TABLE "plan_variant" (
  type "subscription" (line 51) | CREATE TABLE "subscription" (
  type "subscription" (line 66) | CREATE UNIQUE INDEX "subscription_userId_key" ON "subscription"("userId")

FILE: prisma/migrations_backup/20250610182024_add_exercises_and_attributes/migration.sql
  type "exercises" (line 40) | CREATE TABLE "exercises" (
  type "exercise_attribute_names" (line 67) | CREATE TABLE "exercise_attribute_names" (
  type "exercise_attribute_values" (line 78) | CREATE TABLE "exercise_attribute_values" (
  type "exercise_attributes" (line 90) | CREATE TABLE "exercise_attributes" (
  type "exercises" (line 103) | CREATE UNIQUE INDEX "exercises_slug_key" ON "exercises"("slug")
  type "exercises" (line 106) | CREATE UNIQUE INDEX "exercises_slugEn_key" ON "exercises"("slugEn")
  type "exercise_attributes" (line 109) | CREATE UNIQUE INDEX "exercise_attributes_exerciseId_attributeNameId_attr...

FILE: prisma/migrations_backup/20250610182815_add_exercise_enums/migration.sql
  type "exercise_attribute_values" (line 28) | CREATE UNIQUE INDEX "exercise_attribute_values_attributeNameId_value_key...

FILE: prisma/migrations_backup/20250610184725_simplified_exercises/migration.sql
  type "exercise_attribute_names" (line 44) | CREATE UNIQUE INDEX "exercise_attribute_names_name_key" ON "exercise_att...
  type "exercise_attribute_values" (line 47) | CREATE UNIQUE INDEX "exercise_attribute_values_attributeNameId_value_key...

FILE: prisma/migrations_backup/20250611190228_convert_text_to_enums/migration.sql
  type "exercise_attribute_names" (line 38) | CREATE UNIQUE INDEX "exercise_attribute_names_name_key" ON "exercise_att...
  type "exercise_attribute_values" (line 39) | CREATE UNIQUE INDEX "exercise_attribute_values_attributeNameId_value_key...

FILE: prisma/migrations_backup/20250612213546_workout_session_sets/migration.sql
  type "WorkoutSession" (line 8) | CREATE TABLE "WorkoutSession" (
  type "WorkoutSessionExercise" (line 19) | CREATE TABLE "WorkoutSessionExercise" (
  type "WorkoutSet" (line 29) | CREATE TABLE "WorkoutSet" (

FILE: prisma/migrations_backup/20250614125347_add_table_maps/migration.sql
  type "workout_sessions" (line 31) | CREATE TABLE "workout_sessions" (
  type "workout_session_exercises" (line 42) | CREATE TABLE "workout_session_exercises" (
  type "workout_sets" (line 52) | CREATE TABLE "workout_sets" (

FILE: prisma/migrations_backup/20250623142458_add_billing_and_subscriptions/migration.sql
  type "app_configuration" (line 24) | CREATE TABLE "app_configuration" (
  type "subscription_plans" (line 37) | CREATE TABLE "subscription_plans" (
  type "subscriptions" (line 56) | CREATE TABLE "subscriptions" (
  type "payments" (line 74) | CREATE TABLE "payments" (
  type "licenses" (line 93) | CREATE TABLE "licenses" (
  type "webhook_events" (line 109) | CREATE TABLE "webhook_events" (
  type "subscription_plans" (line 123) | CREATE UNIQUE INDEX "subscription_plans_revenueCatProductId_key" ON "sub...
  type "subscriptions" (line 126) | CREATE UNIQUE INDEX "subscriptions_userId_platform_key" ON "subscription...
  type "payments" (line 129) | CREATE INDEX "payments_processorPaymentId_idx" ON "payments"("processorP...
  type "licenses" (line 132) | CREATE UNIQUE INDEX "licenses_key_key" ON "licenses"("key")

FILE: prisma/migrations_backup/20250623144324_add_webhook_events/migration.sql
  type "webhook_events" (line 2) | CREATE TABLE "webhook_events" (
  type "webhook_events" (line 22) | CREATE INDEX "webhook_events_provider_processed_idx" ON "webhook_events"...
  type "webhook_events" (line 25) | CREATE INDEX "webhook_events_relatedUserId_idx" ON "webhook_events"("rel...
  type "webhook_events" (line 28) | CREATE INDEX "webhook_events_createdAt_idx" ON "webhook_events"("created...

FILE: prisma/migrations_backup/20250625155932_add_admin/migration.sql
  type "programs" (line 5) | CREATE TABLE "programs" (
  type "program_coaches" (line 39) | CREATE TABLE "program_coaches" (
  type "program_weeks" (line 50) | CREATE TABLE "program_weeks" (
  type "program_sessions" (line 71) | CREATE TABLE "program_sessions" (
  type "program_session_exercises" (line 95) | CREATE TABLE "program_session_exercises" (
  type "program_suggested_sets" (line 111) | CREATE TABLE "program_suggested_sets" (
  type "user_program_enrollments" (line 124) | CREATE TABLE "user_program_enrollments" (
  type "user_session_progress" (line 139) | CREATE TABLE "user_session_progress" (
  type "programs" (line 151) | CREATE UNIQUE INDEX "programs_slug_key" ON "programs"("slug")
  type "program_weeks" (line 154) | CREATE UNIQUE INDEX "program_weeks_programId_weekNumber_key" ON "program...
  type "program_sessions" (line 157) | CREATE UNIQUE INDEX "program_sessions_weekId_sessionNumber_key" ON "prog...
  type "program_session_exercises" (line 160) | CREATE UNIQUE INDEX "program_session_exercises_sessionId_order_key" ON "...
  type "program_suggested_sets" (line 163) | CREATE UNIQUE INDEX "program_suggested_sets_programSessionExerciseId_set...
  type "user_program_enrollments" (line 166) | CREATE UNIQUE INDEX "user_program_enrollments_userId_programId_key" ON "...
  type "user_session_progress" (line 169) | CREATE UNIQUE INDEX "user_session_progress_workoutSessionId_key" ON "use...
  type "user_session_progress" (line 172) | CREATE UNIQUE INDEX "user_session_progress_enrollmentId_sessionId_key" O...

FILE: prisma/migrations_backup/20250626102058_add_i18n_slugs_on_program/migration.sql
  type "program_sessions" (line 44) | CREATE UNIQUE INDEX "program_sessions_weekId_slug_key" ON "program_sessi...
  type "program_sessions" (line 47) | CREATE UNIQUE INDEX "program_sessions_weekId_slugEn_key" ON "program_ses...
  type "program_sessions" (line 50) | CREATE UNIQUE INDEX "program_sessions_weekId_slugEs_key" ON "program_ses...
  type "program_sessions" (line 53) | CREATE UNIQUE INDEX "program_sessions_weekId_slugPt_key" ON "program_ses...
  type "program_sessions" (line 56) | CREATE UNIQUE INDEX "program_sessions_weekId_slugRu_key" ON "program_ses...
  type "program_sessions" (line 59) | CREATE UNIQUE INDEX "program_sessions_weekId_slugZhCn_key" ON "program_s...
  type "programs" (line 62) | CREATE UNIQUE INDEX "programs_slugEn_key" ON "programs"("slugEn")
  type "programs" (line 65) | CREATE UNIQUE INDEX "programs_slugEs_key" ON "programs"("slugEs")
  type "programs" (line 68) | CREATE UNIQUE INDEX "programs_slugPt_key" ON "programs"("slugPt")
  type "programs" (line 71) | CREATE UNIQUE INDEX "programs_slugRu_key" ON "programs"("slugRu")
  type "programs" (line 74) | CREATE UNIQUE INDEX "programs_slugZhCn_key" ON "programs"("slugZhCn")

FILE: prisma/migrations_backup/20250626182857_cleanup_billing_system/migration.sql
  type "plan_provider_mappings" (line 39) | CREATE TABLE "plan_provider_mappings" (
  type "plan_provider_mappings" (line 54) | CREATE INDEX "plan_provider_mappings_provider_externalId_idx" ON "plan_p...
  type "plan_provider_mappings" (line 57) | CREATE UNIQUE INDEX "plan_provider_mappings_planId_provider_region_key" ...

FILE: prisma/migrations_backup/20250707114920_add_user_favorite_exercises/migration.sql
  type "user_favorite_exercises" (line 2) | CREATE TABLE "user_favorite_exercises" (
  type "user_favorite_exercises" (line 12) | CREATE UNIQUE INDEX "user_favorite_exercises_userId_exerciseId_key" ON "...

FILE: prisma/migrations_backup/20250709_add_revenuecat_fields/migration.sql
  type "revenuecat_webhook_events" (line 6) | CREATE TABLE "revenuecat_webhook_events" (
  type "revenuecat_webhook_events" (line 27) | CREATE INDEX "revenuecat_webhook_events_appUserId_idx" ON "revenuecat_we...
  type "revenuecat_webhook_events" (line 28) | CREATE INDEX "revenuecat_webhook_events_eventType_idx" ON "revenuecat_we...
  type "revenuecat_webhook_events" (line 29) | CREATE INDEX "revenuecat_webhook_events_processed_idx" ON "revenuecat_we...
  type "revenuecat_webhook_events" (line 30) | CREATE INDEX "revenuecat_webhook_events_eventTimestamp_idx" ON "revenuec...

FILE: public/sw.js
  constant CACHE_NAME (line 1) | const CACHE_NAME = "1.2.5";

FILE: scripts/check-pricing-config.ts
  function checkPricingConfig (line 7) | async function checkPricingConfig() {

FILE: scripts/import-exercises-with-attributes.ts
  type ExerciseAttributeCSVRow (line 9) | interface ExerciseAttributeCSVRow {
  function cleanValue (line 25) | function cleanValue(value: string): string | null {
  function groupExercisesByOriginalId (line 30) | function groupExercisesByOriginalId(rows: ExerciseAttributeCSVRow[]) {
  function ensureAttributeNameExists (line 65) | async function ensureAttributeNameExists(name: ExerciseAttributeNameEnum) {
  function normalizeAttributeValue (line 79) | function normalizeAttributeValue(value: string): ExerciseAttributeValueE...
  function ensureAttributeValueExists (line 88) | async function ensureAttributeValueExists(attributeNameId: string, value...
  function importExercisesFromCSV (line 108) | async function importExercisesFromCSV(filePath: string) {
  function main (line 205) | async function main() {

FILE: scripts/seed-leaderboard-data.ts
  function seedLeaderboardData (line 10) | async function seedLeaderboardData() {

FILE: scripts/seed-multi-region-plans.ts
  type RegionPricing (line 9) | interface RegionPricing {
  function seedMultiRegionPlans (line 70) | async function seedMultiRegionPlans() {

FILE: scripts/seed-subscription-plans-simple.ts
  function seedSubscriptionPlans (line 13) | async function seedSubscriptionPlans() {

FILE: scripts/seed-workout-data-advanced.ts
  constant USER_ID (line 4) | const USER_ID = "bwZuBQO4cJgBX6NiZaXgv81vKfgBQcFe";
  constant BENCH_PRESS_ID (line 5) | const BENCH_PRESS_ID = "cmbw9xso904p69kv1vwuadhx6";
  type WorkoutPattern (line 7) | interface WorkoutPattern {
  type ExercisePattern (line 13) | interface ExercisePattern {
  type SetPattern (line 19) | interface SetPattern {
  function seedAdvancedWorkoutData (line 63) | async function seedAdvancedWorkoutData(weeksToGenerate: number = 12, sta...

FILE: src/components/ads/AdBlockerForPremium.tsx
  function AdBlockerForPremium (line 7) | function AdBlockerForPremium() {

FILE: src/components/ads/AdPlaceholder.tsx
  type AdPlaceholderProps (line 1) | interface AdPlaceholderProps {
  function AdPlaceholder (line 7) | function AdPlaceholder({ width, height, type = "Ad" }: AdPlaceholderProp...

FILE: src/components/ads/AdSenseAutoAds.tsx
  type Window (line 8) | interface Window {
  function AdSenseAutoAds (line 13) | function AdSenseAutoAds() {

FILE: src/components/ads/AdWrapper.tsx
  type AdWrapperProps (line 8) | interface AdWrapperProps {
  function AdWrapper (line 14) | function AdWrapper({ children, fallback = null, forceShow = false }: AdW...

FILE: src/components/ads/EzoicAd.tsx
  type EzoicAdProps (line 5) | interface EzoicAdProps {
  type Window (line 11) | interface Window {
  function EzoicAd (line 19) | function EzoicAd({ placementId, className = "" }: EzoicAdProps) {

FILE: src/components/ads/GoogleAdSense.tsx
  type GoogleAdSenseProps (line 5) | interface GoogleAdSenseProps {
  type Window (line 15) | interface Window {
  function GoogleAdSense (line 20) | function GoogleAdSense({

FILE: src/components/ads/HorizontalAdBanner.tsx
  type HorizontalAdBannerProps (line 10) | interface HorizontalAdBannerProps {
  function HorizontalAdBanner (line 15) | function HorizontalAdBanner({ adSlot, ezoicPlacementId }: HorizontalAdBa...

FILE: src/components/ads/HorizontalBottomBanner.tsx
  type HorizontalBottomBannerProps (line 5) | interface HorizontalBottomBannerProps {
  function HorizontalBottomBanner (line 10) | function HorizontalBottomBanner({ adSlot, ezoicPlacementId }: Horizontal...

FILE: src/components/ads/HorizontalTopBanner.tsx
  type HorizontalTopBannerProps (line 3) | interface HorizontalTopBannerProps {
  function HorizontalTopBanner (line 8) | function HorizontalTopBanner({ adSlot, ezoicPlacementId }: HorizontalTop...

FILE: src/components/ads/InArticle.tsx
  function InArticle (line 7) | function InArticle({ adSlot }: { adSlot: string }) {

FILE: src/components/ads/ResponsiveAdBanner.tsx
  type ResponsiveAdBannerProps (line 9) | interface ResponsiveAdBannerProps {
  function ResponsiveAdBanner (line 13) | function ResponsiveAdBanner({ adSlot }: ResponsiveAdBannerProps) {

FILE: src/components/ads/VerticalAdBanner.tsx
  type VerticalAdBannerProps (line 8) | interface VerticalAdBannerProps {
  function VerticalAdBanner (line 14) | function VerticalAdBanner({ adSlot, ezoicPlacementId, position = "left" ...

FILE: src/components/ads/VerticalLeftBanner.tsx
  function VerticalLeftBanner (line 5) | function VerticalLeftBanner() {

FILE: src/components/ads/VerticalRightBanner.tsx
  function VerticalRightBanner (line 5) | function VerticalRightBanner() {

FILE: src/components/ads/nutripure-affiliate-banner.tsx
  type NutripureAffiliateBannerProps (line 38) | interface NutripureAffiliateBannerProps {
  function NutripureAffiliateBanner (line 43) | function NutripureAffiliateBanner({ context = "general", position = "mid...

FILE: src/components/premium/RemoveAdsText.tsx
  function RemoveAdsText (line 8) | function RemoveAdsText() {

FILE: src/components/pwa/ServiceWorkerRegistration.tsx
  function ServiceWorkerRegistration (line 5) | function ServiceWorkerRegistration() {

FILE: src/components/seo/SEOHead.tsx
  type SEOHeadProps (line 9) | interface SEOHeadProps {
  function generateSEOMetadata (line 35) | function generateSEOMetadata({
  type SEOScriptsProps (line 165) | interface SEOScriptsProps extends SEOHeadProps {
  function SEOScripts (line 171) | function SEOScripts({

FILE: src/components/seo/breadcrumbs.tsx
  type BreadcrumbItem (line 7) | interface BreadcrumbItem {
  type BreadcrumbsProps (line 13) | interface BreadcrumbsProps {
  function Breadcrumbs (line 17) | function Breadcrumbs({ items }: BreadcrumbsProps) {

FILE: src/components/seo/duration-badge.tsx
  type DurationBadgeProps (line 3) | interface DurationBadgeProps {
  function DurationBadge (line 11) | function DurationBadge({

FILE: src/components/seo/rich-snippet-rating.tsx
  type RichSnippetRatingProps (line 3) | interface RichSnippetRatingProps {
  function RichSnippetRating (line 9) | function RichSnippetRating({ rating, reviewCount, className = "" }: Rich...

FILE: src/components/seo/session-rich-snippets.tsx
  type SessionRichSnippetsProps (line 5) | interface SessionRichSnippetsProps {
  function SessionRichSnippets (line 12) | function SessionRichSnippets({ duration, exerciseCount, totalSets, class...

FILE: src/components/svg/BrokenLink.tsx
  type BrokenLinkIconProps (line 4) | type BrokenLinkIconProps = ComponentPropsWithoutRef<"svg"> & { size?: nu...

FILE: src/components/svg/DotPattern.tsx
  type DotPatternProps (line 5) | type DotPatternProps = ComponentPropsWithoutRef<"div">;

FILE: src/components/svg/GoogleSvg.tsx
  type GoogleSvgProps (line 3) | type GoogleSvgProps = ComponentPropsWithoutRef<"svg"> & { size?: number };

FILE: src/components/svg/LogoSvg.tsx
  type LogoSvgProps (line 3) | type LogoSvgProps = ComponentPropsWithoutRef<"svg"> & { size?: number };

FILE: src/components/ui/404-page-not-found.tsx
  function NotFoundPage (line 8) | function NotFoundPage() {

FILE: src/components/ui/ToastSonner.tsx
  type ToasterProps (line 6) | type ToasterProps = React.ComponentProps<typeof Sonner>;

FILE: src/components/ui/alert.tsx
  type AlertProps (line 36) | interface AlertProps extends React.HTMLAttributes<HTMLDivElement>, Varia...

FILE: src/components/ui/animated-button/ShinyButton.tsx
  type Props (line 5) | type Props = HTMLAttributes<HTMLButtonElement> & {

FILE: src/components/ui/badge.tsx
  type BadgeProps (line 43) | interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, Varia...
  function Badge (line 45) | function Badge({ className, variant, size, ...props }: BadgeProps) {

FILE: src/components/ui/bottom-sheet-vaul.tsx
  type BottomSheetProps (line 10) | interface BottomSheetProps {
  function BottomSheetVaul (line 21) | function BottomSheetVaul({

FILE: src/components/ui/bottom-sheet.tsx
  type PhoneDrawerProps (line 37) | interface PhoneDrawerProps {
  function BottomSheet (line 45) | function BottomSheet({

FILE: src/components/ui/button.tsx
  type ButtonProps (line 40) | interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonEleme...

FILE: src/components/ui/card-styled.tsx
  type CardProps (line 37) | interface CardProps extends React.HTMLAttributes<HTMLDivElement>, Varian...

FILE: src/components/ui/dialog-stack.tsx
  type DialogStackContextType (line 10) | type DialogStackContextType = {
  type DialogStackChildProps (line 30) | type DialogStackChildProps = {

FILE: src/components/ui/divider.tsx
  type DividerProps (line 5) | type DividerProps = ComponentProps<"div">;

FILE: src/components/ui/donation-alert.tsx
  type DonationAlertProps (line 8) | interface DonationAlertProps {

FILE: src/components/ui/form.tsx
  type FormProps (line 16) | type FormProps<T extends FieldValues> = Omit<React.ComponentProps<"form"...
  type FormFieldContextValue (line 32) | type FormFieldContextValue<
  type FormItemContextValue (line 74) | type FormItemContextValue = {
  type UseZodFormProps (line 143) | type UseZodFormProps<Schema extends ZodSchema> = Exclude<UseFormProps<z....
  function useZodForm (line 147) | function useZodForm<Schema extends ZodSchema>({ schema, ...formProps }: ...

FILE: src/components/ui/input.tsx
  type InputProps (line 26) | interface InputProps extends React.InputHTMLAttributes<HTMLInputElement>...

FILE: src/components/ui/iphone-mockup.tsx
  type IPhoneMockupProps (line 9) | interface IPhoneMockupProps {
  function IPhoneMockup (line 18) | function IPhoneMockup({ children, className, screenClassName, showNotch ...

FILE: src/components/ui/link.tsx
  type LinkProps (line 8) | interface LinkProps extends ComponentProps<typeof NextLink> {

FILE: src/components/ui/local-alert.tsx
  type LocalAlertProps (line 9) | interface LocalAlertProps {

FILE: src/components/ui/moving-border.tsx
  function Button (line 7) | function Button({

FILE: src/components/ui/next-top-loader.tsx
  type NextTopLoaderProps (line 6) | type NextTopLoaderProps = {

FILE: src/components/ui/pagination.tsx
  type PaginationLinkProps (line 26) | type PaginationLinkProps = {

FILE: src/components/ui/phone-frame-preview.tsx
  type PhoneFramePreviewProps (line 5) | interface PhoneFramePreviewProps extends React.HTMLAttributes<HTMLDivEle...
  function PhoneFramePreview (line 9) | function PhoneFramePreview({ children, className, ...props }: PhoneFrame...

FILE: src/components/ui/premium-gate.tsx
  type PremiumGateProps (line 14) | interface PremiumGateProps {
  function PremiumGate (line 30) | function PremiumGate({
  type PremiumUpgradePromptProps (line 69) | interface PremiumUpgradePromptProps {
  function PremiumUpgradePrompt (line 76) | function PremiumUpgradePrompt({ feature, message, onUpgradePress, classN...
  function PremiumBadge (line 122) | function PremiumBadge({ size = "small", className }: { size?: "small" | ...
  function withPremiumGate (line 144) | function withPremiumGate<P extends object>(

FILE: src/components/ui/premium-upsell-alert.tsx
  type PremiumUpsellAlertProps (line 11) | interface PremiumUpsellAlertProps {

FILE: src/components/ui/sheet.tsx
  type SheetContentProps (line 51) | interface SheetContentProps extends React.ComponentPropsWithoutRef<typeo...

FILE: src/components/ui/shine-border.tsx
  type TColorProp (line 5) | type TColorProp = string | string[];
  type ShineBorderProps (line 7) | interface ShineBorderProps {
  function ShineBorder (line 26) | function ShineBorder({

FILE: src/components/ui/simple-select.tsx
  type SelectOption (line 8) | interface SelectOption<T extends string = string> {
  type SimpleSelectProps (line 13) | interface SimpleSelectProps<T extends string = string> {
  function SimpleSelect (line 42) | function SimpleSelect<T extends string = string>({

FILE: src/components/ui/skeleton.tsx
  type SkeletonProps (line 5) | interface SkeletonProps extends React.HTMLAttributes<HTMLDivElement> {
  function Skeleton (line 11) | function Skeleton({ width, height, rounded = "rounded", className, ...pr...

FILE: src/components/ui/sonner.tsx
  type ToasterProps (line 6) | type ToasterProps = React.ComponentProps<typeof Sonner>;

FILE: src/components/ui/star-button.tsx
  type StarButtonProps (line 5) | interface StarButtonProps {
  function StarButton (line 13) | function StarButton({ isActive, isLoading, onClick, className, children ...

FILE: src/components/ui/textarea.tsx
  type TextareaProps (line 5) | interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAre...

FILE: src/components/ui/theme-provider.tsx
  function ThemeProvider (line 6) | function ThemeProvider({ children, ...props }: React.ComponentProps<type...

FILE: src/components/ui/timer.tsx
  function Timer (line 3) | function Timer({

FILE: src/components/ui/title-with-dot.tsx
  function TitleWithDot (line 3) | function TitleWithDot({ title, className }: { title: string; className?:...

FILE: src/components/ui/toast.tsx
  type ToastProps (line 103) | type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;
  type ToastActionElement (line 105) | type ToastActionElement = React.ReactElement<typeof ToastAction>;
  type BrandedToastVariant (line 107) | type BrandedToastVariant = "default" | "success" | "error" | "info" | "w...
  type BrandedToastOptions (line 132) | interface BrandedToastOptions {
  function brandedToast (line 155) | function brandedToast(options: BrandedToastOptions) {

FILE: src/components/ui/toaster.tsx
  function Toaster (line 7) | function Toaster() {

FILE: src/components/ui/typography.tsx
  type PolymorphicAsProp (line 8) | type PolymorphicAsProp<E extends ElementType> = {
  type PolymorphicProps (line 12) | type PolymorphicProps<E extends ElementType> = PropsWithChildren<Omit<Co...
  type TypographyCvaProps (line 35) | type TypographyCvaProps = VariantProps<typeof typographyVariants>;
  function Typography (line 55) | function Typography<E extends ElementType = typeof defaultElement>({

FILE: src/components/ui/use-toast.ts
  constant TOAST_LIMIT (line 8) | const TOAST_LIMIT = 1;
  constant TOAST_REMOVE_DELAY (line 9) | const TOAST_REMOVE_DELAY = 1000;
  type ToasterToast (line 11) | type ToasterToast = ToastProps & {
  function genId (line 31) | function genId() {
  type ActionType (line 36) | type ActionType = typeof _actionTypes;
  type Action (line 38) | type Action =
  type State (line 56) | interface State {
  function dispatch (line 135) | function dispatch(action: Action) {
  type Toast (line 142) | type Toast = Omit<ToasterToast, "id">;
  function toast (line 144) | function toast({ ...props }: Toast) {
  function useToast (line 173) | function useToast() {

FILE: src/components/ui/workout-lol.tsx
  type WorkoutLolProps (line 28) | interface WorkoutLolProps extends VariantProps<typeof workoutLolVariants> {

FILE: src/components/utils/ErrorBoundaries.tsx
  type Props (line 11) | type Props = {
  type State (line 18) | type State = {
  class ErrorBoundary (line 28) | class ErrorBoundary extends Component<Props, State> {
    method getDerivedStateFromError (line 34) | public static getDerivedStateFromError(error: Error): State {
    method componentDidCatch (line 40) | public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    method render (line 44) | public render() {

FILE: src/components/utils/TailwindIndicator.tsx
  function TailwindIndicator (line 1) | function TailwindIndicator() {

FILE: src/entities/exercise/types/exercise.types.ts
  type BaseExercise (line 6) | interface BaseExercise {
  type ExerciseAttribute (line 20) | interface ExerciseAttribute {
  type ExerciseWithAttributes (line 30) | interface ExerciseWithAttributes extends BaseExercise {
  type SuggestedSet (line 35) | interface SuggestedSet {

FILE: src/entities/program-session/types/program-session.types.ts
  type BaseProgramSession (line 7) | interface BaseProgramSession extends I18nText, I18nSlug {
  type ProgramWeek (line 17) | interface ProgramWeek extends I18nText {
  type ProgramExercise (line 26) | interface ProgramExercise extends I18nField<"instructions"> {
  type ProgramSessionWithExercises (line 36) | interface ProgramSessionWithExercises extends BaseProgramSession {

FILE: src/entities/program/types/program.types.ts
  type BaseProgram (line 7) | interface BaseProgram extends I18nText, I18nSlug {
  type ProgramReference (line 26) | interface ProgramReference {
  type ProgramI18nReference (line 33) | interface ProgramI18nReference extends I18nText, I18nSlug {
  type ProgramCoach (line 38) | interface ProgramCoach {
  type PublicProgramResponse (line 46) | interface PublicProgramResponse extends BaseProgram {
  type ProgramDetailResponse (line 51) | interface ProgramDetailResponse extends BaseProgram {
  type ProgramWeekWithSessions (line 58) | interface ProgramWeekWithSessions extends ProgramWeek {
  type SessionDetailResponse (line 63) | interface SessionDetailResponse {
  type ProgramProgressResponse (line 70) | interface ProgramProgressResponse {
  type SessionProgress (line 89) | interface SessionProgress {

FILE: src/entities/user/lib/display-name.ts
  function displayName (line 3) | function displayName(user: SessionUser): string {
  function displayFullName (line 12) | function displayFullName({ firstName, lastName }: { firstName: string; l...
  function displayFirstNameAndFirstLetterLastName (line 16) | function displayFirstNameAndFirstLetterLastName(user: SessionUser): stri...

FILE: src/entities/user/model/get-server-session-user.ts
  class AuthError (line 5) | class AuthError extends Error {
    method constructor (line 6) | constructor(message: string) {

FILE: src/entities/user/model/update-user-locale.ts
  type UpdateUserLocaleParams (line 8) | interface UpdateUserLocaleParams {
  function useUpdateUserLocale (line 12) | function useUpdateUserLocale() {

FILE: src/entities/user/model/use-auto-locale.ts
  function useAutoLocale (line 8) | function useAutoLocale() {

FILE: src/entities/user/schemas/update-user.schema.ts
  type UpdateUserInput (line 11) | type UpdateUserInput = z.infer<typeof updateUserSchema>;

FILE: src/entities/user/types/session-user.ts
  type SessionUser (line 5) | interface SessionUser extends Omit<User, "image" | "createdAt" | "update...
  type Session (line 9) | type Session = typeof authClient.$Infer.Session;

FILE: src/features/admin/layout/admin-header.tsx
  type AdminHeaderProps (line 18) | interface AdminHeaderProps {
  function AdminHeader (line 22) | function AdminHeader({ user }: AdminHeaderProps) {

FILE: src/features/admin/layout/admin-sidebar/ui/admin-header.tsx
  type AdminHeaderProps (line 18) | interface AdminHeaderProps {
  function AdminHeader (line 22) | function AdminHeader({ user }: AdminHeaderProps) {

FILE: src/features/admin/programs/actions/add-exercise.action.ts
  type SuggestedSetData (line 12) | interface SuggestedSetData {
  type AddExerciseData (line 20) | interface AddExerciseData {
  function addExerciseToSession (line 29) | async function addExerciseToSession(data: AddExerciseData) {
  function getExercises (line 87) | async function getExercises(search?: string): Promise<ExerciseWithAttrib...

FILE: src/features/admin/programs/actions/add-session.action.ts
  type AddSessionData (line 10) | interface AddSessionData {
  function addSessionToWeek (line 36) | async function addSessionToWeek(data: AddSessionData) {

FILE: src/features/admin/programs/actions/add-week.action.ts
  type AddWeekData (line 10) | interface AddWeekData {
  function addWeekToProgram (line 27) | async function addWeekToProgram(data: AddWeekData) {

FILE: src/features/admin/programs/actions/create-program.action.ts
  type CreateProgramData (line 11) | interface CreateProgramData {
  function createProgram (line 46) | async function createProgram(data: CreateProgramData) {

FILE: src/features/admin/programs/actions/delete-program.action.ts
  function deleteProgram (line 10) | async function deleteProgram(programId: string) {

FILE: src/features/admin/programs/actions/get-programs.action.ts
  function getPrograms (line 11) | async function getPrograms(visibility?: ProgramVisibility): Promise<Prog...
  function getProgramById (line 65) | async function getProgramById(id: string): Promise<ProgramWithFullDetail...

FILE: src/features/admin/programs/actions/update-exercise-sets.action.ts
  type SetData (line 10) | interface SetData {
  function updateExerciseSets (line 19) | async function updateExerciseSets(exerciseId: string, sets: SetData[]) {

FILE: src/features/admin/programs/actions/update-program-visibility.action.ts
  function updateProgramVisibility (line 10) | async function updateProgramVisibility(programId: string, visibility: Pr...

FILE: src/features/admin/programs/actions/update-program.action.ts
  type UpdateProgramData (line 11) | interface UpdateProgramData {
  function updateProgram (line 42) | async function updateProgram(programId: string, data: UpdateProgramData) {

FILE: src/features/admin/programs/actions/update-session.action.ts
  type UpdateSessionData (line 10) | interface UpdateSessionData {
  function updateSession (line 35) | async function updateSession(data: UpdateSessionData) {

FILE: src/features/admin/programs/actions/update-week.action.ts
  type UpdateWeekData (line 10) | interface UpdateWeekData {
  function updateWeek (line 25) | async function updateWeek(weekId: string, data: UpdateWeekData) {

FILE: src/features/admin/programs/types/program.types.ts
  type ProgramWithFullDetails (line 16) | type ProgramWithFullDetails = Program & {
  type ProgramWithStats (line 34) | type ProgramWithStats = Program & {
  type WeekWithSessions (line 53) | type WeekWithSessions = ProgramWeek & {
  type SessionWithExercises (line 63) | type SessionWithExercises = ProgramSession & {
  type ExerciseWithAttributes (line 71) | type ExerciseWithAttributes = Exercise & {

FILE: src/features/admin/programs/ui/add-exercise-modal.tsx
  type ExerciseFormData (line 30) | type ExerciseFormData = z.infer<typeof exerciseSchema>;
  type AddExerciseModalProps (line 32) | interface AddExerciseModalProps {
  function AddExerciseModal (line 39) | function AddExerciseModal({ open, onOpenChange, sessionId, nextOrder }: ...

FILE: src/features/admin/programs/ui/add-session-modal.tsx
  type SessionFormData (line 38) | type SessionFormData = z.infer<typeof sessionSchema>;
  type AddSessionModalProps (line 40) | interface AddSessionModalProps {
  constant EQUIPMENT_OPTIONS (line 47) | const EQUIPMENT_OPTIONS = [
  function AddSessionModal (line 57) | function AddSessionModal({ open, onOpenChange, weekId, nextSessionNumber...

FILE: src/features/admin/programs/ui/add-week-modal.tsx
  type WeekFormData (line 25) | type WeekFormData = z.infer<typeof weekSchema>;
  type AddWeekModalProps (line 27) | interface AddWeekModalProps {
  function AddWeekModal (line 34) | function AddWeekModal({ open, onOpenChange, programId, nextWeekNumber }:...

FILE: src/features/admin/programs/ui/create-program-button.tsx
  function CreateProgramButton (line 8) | function CreateProgramButton() {

FILE: src/features/admin/programs/ui/create-program-form.tsx
  type ProgramFormData (line 48) | type ProgramFormData = z.infer<typeof programSchema>;
  type CreateProgramFormProps (line 50) | interface CreateProgramFormProps {
  constant EQUIPMENT_OPTIONS (line 57) | const EQUIPMENT_OPTIONS = [
  constant TYPE_OPTIONS (line 67) | const TYPE_OPTIONS = [
  function CreateProgramForm (line 75) | function CreateProgramForm({ currentStep, onStepComplete, onSuccess, onC...

FILE: src/features/admin/programs/ui/create-program-modal.tsx
  type CreateProgramModalProps (line 8) | interface CreateProgramModalProps {
  constant STEPS (line 13) | const STEPS = [
  function CreateProgramModal (line 19) | function CreateProgramModal({ open, onOpenChange }: CreateProgramModalPr...

FILE: src/features/admin/programs/ui/delete-program-button.tsx
  type DeleteProgramButtonProps (line 9) | interface DeleteProgramButtonProps {
  function DeleteProgramButton (line 14) | function DeleteProgramButton({ programId, programTitle }: DeleteProgramB...

FILE: src/features/admin/programs/ui/edit-program-modal.tsx
  type EditProgramModalProps (line 13) | interface EditProgramModalProps {
  function EditProgramModal (line 48) | function EditProgramModal({ program, open, onOpenChange }: EditProgramMo...

FILE: src/features/admin/programs/ui/edit-session-modal.tsx
  type SessionFormData (line 39) | type SessionFormData = z.infer<typeof sessionSchema>;
  type EditSessionModalProps (line 41) | interface EditSessionModalProps {
  constant EQUIPMENT_OPTIONS (line 47) | const EQUIPMENT_OPTIONS = [
  function EditSessionModal (line 57) | function EditSessionModal({ open, onOpenChange, session }: EditSessionMo...

FILE: src/features/admin/programs/ui/edit-sets-modal.tsx
  type EditSetsModalProps (line 13) | interface EditSetsModalProps {
  type SetData (line 22) | interface SetData {
  function EditSetsModal (line 31) | function EditSetsModal({ exercise, open, onOpenChange }: EditSetsModalPr...

FILE: src/features/admin/programs/ui/edit-week-modal.tsx
  type EditWeekModalProps (line 9) | interface EditWeekModalProps {
  function EditWeekModal (line 30) | function EditWeekModal({ week, open, onOpenChange }: EditWeekModalProps) {

FILE: src/features/admin/programs/ui/program-builder.tsx
  type ProgramBuilderProps (line 14) | interface ProgramBuilderProps {
  function ProgramBuilder (line 18) | function ProgramBuilder({ program }: ProgramBuilderProps) {

FILE: src/features/admin/programs/ui/programs-list.tsx
  function ProgramsList (line 9) | async function ProgramsList() {

FILE: src/features/admin/programs/ui/session-card.tsx
  type SessionCardProps (line 13) | interface SessionCardProps {
  function SessionCard (line 17) | function SessionCard({ session }: SessionCardProps) {

FILE: src/features/admin/programs/ui/visibility-badge.tsx
  type VisibilityBadgeProps (line 10) | interface VisibilityBadgeProps {
  function VisibilityBadge (line 33) | function VisibilityBadge({ programId, currentVisibility }: VisibilityBad...

FILE: src/features/admin/programs/ui/week-card.tsx
  type WeekCardProps (line 11) | interface WeekCardProps {
  function WeekCard (line 15) | function WeekCard({ week }: WeekCardProps) {

FILE: src/features/admin/users/list/ui/users-table.tsx
  type SortableColumn (line 25) | type SortableColumn = (typeof sortableColumns)[number];
  type OrderableColumn (line 28) | type OrderableColumn = (typeof orderableColumns)[number];
  type UsersTableProps (line 30) | interface UsersTableProps {
  function UsersTable (line 34) | function UsersTable({ initialUsers }: UsersTableProps) {

FILE: src/features/ads/hooks/useUserSubscription.ts
  function useUserSubscription (line 5) | function useUserSubscription() {

FILE: src/features/auth/forgot-password/forgot-password.schema.ts
  type ForgotPasswordSchema (line 7) | type ForgotPasswordSchema = z.infer<typeof forgotPasswordSchema>;

FILE: src/features/auth/reset-password/model/useResetPassword.ts
  type UseResetPasswordResult (line 10) | interface UseResetPasswordResult {

FILE: src/features/auth/reset-password/schema/reset-password.schema.ts
  type ResetPasswordFormData (line 18) | type ResetPasswordFormData = z.infer<typeof resetPasswordSchema>;

FILE: src/features/auth/reset-password/ui/reset-password-form.tsx
  function ResetPasswordForm (line 14) | function ResetPasswordForm() {

FILE: src/features/auth/signin/schema/signin.schema.ts
  type LoginSchema (line 8) | type LoginSchema = z.infer<typeof loginSchema>;

FILE: src/features/auth/signin/ui/CredentialsLoginForm.tsx
  function CredentialsLoginForm (line 19) | function CredentialsLoginForm({ className, ...props }: React.ComponentPr...

FILE: src/features/auth/signup/schema/signup.schema.ts
  type SignUpSchema (line 11) | type SignUpSchema = z.infer<typeof signUpSchema>;

FILE: src/features/auth/signup/ui/signup-form.tsx
  function onSubmit (line 27) | async function onSubmit(values: SignUpSchema) {

FILE: src/features/auth/ui/ProviderButton.tsx
  type ProviderButtonProps (line 22) | type ProviderButtonProps = {

FILE: src/features/auth/verify-email/constants.ts
  constant COUNTDOWN_TIME (line 1) | const COUNTDOWN_TIME = process.env.NODE_ENV === "development" ? 3 : 60;

FILE: src/features/consent-banner/ui/consent-banner.tsx
  function ConsentBanner (line 11) | function ConsentBanner() {

FILE: src/features/contact-feedback/model/contact-feedback.schema.ts
  type ContactFeedbackSchemaType (line 9) | type ContactFeedbackSchemaType = z.infer<typeof ContactFeedbackSchema>;

FILE: src/features/contact-feedback/ui/contact-feedback-popover.tsx
  type ContactFeedbackPopoverProps (line 19) | type ContactFeedbackPopoverProps = PropsWithChildren<{}>;

FILE: src/features/contact/support/ContactSupportDialog.tsx
  type ContactSupportDialogProps (line 22) | type ContactSupportDialogProps = PropsWithChildren<{

FILE: src/features/contact/support/contact-support.schema.ts
  type ContactSupportSchemaType (line 9) | type ContactSupportSchemaType = z.infer<typeof ContactSupportSchema>;

FILE: src/features/dialogs-provider/DialogProvider.tsx
  type DialogType (line 13) | type DialogType = ConfirmationDialogProps & {
  type DialogStore (line 17) | type DialogStore = {

FILE: src/features/dialogs-provider/DialogProviderDialog.tsx
  type ConfirmationDialogProps (line 17) | type ConfirmationDialogProps = {

FILE: src/features/email/EmailForm.tsx
  type EmailFormProps (line 17) | type EmailFormProps = {

FILE: src/features/email/email.schema.ts
  type EmailActionSchemaType (line 7) | type EmailActionSchemaType = z.infer<typeof EmailActionSchema>;

FILE: src/features/layout/BottomNavigation.tsx
  function BottomNavigation (line 12) | function BottomNavigation() {

FILE: src/features/layout/Footer.tsx
  constant SOCIAL_LINKS (line 12) | const SOCIAL_LINKS = [

FILE: src/features/layout/model/use-sidebar.store.tsx
  type SidebarState (line 3) | interface SidebarState {

FILE: src/features/layout/nav-link.tsx
  type IProp (line 8) | interface IProp {
  function NavLink (line 20) | function NavLink({

FILE: src/features/layout/page-heading.tsx
  type PageHeadingProps (line 4) | type PageHeadingProps = {

FILE: src/features/layout/workout-streak-header.tsx
  constant DEFAULT_STREAK_COUNT (line 17) | const DEFAULT_STREAK_COUNT = 5;
  type WorkoutStreakHeaderProps (line 22) | interface WorkoutStreakHeaderProps {
  type DayStreakData (line 31) | interface DayStreakData {
  type StreakData (line 43) | interface StreakData {
  function WorkoutStreakHeader (line 59) | function WorkoutStreakHeader({ className, streakCount = DEFAULT_STREAK_C...

FILE: src/features/leaderboard/actions/get-top-workout-users.action.ts
  constant LIMIT_TOP_USERS (line 11) | const LIMIT_TOP_USERS = 20;
  type LeaderboardPeriod (line 13) | type LeaderboardPeriod = "all-time" | "weekly" | "monthly";

FILE: src/features/leaderboard/hooks/use-top-workout-users.ts
  type UseTopWorkoutUsersOptions (line 7) | interface UseTopWorkoutUsersOptions {
  function useTopWorkoutUsers (line 12) | function useTopWorkoutUsers(options: UseTopWorkoutUsersOptions = {}) {

FILE: src/features/leaderboard/hooks/use-user-position.ts
  type UseUserPositionOptions (line 8) | interface UseUserPositionOptions {
  function useUserPosition (line 14) | function useUserPosition({ userId, period, enabled = true }: UseUserPosi...

FILE: src/features/leaderboard/lib/utils.ts
  constant PARIS_TZ (line 9) | const PARIS_TZ = "Europe/Paris";
  type LeaderboardPeriod (line 11) | type LeaderboardPeriod = "all-time" | "weekly" | "monthly";
  function getDateRangeForPeriod (line 13) | function getDateRangeForPeriod(period: LeaderboardPeriod): { startDate: ...

FILE: src/features/leaderboard/models/types.ts
  type TopWorkoutUser (line 1) | interface TopWorkoutUser {

FILE: src/features/leaderboard/ui/leaderboard-page.tsx
  function LeaderboardPage (line 15) | function LeaderboardPage() {

FILE: src/features/leaderboard/ui/user-leaderboard-position.tsx
  function UserLeaderboardPosition (line 12) | function UserLeaderboardPosition() {

FILE: src/features/premium/ui/conversion-flow-notification.tsx
  function ConversionFlowNotification (line 9) | function ConversionFlowNotification() {

FILE: src/features/premium/ui/feature-comparison-table.tsx
  type Feature (line 7) | interface Feature {
  type FeatureCategory (line 13) | interface FeatureCategory {
  function FeatureComparisonTable (line 18) | function FeatureComparisonTable() {

FILE: src/features/premium/ui/premium-upgrade-card.tsx
  function PremiumUpgradeCard (line 24) | function PremiumUpgradeCard() {

FILE: src/features/premium/ui/pricing-faq.tsx
  function PricingFAQ (line 8) | function PricingFAQ() {

FILE: src/features/premium/ui/pricing-hero-section.tsx
  function PricingHeroSection (line 8) | function PricingHeroSection() {

FILE: src/features/premium/ui/pricing-testimonials.tsx
  type Testimonial (line 7) | interface Testimonial {
  function PricingTestimonials (line 18) | function PricingTestimonials() {

FILE: src/features/programs/actions/complete-program-session.action.ts
  function completeProgramSession (line 9) | async function completeProgramSession(sessionProgressId: string, workout...

FILE: src/features/programs/actions/enroll-program.action.ts
  function enrollInProgram (line 12) | async function enrollInProgram(programId: string) {

FILE: src/features/programs/actions/get-program-by-slug.action.ts
  type ProgramDetail (line 7) | interface ProgramDetail {
  function getProgramBySlug (line 79) | async function getProgramBySlug(slug: string): Promise<ProgramDetail | n...

FILE: src/features/programs/actions/get-program-progress-by-slug.action.ts
  function getProgramProgressBySlug (line 9) | async function getProgramProgressBySlug(slug: string) {

FILE: src/features/programs/actions/get-program-progress.action.ts
  function getProgramProgress (line 8) | async function getProgramProgress(programId: string) {

FILE: src/features/programs/actions/get-public-programs.action.ts
  type PublicProgram (line 7) | interface PublicProgram {
  function getPublicPrograms (line 47) | async function getPublicPrograms(): Promise<PublicProgram[]> {

FILE: src/features/programs/actions/get-session-by-slug.action.ts
  function getSessionBySlug (line 14) | async function getSessionBySlug(

FILE: src/features/programs/actions/get-sitemap-data.action.ts
  type SitemapProgramData (line 7) | interface SitemapProgramData {
  function getSitemapData (line 29) | async function getSitemapData(): Promise<SitemapProgramData[]> {

FILE: src/features/programs/actions/start-program-session.action.ts
  function startProgramSession (line 9) | async function startProgramSession(enrollmentId: string, sessionId: stri...

FILE: src/features/programs/hooks/use-program-share.ts
  type UseProgramShareProps (line 8) | interface UseProgramShareProps {
  function useProgramShare (line 17) | function useProgramShare({ programTitle }: UseProgramShareProps) {

FILE: src/features/programs/lib/program-metadata.ts
  function generateProgramSEOKeywords (line 9) | function generateProgramSEOKeywords(program: ProgramDetail, locale: Loca...

FILE: src/features/programs/lib/session-metadata.ts
  function generateSessionSEOKeywords (line 9) | function generateSessionSEOKeywords(session: ProgramSessionWithExercises...
  function generateSessionMetadata (line 57) | function generateSessionMetadata(session: ProgramSessionWithExercises, p...

FILE: src/features/programs/lib/suggested-sets-helpers.ts
  type CreateSuggestedSetData (line 3) | interface CreateSuggestedSetData {
  constant SUGGESTED_SET_TEMPLATES (line 12) | const SUGGESTED_SET_TEMPLATES = {

FILE: src/features/programs/lib/translations-mapper.ts
  function getProgramTitle (line 20) | function getProgramTitle(program: ProgramDetail | PublicProgram | Progra...
  function getProgramDescription (line 24) | function getProgramDescription(program: ProgramDetail | PublicProgram, l...
  function getProgramSlug (line 28) | function getProgramSlug(program: ProgramDetail | PublicProgram, locale: ...
  function getSessionTitle (line 32) | function getSessionTitle(
  function getSessionDescription (line 39) | function getSessionDescription(session: any, locale: Locale): string {
  function getSessionSlug (line 43) | function getSessionSlug(session: any, locale: Locale): string {
  function getExerciseName (line 47) | function getExerciseName(exercise: any, locale: Locale): string {
  function getExerciseDescription (line 51) | function getExerciseDescription(exercise: any, locale: Locale): string {
  function getExerciseInstructions (line 55) | function getExerciseInstructions(exercise: any, locale: Locale): string {

FILE: src/features/programs/ui/program-card.tsx
  type ProgramCardProps (line 13) | interface ProgramCardProps {
  function ProgramCard (line 19) | async function ProgramCard({ program, size = "medium", locale }: Program...

FILE: src/features/programs/ui/program-detail-page.tsx
  type ProgramDetailPageProps (line 45) | interface ProgramDetailPageProps {
  function ProgramDetailPage (line 50) | function ProgramDetailPage({ program, isAuthenticated }: ProgramDetailPa...

FILE: src/features/programs/ui/program-progress.tsx
  type ProgramProgressProps (line 10) | interface ProgramProgressProps {
  function ProgramProgress (line 14) | function ProgramProgress({ programId }: ProgramProgressProps) {

FILE: src/features/programs/ui/programs-page.tsx
  type ProgramsPageProps (line 12) | interface ProgramsPageProps {
  function ProgramsPage (line 16) | async function ProgramsPage({ locale }: ProgramsPageProps) {

FILE: src/features/programs/ui/session-access-guard.tsx
  type SessionAccessGuardProps (line 10) | interface SessionAccessGuardProps {
  function SessionAccessGuard (line 23) | function SessionAccessGuard({

FILE: src/features/programs/ui/share-button.tsx
  type ShareButtonProps (line 7) | interface ShareButtonProps {
  function ShareButton (line 19) | function ShareButton({

FILE: src/features/programs/ui/welcome-modal.tsx
  type WelcomeModalProps (line 11) | interface WelcomeModalProps {
  function WelcomeModal (line 21) | function WelcomeModal({

FILE: src/features/release-notes/hooks/use-changelog-notification.ts
  type UseChangelogNotificationReturn (line 6) | interface UseChangelogNotificationReturn {
  type ChangelogNotificationConfig (line 23) | interface ChangelogNotificationConfig {
  function useChangelogNotification (line 32) | function useChangelogNotification(config: ChangelogNotificationConfig = ...

FILE: src/features/release-notes/lib/date-utils.ts
  function isValidISOTimestamp (line 10) | function isValidISOTimestamp(timestamp: string): boolean {
  function isValidDateString (line 28) | function isValidDateString(dateString: string): boolean {
  function compareDateStrings (line 53) | function compareDateStrings(date1: string, date2: string): number {
  function getCurrentISOTimestamp (line 68) | function getCurrentISOTimestamp(): string {

FILE: src/features/release-notes/model/changelog-notification.local.ts
  constant CHANGELOG_NOTIFICATION_STORAGE_KEY (line 5) | const CHANGELOG_NOTIFICATION_STORAGE_KEY = "lastSeenChangelog";
  constant RATE_LIMIT_WINDOW (line 8) | const RATE_LIMIT_WINDOW = 1000;
  function shouldRateLimit (line 14) | function shouldRateLimit(): boolean {
  function updateRateLimitTimestamp (line 23) | function updateRateLimitTimestamp(): void {
  function sanitizeTimestamp (line 30) | function sanitizeTimestamp(timestamp: string): string | null {
  function getLastSeenTimestamp (line 45) | function getLastSeenTimestamp(): string | null {
  function setLastSeenTimestamp (line 64) | function setLastSeenTimestamp(timestamp: string): void {
  function getLatestReleaseDate (line 90) | function getLatestReleaseDate(notes: ReleaseNote[]): string | null {
  function hasNewReleaseNotes (line 106) | function hasNewReleaseNotes(notes: ReleaseNote[], lastSeenTimestamp: str...
  function markChangelogAsSeen (line 141) | function markChangelogAsSeen(): void {

FILE: src/features/release-notes/model/notes.ts
  type ReleaseNote (line 1) | interface ReleaseNote {

FILE: src/features/release-notes/types/notification.ts
  type ChangelogNotificationConfig (line 4) | interface ChangelogNotificationConfig {
  type NotificationCheckResult (line 16) | interface NotificationCheckResult {
  type UseChangelogNotificationReturn (line 30) | interface UseChangelogNotificationReturn {
  type ChangelogNotificationBadgeProps (line 48) | interface ChangelogNotificationBadgeProps {
  type EnhancedReleaseNotesDialogProps (line 64) | interface EnhancedReleaseNotesDialogProps {
  type ChangelogNotificationLocalStorage (line 78) | interface ChangelogNotificationLocalStorage {
  type DateUtils (line 94) | interface DateUtils {
  type NotificationErrorType (line 108) | enum NotificationErrorType {
  class NotificationError (line 119) | class NotificationError extends Error {
    method constructor (line 120) | constructor(

FILE: src/features/release-notes/ui/changelog-notification-badge.tsx
  type ChangelogNotificationBadgeProps (line 5) | interface ChangelogNotificationBadgeProps {
  function ChangelogNotificationBadge (line 32) | function ChangelogNotificationBadge({

FILE: src/features/release-notes/ui/release-notes-dialog.tsx
  type ReleaseNotesDialogProps (line 15) | interface ReleaseNotesDialogProps {
  function ReleaseNotesDialog (line 24) | function ReleaseNotesDialog({ onOpen, onClose, showNotificationBadge = t...

FILE: src/features/statistics/components/ExerciseSelection.tsx
  method getAllExercises (line 20) | async getAllExercises(params: { page?: number; limit?: number; search?: ...
  type ExerciseSelectionProps (line 27) | interface ExerciseSelectionProps {
  constant MUSCLES (line 33) | const MUSCLES = ["CHEST", "BACK", "SHOULDERS", "BICEPS", "TRICEPS", "LEG...

FILE: src/features/statistics/components/ExerciseStatisticsTab.tsx
  type ExerciseStatisticsTabProps (line 19) | interface ExerciseStatisticsTabProps {
  function ExerciseCharts (line 26) | function ExerciseCharts({ timeframe, exerciseId, unit = "kg", className ...

FILE: src/features/statistics/components/ExercisesBrowser.tsx
  constant MUSCLE_GROUPS (line 45) | const MUSCLE_GROUPS = [

FILE: src/features/statistics/components/OneRepMaxChart.tsx
  type OneRepMaxChartProps (line 14) | interface OneRepMaxChartProps {
  function OneRepMaxChart (line 24) | function OneRepMaxChart({

FILE: src/features/statistics/components/StatisticsPreviewOverlay.tsx
  type StatisticsPreviewOverlayProps (line 10) | interface StatisticsPreviewOverlayProps {

FILE: src/features/statistics/components/TimeframeSelector.tsx
  type TimeframeSelectorProps (line 10) | interface TimeframeSelectorProps {
  constant TIMEFRAME_OPTIONS (line 16) | const TIMEFRAME_OPTIONS = [
  function TimeframeSelector (line 23) | function TimeframeSelector({ selected, onSelect, className }: TimeframeS...

FILE: src/features/statistics/components/VolumeChart.tsx
  type VolumeChartProps (line 12) | interface VolumeChartProps {
  function VolumeChart (line 19) | function VolumeChart({ data, height = 300, className }: VolumeChartProps) {

FILE: src/features/statistics/components/WeightProgressionChart.tsx
  type WeightProgressionChartProps (line 13) | interface WeightProgressionChartProps {
  function WeightProgressionChart (line 21) | function WeightProgressionChart({ data, height = 300, unit = "kg", class...

FILE: src/features/statistics/hooks/use-chart-theme.ts
  function useChartTheme (line 3) | function useChartTheme() {

FILE: src/features/statistics/hooks/use-exercise-statistics.ts
  constant STATISTICS_QUERY_KEYS (line 8) | const STATISTICS_QUERY_KEYS = {
  function fetchWeightProgression (line 20) | async function fetchWeightProgression(exerciseId: string, timeframe: Sta...
  function fetchOneRepMax (line 33) | async function fetchOneRepMax(exerciseId: string, timeframe: StatisticsT...
  function fetchVolume (line 44) | async function fetchVolume(exerciseId: string, timeframe: StatisticsTime...
  function useWeightProgression (line 56) | function useWeightProgression(exerciseId: string, timeframe: StatisticsT...
  function useOneRepMax (line 67) | function useOneRepMax(exerciseId: string, timeframe: StatisticsTimeframe...
  function useVolumeData (line 78) | function useVolumeData(exerciseId: string, timeframe: StatisticsTimefram...

FILE: src/features/statistics/types/index.ts
  type ChartDimensions (line 15) | interface ChartDimensions {

FILE: src/features/theme/ThemeProviders.tsx
  function ThemeProvider (line 6) | function ThemeProvider({ children, ...props }: ThemeProviderProps) {

FILE: src/features/theme/ThemeToggle.tsx
  function ThemeToggle (line 8) | function ThemeToggle() {

FILE: src/features/theme/ui/ThemeSynchronizer.tsx
  function ThemeSynchronizer (line 10) | function ThemeSynchronizer() {

FILE: src/features/update-password/model/update-password.action.ts
  function updateUserPassword (line 16) | async function updateUserPassword(

FILE: src/features/update-password/ui/password-form.tsx
  type PasswordFormValues (line 25) | type PasswordFormValues = z.infer<typeof passwordFormSchema>;
  function PasswordForm (line 27) | function PasswordForm() {

FILE: src/features/workout-builder/actions/get-exercises.action.ts
  function shuffleArray (line 11) | function shuffleArray<T>(array: T[]): T[] {

FILE: src/features/workout-builder/hooks/use-exercises.ts
  type UseExercisesProps (line 8) | interface UseExercisesProps {
  function useExercises (line 14) | function useExercises({ equipment, muscles, enabled = true }: UseExercis...

FILE: src/features/workout-builder/hooks/use-favorites-modal.ts
  type ExerciseWithAttributes (line 8) | interface ExerciseWithAttributes {
  type MuscleGroup (line 19) | interface MuscleGroup {
  type UseFavoritesModalProps (line 24) | interface UseFavoritesModalProps {

FILE: src/features/workout-builder/hooks/use-sync-favorite-exercises.ts
  type SyncState (line 10) | interface SyncState {
  function useSyncFavoriteExercises (line 15) | function useSyncFavoriteExercises() {

FILE: src/features/workout-builder/hooks/use-workout-session.ts
  function useWorkoutSession (line 5) | function useWorkoutSession() {

FILE: src/features/workout-builder/hooks/use-workout-stepper.ts
  function useWorkoutStepper (line 5) | function useWorkoutStepper() {

FILE: src/features/workout-builder/model/equipment-config.ts
  constant EQUIPMENT_CONFIG (line 14) | const EQUIPMENT_CONFIG: EquipmentItem[] = [
  function getEquipmentByValue (line 73) | function getEquipmentByValue(value: ExerciseAttributeValueEnum): Equipme...
  function getEquipmentLabel (line 77) | function getEquipmentLabel(value: ExerciseAttributeValueEnum): string {

FILE: src/features/workout-builder/model/favorite-exercises-synchronizer.tsx
  function FavoriteExercisesSynchronizer (line 9) | function FavoriteExercisesSynchronizer() {

FILE: src/features/workout-builder/model/favorite-exercises.local.ts
  type SyncStatus (line 1) | type SyncStatus = "local" | "synced" | "deleteOnSync";
  type LocalFavoriteExercise (line 3) | interface LocalFavoriteExercise {
  constant FAVORITE_EXERICSES_STORAGE_KEY (line 8) | const FAVORITE_EXERICSES_STORAGE_KEY = "favoriteExercises";
  function getAll (line 10) | function getAll(): LocalFavoriteExercise[] {
  function saveAll (line 20) | function saveAll(favorites: LocalFavoriteExercise[]) {
  function removeById (line 28) | function removeById(exerciseId: string) {
  function add (line 46) | function add(exerciseId: string) {

FILE: src/features/workout-builder/model/workout-builder.store.ts
  type WorkoutBuilderState (line 9) | interface WorkoutBuilderState {

FILE: src/features/workout-builder/schema/get-exercises.schema.ts
  type GetExercisesInput (line 10) | type GetExercisesInput = z.infer<typeof getExercisesSchema>;

FILE: src/features/workout-builder/types/index.ts
  type WorkoutBuilderState (line 9) | interface WorkoutBuilderState {
  type WorkoutBuilderStep (line 16) | type WorkoutBuilderStep = 1 | 2 | 3;
  type StepperStepProps (line 18) | interface StepperStepProps {
  type EquipmentItem (line 26) | interface EquipmentItem {
  type WorkoutBuilderExerciseWithAttributes (line 35) | type WorkoutBuilderExerciseWithAttributes = ExerciseWithAttributes & {
  type ExerciseWithAttributesAndSets (line 39) | type ExerciseWithAttributesAndSets = ExerciseWithAttributes & {
  type ExercisesByMuscle (line 43) | interface ExercisesByMuscle {

FILE: src/features/workout-builder/ui/add-exercise-modal.tsx
  type AddExerciseModalProps (line 17) | interface AddExerciseModalProps {
  type ExerciseWithAttributes (line 23) | interface ExerciseWithAttributes {
  type MuscleGroup (line 34) | interface MuscleGroup {

FILE: src/features/workout-builder/ui/equipment-selection.tsx
  type EquipmentSelectionProps (line 16) | interface EquipmentSelectionProps {
  type EquipmentCardProps (line 22) | interface EquipmentCardProps {
  function EquipmentCard (line 28) | function EquipmentCard({ equipment, isSelected, onToggle }: EquipmentCar...
  function EquipmentSelection (line 120) | function EquipmentSelection({ onToggleEquipment, selectedEquipment }: Eq...

FILE: src/features/workout-builder/ui/exercise-card.tsx
  type ExerciseCardProps (line 18) | interface ExerciseCardProps {
  function ExerciseCard (line 26) | function ExerciseCard({ exercise, muscle, onShuffle, onPick, onDelete }:...

FILE: src/features/workout-builder/ui/exercise-list-item.tsx
  constant MUSCLE_CONFIGS (line 15) | const MUSCLE_CONFIGS: Record<string, string> = {
  type ExerciseListItemProps (line 24) | interface ExerciseListItemProps {

FILE: src/features/workout-builder/ui/exercise-pick-modal.tsx
  type ExercisePickModalProps (line 13) | interface ExercisePickModalProps {
  function ExercisePickModal (line 21) | function ExercisePickModal({ exercise, muscle, isOpen, onClose, onConfir...

FILE: src/features/workout-builder/ui/exercise-video-modal.tsx
  type ExerciseVideoModalProps (line 17) | interface ExerciseVideoModalProps {
  function ExerciseVideoModal (line 23) | function ExerciseVideoModal({ open, onOpenChange, exercise }: ExerciseVi...

FILE: src/features/workout-builder/ui/exercises-selection.tsx
  type ExercisesSelectionProps (line 15) | interface ExercisesSelectionProps {

FILE: src/features/workout-builder/ui/favorite-button.tsx
  type FavoriteButtonProps (line 6) | interface FavoriteButtonProps {

FILE: src/features/workout-builder/ui/favorite-exercise-button.tsx
  type FavoriteExerciseButtonProps (line 17) | interface FavoriteExerciseButtonProps {
  function FavoriteExerciseButton (line 22) | function FavoriteExerciseButton({ exerciseId, className }: FavoriteExerc...

FILE: src/features/workout-builder/ui/muscle-selection.tsx
  type MuscleSelectionProps (line 22) | interface MuscleSelectionProps {
  function MuscleSelection (line 1207) | function MuscleSelection({ onToggleMuscle, selectedMuscles }: MuscleSele...

FILE: src/features/workout-builder/ui/quit-workout-dialog.tsx
  type QuitWorkoutDialogProps (line 9) | interface QuitWorkoutDialogProps {
  function QuitWorkoutDialog (line 17) | function QuitWorkoutDialog({ isOpen, onClose, onQuitWithoutSave, exercis...

FILE: src/features/workout-builder/ui/stepper-header.tsx
  type StepperHeaderProps (line 10) | interface StepperHeaderProps {
  function StepperStep (line 16) | function StepperStep({
  function StepperHeader (line 120) | function StepperHeader({ steps, currentStep, onStepClick }: StepperHeade...

FILE: src/features/workout-builder/ui/workout-stepper-footer.tsx
  function WorkoutBuilderFooter (line 7) | function WorkoutBuilderFooter({

FILE: src/features/workout-builder/ui/workout-stepper.tsx
  function WorkoutStepper (line 33) | function WorkoutStepper() {

FILE: src/features/workout-session/hooks/use-donation-modal.ts
  function useDonationModal (line 5) | function useDonationModal() {

FILE: src/features/workout-session/lib/workout-set-labels.ts
  function getWorkoutSetTypeLabels (line 5) | function getWorkoutSetTypeLabels(t: TFunction): Record<WorkoutSetType, s...

FILE: src/features/workout-session/model/use-sync-workout-sessions.ts
  type SyncState (line 10) | interface SyncState {
  constant SYNC_INTERVAL (line 16) | const SYNC_INTERVAL = 5 * 60 * 1000;
  function useSyncWorkoutSessions (line 18) | function useSyncWorkoutSessions() {

FILE: src/features/workout-session/model/use-workout-session.ts
  function useWorkoutSession (line 5) | function useWorkoutSession() {

FILE: src/features/workout-session/model/use-workout-sessions.ts
  function useWorkoutSessions (line 8) | function useWorkoutSessions() {

FILE: src/features/workout-session/model/workout-session.store.ts
  type WorkoutSessionProgress (line 10) | interface WorkoutSessionProgress {
  type WorkoutSessionState (line 20) | interface WorkoutSessionState {

FILE: src/features/workout-session/types/workout-set.ts
  type WorkoutSetType (line 3) | type WorkoutSetType = "TIME" | "WEIGHT" | "REPS" | "BODYWEIGHT" | "NA";
  type WorkoutSetUnit (line 4) | type WorkoutSetUnit = "kg" | "lbs";
  type WorkoutSet (line 6) | interface WorkoutSet {
  type WorkoutSessionExercise (line 16) | interface WorkoutSessionExercise extends ExerciseWithAttributes {

FILE: src/features/workout-session/ui/donation-modal.tsx
  type DonationModalProps (line 9) | interface DonationModalProps {
  function DonationModal (line 14) | function DonationModal({ isOpen, onClose }: DonationModalProps) {

FILE: src/features/workout-session/ui/workout-session-header.tsx
  type WorkoutSessionHeaderProps (line 14) | interface WorkoutSessionHeaderProps {
  function WorkoutSessionHeader (line 18) | function WorkoutSessionHeader({ onQuitWorkout }: WorkoutSessionHeaderPro...

FILE: src/features/workout-session/ui/workout-session-heatmap.tsx
  type Props (line 6) | interface Props {
  constant DEFAULT_PANEL_COLORS (line 12) | const DEFAULT_PANEL_COLORS = [
  constant PANEL_SIZE (line 20) | const PANEL_SIZE = 18;
  constant PANEL_MARGIN (line 21) | const PANEL_MARGIN = 2;
  constant WEEK_LABEL_WIDTH (line 22) | const WEEK_LABEL_WIDTH = 18;
  constant MONTH_LABEL_HEIGHT (line 23) | const MONTH_LABEL_HEIGHT = 18;
  constant MIN_COLUMNS (line 24) | const MIN_COLUMNS = 10;
  constant MAX_COLUMNS (line 25) | const MAX_COLUMNS = 53;
  function updateColumns (line 79) | function updateColumns() {
  function makeCalendarData (line 92) | function makeCalendarData(history: { [k: string]: number }, lastDay: str...

FILE: src/features/workout-session/ui/workout-session-list.tsx
  constant BADGE_COLORS (line 13) | const BADGE_COLORS = [
  function WorkoutSessionList (line 22) | function WorkoutSessionList() {

FILE: src/features/workout-session/ui/workout-session-set.tsx
  type WorkoutSetRowProps (line 9) | interface WorkoutSetRowProps {
  function WorkoutSessionSet (line 17) | function WorkoutSessionSet({ set, setIndex, onChange, onFinish, onRemove...

FILE: src/features/workout-session/ui/workout-session-sets.tsx
  function WorkoutSessionSets (line 24) | function WorkoutSessionSets({

FILE: src/features/workout-session/ui/workout-session-timer.tsx
  function WorkoutSessionTimer (line 11) | function WorkoutSessionTimer() {

FILE: src/index.d.ts
  type Navigator (line 3) | interface Navigator {

FILE: src/shared/api/createHandler.ts
  type HandleReturnedServerErrorFn (line 6) | type HandleReturnedServerErrorFn = (e: unknown) => NextResponse | string;
  type MiddlewareFn (line 8) | type MiddlewareFn<TContext> = (req: NextRequest) => Promise<TContext>;
  type CreateHandlerParams (line 10) | type CreateHandlerParams<TContext> = {
  type HandlerParams (line 15) | type HandlerParams<TBody, TParams, TSearchParams> = {
  type Callback (line 21) | type Callback<TContext, TBody, TParams, TSearchParams> = (params: {
  class CustomZodError (line 29) | class CustomZodError extends Error {
    method constructor (line 33) | constructor(type: "body" | "params", zodError: ZodError, received?: un...
  function createSafeHandler (line 41) | function createSafeHandler<TContext>({ middleware, handleReturnedServerE...

FILE: src/shared/api/handlers.ts
  class HandlerError (line 9) | class HandlerError extends Error {
    method constructor (line 11) | constructor(message: string, status?: number) {
  method middleware (line 41) | async middleware() {

FILE: src/shared/api/mobile-auth.ts
  function getMobileCompatibleSession (line 24) | async function getMobileCompatibleSession(req: NextRequest) {
  function hasValidUser (line 50) | function hasValidUser(session: any): session is { user: { id: string; em...

FILE: src/shared/api/mobile-cookie-utils.ts
  function cleanMobileCookies (line 15) | function cleanMobileCookies(cookieHeader: string): string {

FILE: src/shared/api/mobile-safe-actions.ts
  function getMobileCompatibleUser (line 12) | async function getMobileCompatibleUser() {

FILE: src/shared/api/safe-actions.ts
  class ActionError (line 5) | class ActionError extends Error {
    method constructor (line 6) | constructor(message: string) {
  type HandleReturnedServerError (line 11) | type HandleReturnedServerError = (e: Error) => string;

FILE: src/shared/config/localized-metadata.ts
  type SupportedLocale (line 113) | type SupportedLocale = keyof typeof LocalizedMetadata;
  function getLocalizedMetadata (line 115) | function getLocalizedMetadata(locale: string) {

FILE: src/shared/constants/errors.ts
  constant ERROR_MESSAGES (line 1) | const ERROR_MESSAGES = {

FILE: src/shared/constants/placeholders.ts
  constant PLACEHOLDERS (line 1) | const PLACEHOLDERS = {

FILE: src/shared/constants/regexs.ts
  constant PASSWORD_REGEX (line 1) | const PASSWORD_REGEX = /^(?=.*[A-Za-z])(?=.*\d).{8,}$/;

FILE: src/shared/constants/social-platforms.tsx
  constant SOCIAL_FIELD_PLACEHOLDERS (line 18) | const SOCIAL_FIELD_PLACEHOLDERS: Record<string, string> = {
  constant SOCIAL_FIELD_ICONS (line 34) | const SOCIAL_FIELD_ICONS: Record<string, React.ReactNode> = {

FILE: src/shared/constants/statistics.ts
  constant STATISTICS_TIMEFRAMES (line 1) | const STATISTICS_TIMEFRAMES = {
  type StatisticsTimeframe (line 8) | type StatisticsTimeframe = typeof STATISTICS_TIMEFRAMES[keyof typeof STA...
  constant DEFAULT_TIMEFRAME (line 10) | const DEFAULT_TIMEFRAME = STATISTICS_TIMEFRAMES.EIGHT_WEEKS;
  constant TIMEFRAME_DAYS (line 12) | const TIMEFRAME_DAYS = {
  constant TIMEFRAME_LABELS (line 19) | const TIMEFRAME_LABELS = {
  constant STATISTICS_CACHE_TTL (line 27) | const STATISTICS_CACHE_TTL = 3600;
  constant LOMBARDI_DIVISOR (line 30) | const LOMBARDI_DIVISOR = 30;

FILE: src/shared/constants/success.ts
  constant SUCCESS_MESSAGES (line 1) | const SUCCESS_MESSAGES = {

FILE: src/shared/constants/workout-set-types.ts
  constant AVAILABLE_WORKOUT_SET_TYPES (line 1) | const AVAILABLE_WORKOUT_SET_TYPES = ["TIME", "WEIGHT", "REPS", "BODYWEIG...
  constant ALL_WORKOUT_SET_TYPES (line 2) | const ALL_WORKOUT_SET_TYPES = ["TIME", "WEIGHT", "REPS", "BODYWEIGHT", "...
  constant MAX_WORKOUT_SET_COLUMNS (line 3) | const MAX_WORKOUT_SET_COLUMNS = 4;
  constant WORKOUT_SET_UNITS_TUPLE (line 4) | const WORKOUT_SET_UNITS_TUPLE = ["kg", "lbs"] as const;

FILE: src/shared/hooks/use-clipboard.ts
  type UseClipboardOptions (line 3) | interface UseClipboardOptions {
  type UseClipboardReturn (line 8) | interface UseClipboardReturn {
  function useClipboard (line 23) | function useClipboard({ timeout = 1500 }: UseClipboardOptions = {}): Use...

FILE: src/shared/hooks/use-premium-plans.ts
  type PremiumPlan (line 3) | interface PremiumPlan {
  type PlansResponse (line 14) | interface PlansResponse {
  function usePremiumPlans (line 26) | function usePremiumPlans() {

FILE: src/shared/hooks/useBoolean.ts
  type UseBooleanReturn (line 3) | interface UseBooleanReturn {
  function useBoolean (line 11) | function useBoolean(defaultValue?: boolean): UseBooleanReturn {

FILE: src/shared/hooks/useIsMobile.ts
  function useIsMobile (line 5) | function useIsMobile(breakpoint: number = 768) {

FILE: src/shared/hooks/useScrollToTop.ts
  function useScrollToTop (line 8) | function useScrollToTop() {

FILE: src/shared/lib/access-control.ts
  type AccessControlContext (line 6) | interface AccessControlContext {
  type AccessAction (line 12) | type AccessAction =
  function getSessionAccess (line 20) | function getSessionAccess(context: AccessControlContext): AccessAction {
  function canStartSession (line 50) | function canStartSession(context: AccessControlContext): boolean {

FILE: src/shared/lib/analytics/server.ts
  type Props (line 7) | type Props = {

FILE: src/shared/lib/attribute-value-translation.ts
  constant ATTRIBUTE_VALUE_TRANSLATION_KEYS (line 8) | const ATTRIBUTE_VALUE_TRANSLATION_KEYS: Record<ExerciseAttributeValueEnu...
  function getAttributeValueLabel (line 94) | function getAttributeValueLabel(value: ExerciseAttributeValueEnum, t: TF...

FILE: src/shared/lib/date.ts
  constant DEFAULT_FORMATS (line 12) | const DEFAULT_FORMATS = {
  constant SHORT_FORMATS (line 25) | const SHORT_FORMATS = {

FILE: src/shared/lib/format.ts
  function nullToUndefined (line 1) | function nullToUndefined<T>(value: T | null): T | undefined {

FILE: src/shared/lib/i18n-mapper.ts
  function getI18nField (line 8) | function getI18nField<T extends Record<string, any>>(
  function mapI18nFields (line 25) | function mapI18nFields<T extends Record<string, any>>(

FILE: src/shared/lib/locale-slug.ts
  type SlugData (line 5) | interface SlugData {
  function getSlugForLocale (line 17) | function getSlugForLocale(slugData: SlugData, locale: string): string {
  type TitleData (line 37) | interface TitleData {
  function getTitleForLocale (line 46) | function getTitleForLocale(titleData: TitleData, locale: string): string {

FILE: src/shared/lib/location/eu-countries.ts
  constant EU_COUNTRY_CODES (line 1) | const EU_COUNTRY_CODES = [

FILE: src/shared/lib/location/location.ts
  function getCountryCode (line 5) | async function getCountryCode() {
  function isEU (line 11) | async function isEU() {

FILE: src/shared/lib/mail/sendEmail.ts
  type EmailPayload (line 6) | type EmailPayload = {

FILE: src/shared/lib/mdx/load-mdx.ts
  function getLocalizedMdx (line 8) | async function getLocalizedMdx(

FILE: src/shared/lib/network/use-network-status.ts
  function useNetworkStatus (line 4) | function useNetworkStatus() {

FILE: src/shared/lib/premium/premium.manager.ts
  class PremiumManager (line 17) | class PremiumManager {
    method getAvailablePlans (line 26) | static async getAvailablePlans(provider?: string, region?: string): Pr...
    method createCheckout (line 83) | static async createCheckout(userId: string, planId: string, provider: ...
    method processWebhook (line 110) | static async processWebhook(provider: string, payload: any, signature:...
    method getPlanByInternalId (line 154) | static async getPlanByInternalId(internalId: string, provider: string)...
    method getPlanByExternalId (line 176) | static async getPlanByExternalId(externalId: string, provider: string)...
    method createBillingPortal (line 194) | static async createBillingPortal(userId: string, provider: string = "s...
    method addProvider (line 232) | static addProvider(name: string, provider: PaymentProvider): void {

FILE: src/shared/lib/premium/premium.service.ts
  class PremiumService (line 15) | class PremiumService {
    method checkUserPremiumStatus (line 19) | static async checkUserPremiumStatus(userId: string): Promise<PremiumSt...
    method getUserSubscription (line 48) | static async getUserSubscription(userId: string): Promise<UserSubscrip...
    method grantPremiumAccess (line 66) | static async grantPremiumAccess(
    method assignRevenueCatUserId (line 131) | static async assignRevenueCatUserId(userId: string, revenueCatUserId: ...
    method linkRevenueCatUser (line 207) | static async linkRevenueCatUser(authenticatedUserId: string, anonymous...
    method getUserByRevenueCatId (line 256) | static async getUserByRevenueCatId(revenueCatUserId: string) {
    method hasRevenueCatUserId (line 272) | static async hasRevenueCatUserId(userId: string): Promise<boolean> {
    method getUserRevenueCatId (line 286) | static async getUserRevenueCatId(userId: string): Promise<string | nul...
    method revokePremiumAccess (line 301) | static async revokePremiumAccess(userId: string, platform?: Platform):...
    method fetchRevenueCatSubscriptionStatus (line 345) | static async fetchRevenueCatSubscriptionStatus(revenueCatUserId: strin...
    method syncRevenueCatStatus (line 401) | static async syncRevenueCatStatus(userId: string, revenueCatUserId: st...
    method processPendingWebhookEvents (line 450) | static async processPendingWebhookEvents(userId: string, revenueCatUse...

FILE: src/shared/lib/premium/providers/base-provider.ts
  type PaymentProvider (line 9) | interface PaymentProvider {
  type CheckoutOptions (line 28) | interface CheckoutOptions {
  type WebhookResult (line 34) | interface WebhookResult {

FILE: src/shared/lib/premium/providers/stripe-provider.ts
  class StripeProvider (line 16) | class StripeProvider implements PaymentProvider {
    method constructor (line 20) | constructor() {
    method createCheckoutSession (line 33) | async createCheckoutSession(userId: string, plan: PremiumPlan, options...
    method verifyWebhookSignature (line 93) | verifyWebhookSignature(payload: string, signature: string, secret: str...
    method processWebhook (line 106) | async processWebhook(payload: string, signature: string): Promise<Webh...
    method createPortalSession (line 314) | async createPortalSession(customerId: string, returnUrl?: string): Pro...
    method getSubscription (line 339) | async getSubscription(subscriptionId: string): Promise<Stripe.Subscrip...
    method cancelSubscription (line 351) | async cancelSubscription(subscriptionId: string, immediately = false):...
    method getOrCreateCustomer (line 370) | private async getOrCreateCustomer(userId: string): Promise<Stripe.Cust...
    method getCustomerByUserId (line 411) | async getCustomerByUserId(userId: string): Promise<Stripe.Customer | n...

FILE: src/shared/lib/premium/use-pending-checkout.ts
  constant PENDING_CHECKOUT_KEY (line 5) | const PENDING_CHECKOUT_KEY = "pendingCheckout";
  type PendingCheckout (line 7) | interface PendingCheckout {
  function usePendingCheckout (line 12) | function usePendingCheckout() {

FILE: src/shared/lib/premium/use-premium-redirect.ts
  function usePremiumRedirect (line 8) | function usePremiumRedirect() {

FILE: src/shared/lib/premium/use-premium.ts
  function usePremiumStatus (line 9) | function usePremiumStatus() {
  function useSubscription (line 32) | function useSubscription() {
  function useIsPremium (line 56) | function useIsPremium(): boolean {

FILE: src/shared/lib/prisma.ts
  type PrismaClientSingleton (line 7) | type PrismaClientSingleton = ReturnType<typeof prismaClientSingleton>;

FILE: src/shared/lib/revenuecat/revenuecat.api.ts
  type RevenueCatSubscriber (line 10) | interface RevenueCatSubscriber {
  type RevenueCatApiError (line 67) | interface RevenueCatApiError {
  class RevenueCatApiClient (line 73) | class RevenueCatApiClient {
    method constructor (line 77) | constructor() {
    method getSubscriber (line 87) | async getSubscriber(appUserId: string): Promise<RevenueCatSubscriber |...
    method hasActiveEntitlements (line 135) | async hasActiveEntitlements(appUserId: string): Promise<boolean> {
    method getLatestExpirationDate (line 175) | async getLatestExpirationDate(appUserId: string): Promise<Date | null> {
    method getActiveEntitlementIds (line 215) | async getActiveEntitlementIds(appUserId: string): Promise<string[]> {

FILE: src/shared/lib/revenuecat/revenuecat.config.ts
  class RevenueCatConfig (line 9) | class RevenueCatConfig {
    method getSecretKey (line 13) | static getSecretKey(): string {
    method getWebhookSecret (line 24) | static getWebhookSecret(): string {
    method isConfigured (line 35) | static isConfigured(): boolean {
    method getApiBaseUrl (line 42) | static getApiBaseUrl(): string {
    method getApiHeaders (line 49) | static getApiHeaders(): Record<string, string> {
    method getWebhookConfig (line 60) | static getWebhookConfig() {
    method isDevelopment (line 70) | static isDevelopment(): boolean {
    method isSandbox (line 77) | static isSandbox(): boolean {

FILE: src/shared/lib/revenuecat/revenuecat.mapping.ts
  class RevenueCatMapping (line 10) | class RevenueCatMapping {
    method getSubscriptionPlanByProductId (line 14) | static async getSubscriptionPlanByProductId(productId: string) {
    method createOrUpdateProductMapping (line 32) | static async createOrUpdateProductMapping(planId: string, productId: s...
    method getAllRevenueCatMappings (line 61) | static async getAllRevenueCatMappings() {
    method getRevenueCatProductId (line 76) | static async getRevenueCatProductId(planId: string, region?: string) {
    method validateProductId (line 92) | static async validateProductId(productId: string): Promise<boolean> {
    method getSubscriptionPlanWithMapping (line 107) | static async getSubscriptionPlanWithMapping(planId: string) {

FILE: src/shared/lib/slug.ts
  function generateSlug (line 12) | function generateSlug(text: string): string {
  function generateSlugsForAllLanguages (line 44) | function generateSlugsForAllLanguages(titles: {
  function ensureUniqueSlug (line 65) | function ensureUniqueSlug(baseSlug: string, existingSlugs: string[]): st...

FILE: src/shared/lib/structured-data.ts
  type StructuredDataProps (line 9) | interface StructuredDataProps {
  function generateStructuredData (line 56) | function generateStructuredData({
  function StructuredDataScript (line 696) | function StructuredDataScript({ data }: { data: object }) {

FILE: src/shared/lib/utils.ts
  function cn (line 4) | function cn(...inputs: ClassValue[]) {

FILE: src/shared/lib/web-share.ts
  type ShareData (line 6) | interface ShareData {
  type ShareResult (line 12) | type ShareResult = { success: true; method: "native" | "clipboard" } | {...
  function isWebShareSupported (line 17) | function isWebShareSupported(): boolean {
  function isClipboardSupported (line 24) | function isClipboardSupported(): boolean {
  function shareContent (line 31) | async function shareContent(data: ShareData): Promise<ShareResult> {
  function getCurrentPageUrl (line 72) | function getCurrentPageUrl(): string {

FILE: src/shared/lib/weight-conversion.ts
  constant WEIGHT_CONVERSION (line 1) | const WEIGHT_CONVERSION = {
  type WeightUnit (line 6) | type WeightUnit = "kg" | "lbs";
  function convertWeight (line 8) | function convertWeight(weight: number, fromUnit: WeightUnit, toUnit: Wei...
  function formatWeight (line 22) | function formatWeight(weight: number, unit: WeightUnit, decimals: number...
  function convertVolumeToUnit (line 26) | function convertVolumeToUnit(

FILE: src/shared/lib/workout-session/types/workout-session.ts
  type WorkoutSessionStatus (line 6) | type WorkoutSessionStatus = (typeof workoutSessionStatuses)[number];
  type WorkoutSession (line 8) | interface WorkoutSession {

FILE: src/shared/lib/workout-session/workout-session.local.ts
  constant STORAGE_KEY (line 3) | const STORAGE_KEY = "workoutSessions";
  constant MAX_SESSIONS (line 4) | const MAX_SESSIONS = 10;
  constant CURRENT_SESSION_KEY (line 5) | const CURRENT_SESSION_KEY = "currentWorkoutSessionId";
  function getAll (line 7) | function getAll(): WorkoutSession[] {
  function saveAll (line 15) | function saveAll(sessions: WorkoutSession[]) {
  function getById (line 19) | function getById(id: string): WorkoutSession | undefined {
  function setCurrent (line 23) | function setCurrent(id: string) {
  function getCurrent (line 27) | function getCurrent(): string | null {

FILE: src/shared/lib/youtube.ts
  type YouTubeEmbedOptions (line 3) | interface YouTubeEmbedOptions {
  function getYouTubeEmbedUrl (line 20) | function getYouTubeEmbedUrl(url: string, options: YouTubeEmbedOptions = ...

FILE: src/shared/schemas/url.ts
  type Url (line 4) | type Url = z.infer<typeof urlSchema>;

FILE: src/shared/types/i18n.types.ts
  type I18nField (line 4) | type I18nField<T extends string> = {
  type I18nText (line 19) | type I18nText = I18nField<"title"> & I18nField<"description">;
  type I18nSlug (line 20) | type I18nSlug = I18nField<"slug">;
  type I18nName (line 21) | type I18nName = I18nField<"name">;
  type ExtractLocaleField (line 24) | type ExtractLocaleField<T extends Record<string, any>, Field extends key...
  function getLocaleSuffix (line 39) | function getLocaleSuffix(locale: Locale): "En" | "Es" | "Pt" | "Ru" | "Z...

FILE: src/shared/types/next.ts
  type PageParams (line 18) | type PageParams<T extends Record<string, string> = {}> = {
  type LayoutParams (line 37) | type LayoutParams<T extends Record<string, string> = {}> = {
  type ErrorParams (line 48) | type ErrorParams = {

FILE: src/shared/types/premium.types.ts
  type PremiumStatus (line 5) | interface PremiumStatus {
  type PremiumProvider (line 14) | type PremiumProvider = "stripe" | "paypal" | "lemonsqueezy" | "revenueca...
  type PlanType (line 17) | type PlanType = "free" | "supporter" | "premium";
  type PremiumPlan (line 20) | interface PremiumPlan {
  type CheckoutResult (line 36) | interface CheckoutResult {
  type UserSubscription (line 44) | interface UserSubscription {
  type FAQItem (line 52) | interface FAQItem {
  type Testimonial (line 58) | interface Testimonial {
  type FeatureComparison (line 67) | interface FeatureComparison {

FILE: src/shared/types/statistics.types.ts
  type WeightProgressionPoint (line 6) | interface WeightProgressionPoint {
  type WeightProgressionResponse (line 11) | interface WeightProgressionResponse {
  type OneRepMaxPoint (line 19) | interface OneRepMaxPoint {
  type OneRepMaxResponse (line 24) | interface OneRepMaxResponse {
  type VolumePoint (line 34) | interface VolumePoint {
  type VolumeResponse (line 41) | interface VolumeResponse {
  type ExerciseStatisticsResponse (line 50) | interface ExerciseStatisticsResponse {
  type StatisticsErrorResponse (line 61) | interface StatisticsErrorResponse {
  type StatisticsRequestParams (line 69) | interface StatisticsRequestParams {
  type ExerciseAttribute (line 75) | interface ExerciseAttribute {
  type ExercisesPaginationResponse (line 87) | interface ExercisesPaginationResponse {
  type ExercisesListRequestParams (line 99) | interface ExercisesListRequestParams {

FILE: src/shared/types/storage.ts
  type ImageUploader (line 1) | interface ImageUploader {
  type ImageDeleter (line 5) | interface ImageDeleter {

FILE: src/widgets/404.tsx
  function Page404 (line 6) | async function Page404() {

FILE: src/widgets/language-selector/language-selector.tsx
  function LanguageSelector (line 18) | function LanguageSelector() {
Condensed preview — 664 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,952K chars).
[
  {
    "path": ".cursorrules",
    "chars": 5606,
    "preview": "## 🧑‍💻 Development Guidelines\n\nThis project follows **Next.js (App Router)** and is structured using **Feature-Sliced De"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 66,
    "preview": "github: snouzy\nko_fi: workoutcool\n# buy_me_a_coffee: workout_cool\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 829,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: \"\"\nlabels: \"\"\nassignees: \"\"\n---\n\n**Describe the bu"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 200,
    "preview": "blank_issues_enabled: false\ncontact_links:\n  - name: 💬 Workout Cool Discord\n    url: https://discord.gg/NtrsUBuHUB\n    a"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 596,
    "preview": "---\nname: Feature request\nabout: Suggest a feature for this project\ntitle: \"\"\nlabels: \"\"\nassignees: \"\"\n---\n\n**Is your fe"
  },
  {
    "path": ".github/pull_request_template.md",
    "chars": 481,
    "preview": "## 📝 Description\n\n<!-- Briefly describe the changes made -->\n\n## 📋 Checklist\n\n- [ ] My code follows the project conventi"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 5977,
    "preview": "name: ci\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\njobs:\n  lint:\n    runs-on: ubuntu-lates"
  },
  {
    "path": ".github/workflows/notify-discord-issues.yml",
    "chars": 6661,
    "preview": "name: Discord Issue Notification\n\non:\n  issues:\n    types: [opened, reopened, closed]\n  workflow_dispatch:\n    inputs:\n "
  },
  {
    "path": ".github/workflows/notify-discord-pr.yml",
    "chars": 9913,
    "preview": "name: Discord PR Notification\n\non:\n  pull_request:\n    types: [opened]\n  workflow_dispatch:\n    inputs:\n      pr_number:"
  },
  {
    "path": ".github/workflows/notify-discord.yml",
    "chars": 5838,
    "preview": "name: Discord Notification\n\non:\n  release:\n    types: [published]\n  workflow_dispatch:\n    inputs:\n      tag_name:\n     "
  },
  {
    "path": ".github/workflows/publish-ghcr-image.yml",
    "chars": 1498,
    "preview": "name: Publish Docker GHCR Image\n\non:\n  release:\n    types: [published]\n  workflow_dispatch:\n    inputs:\n      version:\n "
  },
  {
    "path": ".gitignore",
    "chars": 636,
    "preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
  },
  {
    "path": ".npmrc",
    "chars": 92,
    "preview": "public-hoist-pattern[]=*import-in-the-middle*\npublic-hoist-pattern[]=*require-in-the-middle*"
  },
  {
    "path": ".prettierrc",
    "chars": 117,
    "preview": "{\n  \"plugins\": [\"prettier-plugin-sort-json\"],\n  \"printWidth\": 140,\n  \"proseWrap\": \"always\",\n  \"singleQuote\": false\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 131,
    "preview": "{\n  \"editor.codeActionsOnSave\": {\n    \"source.fixAll.eslint\": \"explicit\"\n  },\n  \"typescript.tsdk\": \"node_modules/typescr"
  },
  {
    "path": "AGENTS.md",
    "chars": 1813,
    "preview": "# AGENTS.md - Development Guide for AI Coding Agents\n\n## Build/Test Commands\n\n- `pnpm dev` - Start development server wi"
  },
  {
    "path": "CLAUDE.md",
    "chars": 2114,
    "preview": "# CLAUDE.md\n\n## Project Overview\n\nWorkout.cool is a fitness app with two main components:\n\n- **Website** Next.js (App Ro"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 1024,
    "preview": "### ✅ Review & Contribution Flow\n\n- Before starting, **create an issue** for the task you want to work on.\n- **Assign yo"
  },
  {
    "path": "Dockerfile",
    "chars": 872,
    "preview": "FROM node:20-alpine AS base\n\nWORKDIR /app\nRUN npm install -g pnpm\n\n# Install dependencies\nFROM base AS deps\nCOPY package"
  },
  {
    "path": "LICENSE",
    "chars": 1075,
    "preview": "MIT License\n\nCopyright (c) 2023 Mathias Bradiceanu\n\nPermission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "Makefile",
    "chars": 901,
    "preview": ".PHONY: dev setup up down help\n\nhelp:\n\t@echo \"🚀 Workout Cool Development Commands\"\n\t@echo \"\"\n\t@echo \"  dev     - Start d"
  },
  {
    "path": "README.md",
    "chars": 14187,
    "preview": "<div align=\"center\">\n<img src=\"public/logo.png\" alt=\"Workout.cool Logo\" width=\"120\" height=\"120\">\n<h1>Workout.cool</h1>\n"
  },
  {
    "path": "app/[locale]/(admin)/admin/[...catchAll]/not-found.tsx",
    "chars": 107,
    "preview": "import { Page404 } from \"@/widgets/404\";\n\nexport default function NotFoundPage() {\n  return <Page404 />;\n}\n"
  },
  {
    "path": "app/[locale]/(admin)/admin/[...catchAll]/page.tsx",
    "chars": 108,
    "preview": "import { Page404 } from \"@/widgets/404\";\n\nexport default function AdminCatchAll() {\n  return <Page404 />;\n}\n"
  },
  {
    "path": "app/[locale]/(admin)/admin/dashboard/page.tsx",
    "chars": 9688,
    "preview": "import { Suspense } from \"react\";\nimport Image from \"next/image\";\nimport { Users, Target } from \"lucide-react\";\n\nimport "
  },
  {
    "path": "app/[locale]/(admin)/admin/layout.tsx",
    "chars": 1147,
    "preview": "import { ReactElement } from \"react\";\nimport { redirect } from \"next/navigation\";\nimport { UserRole } from \"@prisma/clie"
  },
  {
    "path": "app/[locale]/(admin)/admin/not-found.tsx",
    "chars": 107,
    "preview": "import { Page404 } from \"@/widgets/404\";\n\nexport default function NotFoundPage() {\n  return <Page404 />;\n}\n"
  },
  {
    "path": "app/[locale]/(admin)/admin/programs/[id]/edit/page.tsx",
    "chars": 531,
    "preview": "import { notFound } from \"next/navigation\";\n\nimport { ProgramBuilder } from \"@/features/admin/programs/ui/program-builde"
  },
  {
    "path": "app/[locale]/(admin)/admin/programs/page.tsx",
    "chars": 704,
    "preview": "import { Suspense } from \"react\";\n\nimport { ProgramsList } from \"@/features/admin/programs/ui/programs-list\";\nimport { C"
  },
  {
    "path": "app/[locale]/(admin)/admin/settings/page.tsx",
    "chars": 297,
    "preview": "export default function AdminSettings() {\n  return (\n    <div className=\"space-y-6\">\n      <div>\n        <h1 className=\""
  },
  {
    "path": "app/[locale]/(admin)/admin/users/page.tsx",
    "chars": 1992,
    "preview": "import { redirect } from \"next/navigation\";\nimport { UserRole } from \"@prisma/client\";\n\nimport { UsersTable } from \"@/fe"
  },
  {
    "path": "app/[locale]/(app)/(legal-and-payment)/layout.tsx",
    "chars": 577,
    "preview": "import { LayoutParams } from \"@/shared/types/next\";\n\ntype LocaleParams = Record<string, string> & {\n  locale: string;\n};"
  },
  {
    "path": "app/[locale]/(app)/(legal-and-payment)/legal/privacy/page.tsx",
    "chars": 1086,
    "preview": "import { getLocalizedMdx } from \"@/shared/lib/mdx/load-mdx\";\nimport { Typography } from \"@/components/ui/typography\";\n\nt"
  },
  {
    "path": "app/[locale]/(app)/(legal-and-payment)/legal/sales-terms/page.tsx",
    "chars": 1237,
    "preview": "import { getLocalizedMdx } from \"@/shared/lib/mdx/load-mdx\";\nimport { Layout, LayoutContent } from \"@/features/page/layo"
  },
  {
    "path": "app/[locale]/(app)/(legal-and-payment)/legal/terms/page.tsx",
    "chars": 1225,
    "preview": "import { getLocalizedMdx } from \"@/shared/lib/mdx/load-mdx\";\nimport { Layout, LayoutContent } from \"@/features/page/layo"
  },
  {
    "path": "app/[locale]/(app)/[slug]/layout.tsx",
    "chars": 288,
    "preview": "import { ReactElement } from \"react\";\n\ninterface RootLayoutProps {\n    params: Promise<{ locale: string }>;\n    children"
  },
  {
    "path": "app/[locale]/(app)/about/page.tsx",
    "chars": 525,
    "preview": "import { getLocalizedMdx } from \"@/shared/lib/mdx/load-mdx\";\n\ntype PageProps = {\n  params: Promise<{ locale: string }>;\n"
  },
  {
    "path": "app/[locale]/(app)/auth/(auth-layout)/forgot-password/page.tsx",
    "chars": 183,
    "preview": "import { ForgotPasswordForm } from \"@/features/auth/forgot-password/ui/forgot-password-form\";\n\nexport default async func"
  },
  {
    "path": "app/[locale]/(app)/auth/(auth-layout)/layout.tsx",
    "chars": 1296,
    "preview": "import { redirect } from \"next/navigation\";\nimport { headers } from \"next/headers\";\n\nimport { getI18n } from \"locales/se"
  },
  {
    "path": "app/[locale]/(app)/auth/(auth-layout)/reset-password/page.tsx",
    "chars": 172,
    "preview": "import { ResetPasswordForm } from \"@/features/auth/reset-password/ui/reset-password-form\";\n\nexport default function Rese"
  },
  {
    "path": "app/[locale]/(app)/auth/(auth-layout)/signin/page.tsx",
    "chars": 174,
    "preview": "import { CredentialsLoginForm } from \"@/features/auth/signin/ui/CredentialsLoginForm\";\n\nexport default async function Au"
  },
  {
    "path": "app/[locale]/(app)/auth/(auth-layout)/signup/page.tsx",
    "chars": 1039,
    "preview": "import Link from \"next/link\";\n\nimport { getI18n } from \"locales/server\";\nimport { paths } from \"@/shared/constants/paths"
  },
  {
    "path": "app/[locale]/(app)/auth/error/page.tsx",
    "chars": 743,
    "preview": "import Link from \"next/link\";\n\nimport { Card, CardDescription, CardFooter, CardHeader, CardTitle } from \"@/components/ui"
  },
  {
    "path": "app/[locale]/(app)/auth/error.tsx",
    "chars": 718,
    "preview": "\"use client\";\n\nimport { useEffect } from \"react\";\n\nimport { logger } from \"@/shared/lib/logger\";\nimport { Card, CardFoot"
  },
  {
    "path": "app/[locale]/(app)/auth/layout.tsx",
    "chars": 110,
    "preview": "export default function AuthLayout({ children }: { children: React.ReactNode }) {\n  return <>{children}</>;\n}\n"
  },
  {
    "path": "app/[locale]/(app)/auth/signout/page.tsx",
    "chars": 83,
    "preview": "export default function AuthSignOutPage() {\n  return <div>AuthSignOutPage</div>;\n}\n"
  },
  {
    "path": "app/[locale]/(app)/auth/verify-email/layout.tsx",
    "chars": 591,
    "preview": "import { ReactElement } from \"react\";\nimport { redirect } from \"next/navigation\";\n\nimport { getServerUrl } from \"@/share"
  },
  {
    "path": "app/[locale]/(app)/auth/verify-email/page.tsx",
    "chars": 181,
    "preview": "\"use client\";\n\nimport { VerifyEmailPage } from \"@/features/auth/verify-email/ui/verify-email-page\";\n\nexport default func"
  },
  {
    "path": "app/[locale]/(app)/auth/verify-request/page.tsx",
    "chars": 1256,
    "preview": "import Image from \"next/image\";\n\nimport { SiteConfig } from \"@/shared/config/site-config\";\nimport { Typography } from \"@"
  },
  {
    "path": "app/[locale]/(app)/layout.tsx",
    "chars": 792,
    "preview": "import { ReactElement } from \"react\";\n\nimport { Header } from \"@/features/layout/Header\";\nimport { Footer } from \"@/feat"
  },
  {
    "path": "app/[locale]/(app)/leaderboard/page.tsx",
    "chars": 964,
    "preview": "import { Metadata } from \"next\";\n\nimport { Locale } from \"locales/types\";\nimport { getI18n } from \"locales/server\";\nimpo"
  },
  {
    "path": "app/[locale]/(app)/onboarding/layout.tsx",
    "chars": 187,
    "preview": "import { LayoutParams } from \"@/shared/types/next\";\n\nexport default async function OnboardingLayout(props: LayoutParams<"
  },
  {
    "path": "app/[locale]/(app)/onboarding/page.tsx",
    "chars": 194,
    "preview": "export default async function OnboardingPage() {\n  return (\n    <main className=\"bg-muted flex min-h-screen flex-col ite"
  },
  {
    "path": "app/[locale]/(app)/page.tsx",
    "chars": 1951,
    "preview": "import React from \"react\";\n\nimport { getServerUrl } from \"@/shared/lib/server-url\";\nimport { SiteConfig } from \"@/shared"
  },
  {
    "path": "app/[locale]/(app)/premium/page.tsx",
    "chars": 1205,
    "preview": "import { Metadata } from \"next\";\n\nimport { PremiumUpgradeCard } from \"@/features/premium/ui/premium-upgrade-card\";\n\nexpo"
  },
  {
    "path": "app/[locale]/(app)/profile/page.tsx",
    "chars": 1969,
    "preview": "\"use client\";\nimport { useRouter } from \"next/navigation\";\n\nimport { useI18n } from \"locales/client\";\nimport { WorkoutSe"
  },
  {
    "path": "app/[locale]/(app)/programs/[slug]/page.tsx",
    "chars": 3581,
    "preview": "import { notFound } from \"next/navigation\";\nimport { headers } from \"next/headers\";\nimport { Metadata } from \"next\";\n\nim"
  },
  {
    "path": "app/[locale]/(app)/programs/[slug]/session/[sessionSlug]/ProgramSessionClient.tsx",
    "chars": 11888,
    "preview": "\"use client\";\n\nimport { useState } from \"react\";\nimport { useRouter } from \"next/navigation\";\nimport { ArrowLeft, Play }"
  },
  {
    "path": "app/[locale]/(app)/programs/[slug]/session/[sessionSlug]/page.tsx",
    "chars": 5504,
    "preview": "import { notFound } from \"next/navigation\";\nimport { headers } from \"next/headers\";\nimport { Metadata } from \"next\";\n\nim"
  },
  {
    "path": "app/[locale]/(app)/programs/page.tsx",
    "chars": 933,
    "preview": "import { Metadata } from \"next\";\n\nimport { Locale } from \"locales/types\";\nimport { getI18n } from \"locales/server\";\nimpo"
  },
  {
    "path": "app/[locale]/(app)/statistics/page.tsx",
    "chars": 1873,
    "preview": "import React from \"react\";\n\nimport { getI18n } from \"locales/server\";\nimport { ExercisesBrowser } from \"@/features/stati"
  },
  {
    "path": "app/[locale]/(app)/tools/bmi-calculator/bmi-calculator.utils.ts",
    "chars": 11370,
    "preview": "import { TFunction } from \"locales/client\";\n\nexport type UnitSystem = \"metric\" | \"imperial\";\n\nexport interface BmiData {"
  },
  {
    "path": "app/[locale]/(app)/tools/bmi-calculator/page.tsx",
    "chars": 4515,
    "preview": "import React from \"react\";\nimport { Metadata } from \"next\";\n\nimport { getI18n } from \"locales/server\";\nimport { BmiEduca"
  },
  {
    "path": "app/[locale]/(app)/tools/bmi-calculator/shared/BmiCalculatorClient.tsx",
    "chars": 2770,
    "preview": "\"use client\";\n\nimport React, { useState, useEffect } from \"react\";\n\nimport { useI18n } from \"locales/client\";\nimport {\n "
  },
  {
    "path": "app/[locale]/(app)/tools/bmi-calculator/shared/components/BmiEducationalContent.tsx",
    "chars": 19219,
    "preview": "\"use client\";\n\nimport { useI18n } from \"locales/client\";\nimport { env } from \"@/env\";\nimport { InArticle } from \"@/compo"
  },
  {
    "path": "app/[locale]/(app)/tools/bmi-calculator/shared/components/BmiHeightInput.tsx",
    "chars": 3524,
    "preview": "\"use client\";\n\nimport React from \"react\";\n\nimport { useI18n } from \"locales/client\";\nimport { UnitSystem } from \"app/[lo"
  },
  {
    "path": "app/[locale]/(app)/tools/bmi-calculator/shared/components/BmiResultsDisplay.tsx",
    "chars": 13079,
    "preview": "\"use client\";\n\nimport React from \"react\";\nimport { CheckCircleIcon, AlertTriangleIcon, XCircleIcon, InfoIcon, TrendingUp"
  },
  {
    "path": "app/[locale]/(app)/tools/bmi-calculator/shared/components/BmiUnitSelector.tsx",
    "chars": 2106,
    "preview": "\"use client\";\n\nimport React from \"react\";\n\nimport { useI18n } from \"locales/client\";\nimport { UnitSystem } from \"app/[lo"
  },
  {
    "path": "app/[locale]/(app)/tools/bmi-calculator/shared/components/BmiWeightInput.tsx",
    "chars": 1542,
    "preview": "\"use client\";\n\nimport React from \"react\";\n\nimport { useI18n } from \"locales/client\";\nimport { UnitSystem } from \"app/[lo"
  },
  {
    "path": "app/[locale]/(app)/tools/bmi-calculator/shared/components/MathEquation.tsx",
    "chars": 2416,
    "preview": "\"use client\";\n\ninterface MathEquationProps {\n  equation: string;\n  display?: boolean;\n  className?: string;\n}\n\nexport fu"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/CalorieCalculatorHub.tsx",
    "chars": 8614,
    "preview": "\"use client\";\n\nimport React from \"react\";\nimport Link from \"next/link\";\nimport { TrendingUpIcon, AwardIcon, TargetIcon, "
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/calorie-calculator-comparison/CalorieCalculatorComparison.tsx",
    "chars": 10994,
    "preview": "\"use client\";\n\nimport React, { useState } from \"react\";\n\nimport { useI18n } from \"locales/client\";\nimport { BodyFatInput"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/calorie-calculator-comparison/page.tsx",
    "chars": 5315,
    "preview": "import React from \"react\";\nimport Link from \"next/link\";\nimport { Metadata } from \"next\";\nimport { ChevronLeftIcon } fro"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/calorie-calculator.utils.ts",
    "chars": 3504,
    "preview": "// Types for the calorie calculator\nexport type Gender = \"male\" | \"female\";\nexport type UnitSystem = \"metric\" | \"imperia"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/cunningham-calculator/page.tsx",
    "chars": 5753,
    "preview": "import React from \"react\";\nimport Link from \"next/link\";\nimport { Metadata } from \"next\";\nimport { ChevronLeftIcon } fro"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/harris-benedict-calculator/page.tsx",
    "chars": 5669,
    "preview": "import React from \"react\";\nimport Link from \"next/link\";\nimport { Metadata } from \"next\";\nimport { ChevronLeftIcon } fro"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/katch-mcardle-calculator/page.tsx",
    "chars": 5780,
    "preview": "import React from \"react\";\nimport Link from \"next/link\";\nimport { Metadata } from \"next\";\nimport { ChevronLeftIcon } fro"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/mifflin-st-jeor-calculator/page.tsx",
    "chars": 5656,
    "preview": "import React from \"react\";\nimport Link from \"next/link\";\nimport { Metadata } from \"next\";\nimport { ChevronLeftIcon } fro"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/oxford-calculator/page.tsx",
    "chars": 5587,
    "preview": "import React from \"react\";\nimport Link from \"next/link\";\nimport { Metadata } from \"next\";\nimport { ChevronLeftIcon } fro"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/page.tsx",
    "chars": 3023,
    "preview": "import React from \"react\";\nimport { Metadata } from \"next\";\n\nimport { getI18n } from \"locales/server\";\nimport { getServe"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/shared/CalorieCalculatorClient.tsx",
    "chars": 4716,
    "preview": "\"use client\";\n\nimport React, { useState } from \"react\";\nimport { Calculator } from \"lucide-react\";\n\nimport { useI18n } f"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/shared/calculator-configs.ts",
    "chars": 1458,
    "preview": "import type { CalculatorConfig } from \"./types\";\n\n// Calculator configurations\nexport const calculatorConfigs: Record<st"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/shared/calorie-formulas.utils.ts",
    "chars": 4841,
    "preview": "// Shared types and formulas for all calorie calculators\n\nexport type Gender = \"male\" | \"female\";\nexport type UnitSystem"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/shared/components/ActivityLevelSelector.tsx",
    "chars": 4025,
    "preview": "\"use client\";\n\nimport React from \"react\";\nimport { ActivityIcon, BedIcon, BedDoubleIcon, BikeIcon, ZapIcon } from \"lucid"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/shared/components/AgeInput.tsx",
    "chars": 2214,
    "preview": "\"use client\";\n\nimport React from \"react\";\n\nimport { useI18n } from \"locales/client\";\n\ninterface AgeInputProps {\n  value:"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/shared/components/BodyFatInput.tsx",
    "chars": 2209,
    "preview": "\"use client\";\n\nimport React from \"react\";\n\nimport { useI18n } from \"locales/client\";\n\ninterface BodyFatInputProps {\n  va"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/shared/components/FAQSection.tsx",
    "chars": 2525,
    "preview": "\"use client\";\n\nimport React, { useState } from \"react\";\nimport { ChevronDownIcon, HelpCircleIcon } from \"lucide-react\";\n"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/shared/components/GenderSelector.tsx",
    "chars": 3546,
    "preview": "\"use client\";\n\nimport React from \"react\";\nimport { UserIcon, UsersIcon } from \"lucide-react\";\n\nimport { useI18n } from \""
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/shared/components/GoalSelector.tsx",
    "chars": 3853,
    "preview": "\"use client\";\n\nimport React from \"react\";\nimport { TrendingDownIcon, TrendingUpIcon, ScaleIcon, RocketIcon, ZapOffIcon }"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/shared/components/HeightInput.tsx",
    "chars": 3574,
    "preview": "\"use client\";\n\nimport React from \"react\";\n\nimport { useI18n } from \"locales/client\";\nimport { UnitSystem } from \"app/[lo"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/shared/components/InfoButton.tsx",
    "chars": 1071,
    "preview": "\"use client\";\n\nimport React from \"react\";\nimport { InfoIcon } from \"lucide-react\";\n\nimport { useIsMobile } from \"@/share"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/shared/components/InfoModal.tsx",
    "chars": 1822,
    "preview": "\"use client\";\n\nimport React, { useEffect } from \"react\";\nimport { XIcon } from \"lucide-react\";\n\ninterface InfoModalProps"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/shared/components/ResultsDisplay.tsx",
    "chars": 9884,
    "preview": "\"use client\";\n\nimport React, { useState } from \"react\";\nimport Image from \"next/image\";\nimport { BeefIcon, WheatIcon, Dr"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/shared/components/UnitSelector.tsx",
    "chars": 3443,
    "preview": "\"use client\";\n\nimport React from \"react\";\nimport { RulerIcon, GlobeIcon } from \"lucide-react\";\n\nimport { useI18n } from "
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/shared/components/WeightInput.tsx",
    "chars": 1578,
    "preview": "\"use client\";\n\nimport React from \"react\";\n\nimport { useI18n } from \"locales/client\";\nimport { UnitSystem } from \"app/[lo"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/shared/components/index.ts",
    "chars": 549,
    "preview": "// Export all shared components\nexport { GenderSelector } from \"./GenderSelector\";\nexport { WeightInput } from \"./Weight"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/shared/types/index.ts",
    "chars": 336,
    "preview": "// Calorie calculator types for better type safety\nexport type CalculatorFormula = \"mifflin\" | \"harris\" | \"katch\" | \"cun"
  },
  {
    "path": "app/[locale]/(app)/tools/calorie-calculator/styles.css",
    "chars": 2295,
    "preview": "@keyframes fadeIn {\n  from {\n    opacity: 0;\n    transform: translateY(10px);\n  }\n  to {\n    opacity: 1;\n    transform: "
  },
  {
    "path": "app/[locale]/(app)/tools/heart-rate-zones/lib/utils.ts",
    "chars": 2123,
    "preview": "import { TFunction } from \"locales/client\";\n\ninterface HeartRateZone {\n  name: string;\n  minHR: number;\n  maxHR: number;"
  },
  {
    "path": "app/[locale]/(app)/tools/heart-rate-zones/page.tsx",
    "chars": 8615,
    "preview": "import React from \"react\";\nimport { Metadata } from \"next\";\n\nimport { Locale } from \"locales/types\";\nimport { getI18n } "
  },
  {
    "path": "app/[locale]/(app)/tools/heart-rate-zones/seo/config.ts",
    "chars": 4147,
    "preview": "import { Locale } from \"locales/types\";\n\n// All SEO metadata in one place for easy maintenance\nexport const HEART_RATE_Z"
  },
  {
    "path": "app/[locale]/(app)/tools/heart-rate-zones/seo/page-content.ts",
    "chars": 1140,
    "preview": "import { Locale } from \"locales/types\";\n\ninterface PageContent {\n  heroSubtitle: string;\n}\n\n// Page content separate fro"
  },
  {
    "path": "app/[locale]/(app)/tools/heart-rate-zones/ui/HeartRateZonesCalculatorClient.tsx",
    "chars": 10606,
    "preview": "\"use client\";\n\nimport { z } from \"zod\";\nimport { useForm } from \"react-hook-form\";\nimport React, { useState, useEffect }"
  },
  {
    "path": "app/[locale]/(app)/tools/heart-rate-zones/ui/components/EducationalContent.tsx",
    "chars": 10470,
    "preview": "\"use client\";\n\nimport React from \"react\";\n\nimport { TFunction, useI18n } from \"locales/client\";\nimport { useScrollToTop "
  },
  {
    "path": "app/[locale]/(app)/tools/heart-rate-zones/ui/components/EducationalContentServer.tsx",
    "chars": 10149,
    "preview": "import React from \"react\";\n\nimport { getI18n } from \"locales/server\";\nimport { ScrollToTopButton } from \"app/[locale]/(a"
  },
  {
    "path": "app/[locale]/(app)/tools/heart-rate-zones/ui/components/FAQAccordion.tsx",
    "chars": 1564,
    "preview": "\"use client\";\n\nimport React, { useState } from \"react\";\n\ninterface FAQItem {\n  question: string;\n  answer: string;\n}\n\nin"
  },
  {
    "path": "app/[locale]/(app)/tools/heart-rate-zones/ui/components/SEOOptimizedContentServer.tsx",
    "chars": 18870,
    "preview": "import React from \"react\";\nimport Link from \"next/link\";\n\nimport { getI18n } from \"locales/server\";\nimport { ScrollToTop"
  },
  {
    "path": "app/[locale]/(app)/tools/heart-rate-zones/ui/components/ScrollToTopButton.tsx",
    "chars": 587,
    "preview": "\"use client\";\n\nimport React from \"react\";\n\nimport { useScrollToTop } from \"@/shared/hooks/useScrollToTop\";\n\ninterface Sc"
  },
  {
    "path": "app/[locale]/(app)/tools/heart-rate-zones/ui/styles.css",
    "chars": 2196,
    "preview": "/* Simple animations for heart rate calculator */\n\n@keyframes heartbeat {\n  0% { transform: scale(1); }\n  50% { transfor"
  },
  {
    "path": "app/[locale]/(app)/tools/page.tsx",
    "chars": 9828,
    "preview": "import React from \"react\";\nimport Link from \"next/link\";\nimport Image from \"next/image\";\nimport { Metadata } from \"next\""
  },
  {
    "path": "app/[locale]/@modal/(.)auth/login/page.tsx",
    "chars": 350,
    "preview": "import { CredentialsLoginForm } from \"@/features/auth/signin/ui/CredentialsLoginForm\";\nimport { Dialog, DialogContent } "
  },
  {
    "path": "app/[locale]/layout.tsx",
    "chars": 12863,
    "preview": "import { Inter, Permanent_Marker } from \"next/font/google\";\nimport { GeistSans } from \"geist/font/sans\";\nimport { GeistM"
  },
  {
    "path": "app/[locale]/manifest.json/route.ts",
    "chars": 1518,
    "preview": "import { NextRequest } from \"next/server\";\n\nimport { getLocalizedMetadata } from \"@/shared/config/localized-metadata\";\n\n"
  },
  {
    "path": "app/[locale]/not-found.tsx",
    "chars": 107,
    "preview": "import { Page404 } from \"@/widgets/404\";\n\nexport default function NotFoundPage() {\n  return <Page404 />;\n}\n"
  },
  {
    "path": "app/[locale]/providers.tsx",
    "chars": 1508,
    "preview": "\"use client\";\n\nimport { NuqsAdapter } from \"nuqs/adapters/next/app\";\nimport { ReactQueryDevtools } from \"@tanstack/react"
  },
  {
    "path": "app/ads.txt/route.ts",
    "chars": 1357,
    "preview": "import { NextResponse } from \"next/server\";\n\nexport async function GET() {\n  // Redirect to Ezoic's ads.txt manager\n  //"
  },
  {
    "path": "app/api/analytics/premium/route.ts",
    "chars": 2459,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { LogEvents } from \"@/shared/lib/analytics/events\";\n\n/*"
  },
  {
    "path": "app/api/auth/[...all]/route.ts",
    "chars": 165,
    "preview": "import { toNextJsHandler } from \"better-auth/next-js\";\n\nimport { auth } from \"@/features/auth/lib/better-auth\";\n\nexport "
  },
  {
    "path": "app/api/auth/signup/route.ts",
    "chars": 1598,
    "preview": "import { z } from \"zod\";\nimport { NextRequest, NextResponse } from \"next/server\";\nimport { UserRole } from \"@prisma/clie"
  },
  {
    "path": "app/api/billing/status/route.ts",
    "chars": 1626,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { prisma } from \"@/shared/lib/prisma\";\nimport { Premium"
  },
  {
    "path": "app/api/exercises/[exerciseId]/statistics/one-rep-max/route.ts",
    "chars": 6209,
    "preview": "import { z } from \"zod\";\nimport { NextRequest, NextResponse } from \"next/server\";\n\nimport { OneRepMaxResponse, Statistic"
  },
  {
    "path": "app/api/exercises/[exerciseId]/statistics/route.ts",
    "chars": 2905,
    "preview": "import { z } from \"zod\";\nimport { NextRequest, NextResponse } from \"next/server\";\n\nimport { ExerciseStatisticsResponse, "
  },
  {
    "path": "app/api/exercises/[exerciseId]/statistics/volume/route.ts",
    "chars": 7031,
    "preview": "import { z } from \"zod\";\nimport { NextRequest, NextResponse } from \"next/server\";\n\nimport { VolumeResponse, StatisticsEr"
  },
  {
    "path": "app/api/exercises/[exerciseId]/statistics/weight-progression/route.ts",
    "chars": 5669,
    "preview": "import { z } from \"zod\";\nimport { NextRequest, NextResponse } from \"next/server\";\n\nimport { WeightProgressionResponse, S"
  },
  {
    "path": "app/api/exercises/all/route.ts",
    "chars": 4216,
    "preview": "import { z } from \"zod\";\nimport { NextRequest, NextResponse } from \"next/server\";\n\nimport { prisma } from \"@/shared/lib/"
  },
  {
    "path": "app/api/exercises/route.ts",
    "chars": 1312,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { getExercisesSchema } from \"@/features/workout-builder"
  },
  {
    "path": "app/api/exercises/shuffle/route.ts",
    "chars": 1464,
    "preview": "import { z } from \"zod\";\nimport { NextRequest, NextResponse } from \"next/server\";\nimport { ExerciseAttributeValueEnum } "
  },
  {
    "path": "app/api/premium/billing-portal/route.ts",
    "chars": 1096,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { PremiumManager } from \"@/shared/lib/premium/premium.m"
  },
  {
    "path": "app/api/premium/checkout/route.ts",
    "chars": 1035,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { PremiumManager } from \"@/shared/lib/premium/premium.m"
  },
  {
    "path": "app/api/premium/plans/route.ts",
    "chars": 7646,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { PremiumManager } from \"@/shared/lib/premium/premium.m"
  },
  {
    "path": "app/api/premium/status/route.ts",
    "chars": 2843,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { prisma } from \"@/shared/lib/prisma\";\nimport { getMobi"
  },
  {
    "path": "app/api/programs/[slug]/enroll/route.ts",
    "chars": 2927,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\nimport { headers } from \"next/headers\";\n\nimport { prisma } from"
  },
  {
    "path": "app/api/programs/[slug]/progress/route.ts",
    "chars": 695,
    "preview": "import { NextResponse } from \"next/server\";\n\nimport { getProgramProgressBySlug } from \"@/features/programs/actions/get-p"
  },
  {
    "path": "app/api/programs/[slug]/route.ts",
    "chars": 639,
    "preview": "import { NextResponse } from \"next/server\";\n\nimport { getProgramBySlug } from \"@/features/programs/actions/get-program-b"
  },
  {
    "path": "app/api/programs/[slug]/sessions/[sessionSlug]/route.ts",
    "chars": 826,
    "preview": "import { NextResponse } from \"next/server\";\n\nimport { getSessionBySlug } from \"@/features/programs/actions/get-session-b"
  },
  {
    "path": "app/api/programs/route.ts",
    "chars": 430,
    "preview": "import { NextResponse } from \"next/server\";\n\nimport { getPublicPrograms } from \"@/features/programs/actions/get-public-p"
  },
  {
    "path": "app/api/programs/session-progress/[progressId]/complete/route.ts",
    "chars": 1271,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { completeProgramSession } from \"@/features/programs/ac"
  },
  {
    "path": "app/api/programs/session-progress/start/route.ts",
    "chars": 1316,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { startProgramSession } from \"@/features/programs/actio"
  },
  {
    "path": "app/api/revenuecat/link-user/route.ts",
    "chars": 1086,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\n/**\n * Get Premium Status\n *\n * GET /api/revenuecat/link-user\n"
  },
  {
    "path": "app/api/revenuecat/sync-status/route.ts",
    "chars": 4855,
    "preview": "import { z } from \"zod\";\nimport { NextRequest, NextResponse } from \"next/server\";\nimport { Platform } from \"@prisma/clie"
  },
  {
    "path": "app/api/revenuecat/webhook/route.ts",
    "chars": 7232,
    "preview": "import crypto from \"crypto\";\n\nimport { z } from \"zod\";\nimport { NextRequest, NextResponse } from \"next/server\";\nimport {"
  },
  {
    "path": "app/api/user/password/route.ts",
    "chars": 1538,
    "preview": "import { z } from \"zod\";\nimport { NextRequest, NextResponse } from \"next/server\";\n\nimport { ActionError } from \"@/shared"
  },
  {
    "path": "app/api/user/profile/route.ts",
    "chars": 2482,
    "preview": "import { z } from \"zod\";\nimport { NextRequest, NextResponse } from \"next/server\";\n\nimport { prisma } from \"@/shared/lib/"
  },
  {
    "path": "app/api/webhooks/revenuecat/route.ts",
    "chars": 10545,
    "preview": "/* eslint-disable @typescript-eslint/naming-convention */\n\nimport { NextRequest, NextResponse } from \"next/server\";\n\nimp"
  },
  {
    "path": "app/api/webhooks/stripe/route.ts",
    "chars": 1036,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { PremiumManager } from \"@/shared/lib/premium/premium.m"
  },
  {
    "path": "app/api/workout-sessions/[sessionId]/feedback/route.ts",
    "chars": 2067,
    "preview": "import { z } from \"zod\";\nimport { NextRequest, NextResponse } from \"next/server\";\n\nimport { prisma } from \"@/shared/lib/"
  },
  {
    "path": "app/api/workout-sessions/[sessionId]/rating/route.ts",
    "chars": 1983,
    "preview": "import { z } from \"zod\";\nimport { NextRequest, NextResponse } from \"next/server\";\n\nimport { prisma } from \"@/shared/lib/"
  },
  {
    "path": "app/api/workout-sessions/[sessionId]/route.ts",
    "chars": 1506,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { getMobileCompatibleSession } from \"@/shared/api/mobil"
  },
  {
    "path": "app/api/workout-sessions/[sessionId]/summary/route.ts",
    "chars": 3822,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { prisma } from \"@/shared/lib/prisma\";\nimport { getMobi"
  },
  {
    "path": "app/api/workout-sessions/sync/route.ts",
    "chars": 1363,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { getMobileCompatibleSession } from \"@/shared/api/mobil"
  },
  {
    "path": "app/api/workout-sessions/user/[userId]/route.ts",
    "chars": 3034,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { getMobileCompatibleSession } from \"@/shared/api/mobil"
  },
  {
    "path": "app/robots.txt",
    "chars": 560,
    "preview": "User-agent: *\nAllow: /\nDisallow: /admin/\nDisallow: /api/\nDisallow: /dashboard/\nDisallow: /preview/\nDisallow: /auth/\nDisa"
  },
  {
    "path": "app/sitemap.ts",
    "chars": 6732,
    "preview": "import { MetadataRoute } from \"next/types\";\n\nimport { getSitemapData } from \"@/features/programs/actions/get-sitemap-dat"
  },
  {
    "path": "components.json",
    "chars": 463,
    "preview": "{\n  \"$schema\": \"https://ui.shadcn.com/schema.json\",\n  \"aliases\": {\n    \"components\": \"@/components\",\n    \"utils\": \"@/sha"
  },
  {
    "path": "content/about/en.mdx",
    "chars": 2022,
    "preview": "import Link from \"next/link\";\n\n# About Workout.cool\n\n## Why Workout.cool?\n\nWorkout.cool was born out of the desire to of"
  },
  {
    "path": "content/about/es.mdx",
    "chars": 2199,
    "preview": "import Link from \"next/link\";\n\n# Acerca de Workout.cool\n\n## ¿Por qué Workout.cool?\n\nWorkout.cool nació del deseo de ofre"
  },
  {
    "path": "content/about/fr.mdx",
    "chars": 2202,
    "preview": "import Link from \"next/link\";\n\n# À propos de Workout.cool\n\n## Pourquoi Workout.cool ?\n\nWorkout.cool est né de la volonté"
  },
  {
    "path": "content/about/pt.mdx",
    "chars": 2152,
    "preview": "import Link from \"next/link\";\n\n# Sobre o Workout.cool\n\n## Por que Workout.cool?\n\nWorkout.cool nasceu do desejo de oferec"
  },
  {
    "path": "content/about/ru.mdx",
    "chars": 2167,
    "preview": "import Link from \"next/link\";\n\n# О Workout.cool\n\n## Почему Workout.cool?\n\nWorkout.cool родился из желания предложить над"
  },
  {
    "path": "content/about/zh-CN.mdx",
    "chars": 961,
    "preview": "import Link from \"next/link\";\n\n# 关于 Workout.cool\n\n## 为什么选择 Workout.cool?\n\nWorkout.cool 诞生于在原项目 <WorkoutLol variant=\"mute"
  },
  {
    "path": "content/privacy-policy/en.mdx",
    "chars": 5016,
    "preview": "# WorkoutCool Privacy Policy\n\n## 1. Introduction\n\nAt **WorkoutCool**, we take our users' privacy very seriously.  \nThis "
  },
  {
    "path": "content/privacy-policy/es.mdx",
    "chars": 5598,
    "preview": "# Política de Privacidad de WorkoutCool\n\n## 1. Introducción\n\nEn **WorkoutCool**, tomamos muy en serio la privacidad de n"
  },
  {
    "path": "content/privacy-policy/fr.mdx",
    "chars": 5959,
    "preview": "# Politique de Confidentialité WorkoutCool\n\n## 1. Introduction\n\nChez **WorkoutCool**, nous accordons une grande importan"
  },
  {
    "path": "content/privacy-policy/pt.mdx",
    "chars": 5367,
    "preview": "# Política de Privacidade WorkoutCool\n\n## 1. Introdução\n\nNa **WorkoutCool**, levamos a privacidade dos nossos usuários m"
  },
  {
    "path": "content/privacy-policy/ru.mdx",
    "chars": 5633,
    "preview": "# Политика конфиденциальности WorkoutCool\n\n## 1. Введение\n\nВ **WorkoutCool** мы очень серьезно относимся к конфиденциаль"
  },
  {
    "path": "content/privacy-policy/zh-CN.mdx",
    "chars": 2163,
    "preview": "# WorkoutCool 隐私政策\n\n## 1. 简介\n\n在 **WorkoutCool**,我们非常重视用户的隐私。  \n本隐私政策解释了我们如何收集、使用和保护您的个人数据。  \n使用我们的服务即表示您同意以下所述的做法。\n\n## 2"
  },
  {
    "path": "content/sales-terms/en.mdx",
    "chars": 4467,
    "preview": "# General Terms of Sale – WorkoutCool\n\n## ARTICLE 1: Purpose\n\nThese terms govern the provision of services offered via t"
  },
  {
    "path": "content/sales-terms/es.mdx",
    "chars": 5050,
    "preview": "# Términos Generales de Venta – WorkoutCool\n\n## ARTÍCULO 1: Objeto\n\nEstos términos rigen la prestación de servicios ofre"
  },
  {
    "path": "content/sales-terms/fr.mdx",
    "chars": 4992,
    "preview": "# Conditions Générales de Vente – WorkoutCool\n\n## ARTICLE 1 : Objet\n\nLes présentes conditions régissent la fourniture de"
  },
  {
    "path": "content/sales-terms/pt.mdx",
    "chars": 4821,
    "preview": "# Termos Gerais de Venda – WorkoutCool\n\n## ARTIGO 1: Objeto\n\nEstes termos regem a prestação de serviços oferecidos atrav"
  },
  {
    "path": "content/sales-terms/ru.mdx",
    "chars": 4908,
    "preview": "# Общие условия продажи – WorkoutCool\n\n## СТАТЬЯ 1: Предмет\n\nЭти условия регулируют предоставление услуг, предлагаемых ч"
  },
  {
    "path": "content/sales-terms/zh-CN.mdx",
    "chars": 1674,
    "preview": "# 销售通用条款 – WorkoutCool\n\n## 第1条:目的\n\n这些条款规范通过 WorkoutCool 平台提供的服务(SaaS 模式)。  \n通过订阅计划或使用 WorkoutCool 应用程序,客户完全无条件地同意这些销售条款,"
  },
  {
    "path": "content/terms/en.mdx",
    "chars": 5333,
    "preview": "# Terms of Use – WorkoutCool\n\nThese Terms of Use (\"Terms\") define the conditions for accessing and using the services pr"
  },
  {
    "path": "content/terms/es.mdx",
    "chars": 5948,
    "preview": "# Términos de Uso – WorkoutCool\n\nEstos Términos de Uso (\"Términos\") definen las condiciones para acceder y usar los serv"
  },
  {
    "path": "content/terms/fr.mdx",
    "chars": 5973,
    "preview": "# Conditions Générales d’Utilisation – WorkoutCool\n\nLes présentes Conditions Générales d’Utilisation (ci-après « CGU ») "
  },
  {
    "path": "content/terms/pt.mdx",
    "chars": 5664,
    "preview": "# Termos de Uso – WorkoutCool\n\nEstes Termos de Uso (\"Termos\") definem as condições para acessar e usar os serviços forne"
  },
  {
    "path": "content/terms/ru.mdx",
    "chars": 5834,
    "preview": "# Условия использования – WorkoutCool\n\nЭти Условия использования (\"Условия\") определяют условия доступа и использования "
  },
  {
    "path": "content/terms/zh-CN.mdx",
    "chars": 1942,
    "preview": "# 使用条款 – WorkoutCool\n\n这些使用条款(\"条款\")定义了访问和使用 WorkoutCool 平台提供的服务的条件,并规范 WorkoutCool 与其用户之间的权利和义务。\n\n_最后更新:2025年5月3日_\n\n## 第1"
  },
  {
    "path": "data/sample-exercises.csv",
    "chars": 35541,
    "preview": "id,name,name_en,description,description_en,full_video_url,full_video_image_url,introduction,introduction_en,slug,slug_en"
  },
  {
    "path": "docker-compose.yml",
    "chars": 532,
    "preview": "services:\n  postgres:\n    image: postgres:15\n    ports:\n      - \"${DB_PORT:-5432}:5432\"\n    volumes:\n      - pgdata:/var"
  },
  {
    "path": "docs/SELF-HOSTING.md",
    "chars": 8273,
    "preview": "# Self-Hosting Workout-Cool\n\nDeploy **Workout-Cool** on your own server using Docker. This guide provides two deployment"
  },
  {
    "path": "emails/ContactSupportEmail.tsx",
    "chars": 1518,
    "preview": "import * as React from \"react\";\nimport { Body, Container, Head, Heading, Hr, Html, Preview, Section, Text, Tailwind } fr"
  },
  {
    "path": "emails/DeleteAccountEmail.tsx",
    "chars": 1070,
    "preview": "import { Link, Section, Text } from \"@react-email/components\";\n\nimport { SiteConfig } from \"@/shared/config/site-config\""
  },
  {
    "path": "emails/ResetPasswordEmail.tsx",
    "chars": 2075,
    "preview": "import * as React from \"react\";\nimport { Button, Heading, Hr, Link, Section, Text } from \"@react-email/components\";\n\nimp"
  },
  {
    "path": "emails/VerifyEmail.tsx",
    "chars": 1907,
    "preview": "import * as React from \"react\";\nimport { Button, Heading, Hr, Link, Section, Text } from \"@react-email/components\";\n\nimp"
  },
  {
    "path": "emails/utils/BaseEmailLayout.tsx",
    "chars": 2868,
    "preview": "import * as React from \"react\";\nimport { Body, Container, Head, Hr, Html, Img, Preview, Section, Text, Tailwind } from \""
  },
  {
    "path": "eslint.config.mjs",
    "chars": 5556,
    "preview": "import { fixupConfigRules, fixupPluginRules } from \"@eslint/compat\";\nimport js from \"@eslint/js\";\nimport { FlatCompat } "
  },
  {
    "path": "locales/client.ts",
    "chars": 1350,
    "preview": "\"use client\";\n\nimport { createI18nClient } from \"next-international/client\";\n\n// NOTE: Also update middleware.ts to supp"
  },
  {
    "path": "locales/en.ts",
    "chars": 89855,
    "preview": "export default {\n  leaderboard: {\n    title: \"Leaderboard\",\n    description: \"Top workout champions\",\n    champion_badge"
  },
  {
    "path": "locales/es.ts",
    "chars": 96809,
    "preview": "export default {\n  leaderboard: {\n    title: \"Clasificación\",\n    description: \"Campeones de entrenamientos\",\n    champi"
  },
  {
    "path": "locales/fr.ts",
    "chars": 99940,
    "preview": "export default {\n  leaderboard: {\n    title: \"Classification\",\n    description: \"Champions des entraînements\",\n    champ"
  },
  {
    "path": "locales/heart-rate-zones-translations.ts",
    "chars": 8702,
    "preview": "// SEO-optimized translations for heart rate zones calculator\n\nexport const heartRateZonesTranslations = {\n  en: {\n    m"
  },
  {
    "path": "locales/pt.ts",
    "chars": 94882,
    "preview": "export default {\n  leaderboard: {\n    title: \"Classificação\",\n    description: \"Campeões dos treinos\",\n    champion_badg"
  },
  {
    "path": "locales/ru.ts",
    "chars": 91823,
    "preview": "export default {\n  leaderboard: {\n    title: \"Рейтинг\",\n    description: \"Чемпионы тренировок\",\n    champion_badge: \"🏆 Ч"
  },
  {
    "path": "locales/server.ts",
    "chars": 319,
    "preview": "import { createI18nServer } from \"next-international/server\";\n\nexport const { getI18n, getScopedI18n, getStaticParams } "
  },
  {
    "path": "locales/types.ts",
    "chars": 120,
    "preview": "export const locales = [\"en\", \"fr\", \"es\", \"zh-CN\", \"ru\", \"pt\"] as const;\nexport type Locale = (typeof locales)[number];\n"
  },
  {
    "path": "locales/zh-CN.ts",
    "chars": 56390,
    "preview": "export default {\n  leaderboard: {\n    title: \"排行榜\",\n    description: \"锻炼冠军\",\n    champion_badge: \"🏆 冠军\",\n    runner_up_b"
  },
  {
    "path": "middleware.ts",
    "chars": 3249,
    "preview": "// middleware.ts\nimport { createI18nMiddleware } from \"next-international/middleware\";\nimport { NextRequest, NextRespons"
  },
  {
    "path": "next.config.ts",
    "chars": 473,
    "preview": "import type { NextConfig } from \"next\";\n\nconst nextConfig: NextConfig = {\n  reactStrictMode: true,\n  images: {\n    unopt"
  },
  {
    "path": "nextauth.d.ts",
    "chars": 301,
    "preview": "/* eslint-disable @typescript-eslint/consistent-type-definitions */\nimport type { DefaultSession } from \"next-auth\";\n\nde"
  }
]

// ... and 464 more files (download for full content)

About this extraction

This page contains the full source code of the Snouzy/workout-cool GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 664 files (2.6 MB), approximately 732.1k tokens, and a symbol index with 1182 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!