|
|
| // src/types/index.ts |
| export interface Exercise { |
| name: string; |
| sets: number; |
| reps: string; |
| type: string; |
| } |
|
|
| export interface WorkoutPlan { |
| duration: string; |
| daysPerWeek: number; |
| goal: string; |
| exercises: Exercise[]; |
| } |
|
|
| export interface Question { |
| id: string; |
| question: string; |
| options: { |
| text: string; |
| value: string; |
| icon: string; |
| }[]; |
| } |
|
|
|
|
| // src/services/workoutService.ts |
| import { WorkoutPlan } from '../types'; |
|
|
| export const generateWorkoutPlan = async (answers: any): Promise<WorkoutPlan> => { |
| const response = await fetch('/api/workout-plan', { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| }, |
| body: JSON.stringify({ answers }), |
| }); |
| |
| if (!response.ok) { |
| throw new Error('Failed to generate workout plan'); |
| } |
| |
| return response.json(); |
| }; |
|
|
|
|
| // server/index.ts |
| import express from 'express'; |
| import cors from 'cors'; |
| import { ClerkExpressRequireAuth } from '@clerk/clerk-sdk-node'; |
|
|
| const app = express(); |
| app.use(cors()); |
| app.use(express.json()); |
|
|
| // API routes |
| app.get('/api/workout-plan', ClerkExpressRequireAuth(), async (req, res) => { |
| // Generate workout plan logic here |
| res.json({ |
| plan: { |
| duration: '4 weeks', |
| daysPerWeek: 4, |
| goal: 'Muscle Building', |
| exercises: [ |
| // Exercise data |
| ] |
| } |
| }); |
| }); |
|
|
| const PORT = process.env.PORT || 5000; |
| app.listen(PORT, () => { |
| console.log(`Server running on port ${PORT}`); |
| }); |
|
|
|
|
| // src/pages/MainApp.tsx |
| import React from 'react'; |
| import { Outlet } from 'react-router-dom'; |
| import BottomNav from '../components/BottomNav'; |
|
|
| const MainApp = () => { |
| return ( |
| <div className="min-h-screen bg-gradient-to-br from-gray-900 to-gray-800 text-white"> |
| <div className="pb-20"> |
| <Outlet /> |
| </div> |
| <BottomNav /> |
| </div> |
| ); |
| }; |
|
|
| export default MainApp; |
|
|
|
|
| // src/pages/LandingPage.tsx |
| import React from 'react'; |
| import { useClerk } from '@clerk/clerk-react'; |
| import { Button } from '../components/ui/button'; |
|
|
| const LandingPage = () => { |
| const { openSignIn } = useClerk(); |
|
|
| return ( |
| <div className="min-h-screen px-4 py-8 pb-24 bg-gradient-to-br from-gray-900 to-gray-800 text-white"> |
| <div className="max-w-6xl mx-auto"> |
| <nav className="flex justify-between items-center py-6"> |
| <div> |
| <h1 className="text-2xl font-bold">Fit<span className="text-indigo-500">AI</span></h1> |
| </div> |
| <UserButton /> |
| </nav> |
|
|
| <div className="flex flex-col md:flex-row items-center justify-between mt-16"> |
| {/* Rest of landing page content */} |
| <Button onClick={() => openSignIn()}> |
| Rozpocznij teraz |
| </Button> |
| </div> |
| </div> |
| </div> |
| ); |
| }; |
|
|
| export default LandingPage; |
|
|
|
|
| // src/App.tsx |
| import React, { useState } from 'react'; |
| import { ClerkProvider, SignedIn, SignedOut, UserButton } from '@clerk/clerk-react'; |
| import LandingPage from './pages/LandingPage'; |
| import MainApp from './pages/MainApp'; |
| import './App.css'; |
|
|
| const clerkPubKey = process.env.REACT_APP_CLERK_PUBLISHABLE_KEY || ''; |
|
|
| function App() { |
| return ( |
| <ClerkProvider publishableKey={clerkPubKey}> |
| <SignedIn> |
| <MainApp /> |
| </SignedIn> |
| <SignedOut> |
| <LandingPage /> |
| </SignedOut> |
| </ClerkProvider> |
| ); |
| } |
|
|
| export default App; |
|
|
| <!DOCTYPE html> |
| <html lang="pl"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>FitAI - Twój inteligentny plan treningowy</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <script src="https://cdn.jsdelivr.net/npm/@clerk/clerk-js@latest/dist/clerk.browser.js"></script> |
| <script> |
| tailwind.config = { |
| theme: { |
| extend: { |
| colors: { |
| primary: '#6366f1', |
| secondary: '#8b5cf6', |
| dark: '#1e293b', |
| light: '#f8fafc' |
| } |
| } |
| } |
| } |
| </script> |
| <style> |
| @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap'); |
| |
| body { |
| font-family: 'Poppins', sans-serif; |
| background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%); |
| min-height: 100vh; |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| } |
| |
| .card { |
| backdrop-filter: blur(10px); |
| background: rgba(255, 255, 255, 0.08); |
| border-radius: 20px; |
| border: 1px solid rgba(255, 255, 255, 0.1); |
| box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); |
| } |
| |
| .exercise-card { |
| transition: all 0.3s ease; |
| } |
| |
| .exercise-card:hover { |
| transform: translateY(-5px); |
| box-shadow: 0 12px 20px rgba(0, 0, 0, 0.2); |
| } |
| |
| .progress-bar { |
| height: 8px; |
| border-radius: 4px; |
| overflow: hidden; |
| } |
| |
| .progress-fill { |
| height: 100%; |
| border-radius: 4px; |
| transition: width 0.5s ease-in-out; |
| } |
| |
| .floating-btn { |
| box-shadow: 0 6px 20px rgba(99, 102, 241, 0.4); |
| transition: all 0.3s ease; |
| } |
| |
| .floating-btn:hover { |
| transform: scale(1.05); |
| box-shadow: 0 8px 25px rgba(99, 102, 241, 0.6); |
| } |
| |
| .animate-pulse-slow { |
| animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; |
| } |
| |
| @keyframes pulse { |
| 0%, 100% { opacity: 1; } |
| 50% { opacity: 0.5; } |
| } |
| |
| .slide-in { |
| animation: slideIn 0.5s ease-out forwards; |
| } |
| |
| @keyframes slideIn { |
| from { |
| opacity: 0; |
| transform: translateY(20px); |
| } |
| to { |
| opacity: 1; |
| transform: translateY(0); |
| } |
| } |
| |
| .sticky-nav { |
| position: fixed; |
| bottom: 0; |
| left: 0; |
| right: 0; |
| background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%); |
| padding: 12px 0; |
| z-index: 100; |
| display: flex; |
| justify-content: center; |
| height: 60px; |
| box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.3); |
| } |
| |
| .clerk-user-button { |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| justify-content: center; |
| } |
| |
| .clerk-user-button-trigger { |
| width: 24px; |
| height: 24px; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| } |
| |
| .nav-content { |
| width: 100%; |
| max-width: 1024px; |
| display: flex; |
| justify-content: space-around; |
| } |
| |
| .main-container { |
| padding-bottom: 120px; |
| width: 100%; |
| max-width: 1024px; |
| margin-bottom: 60px; |
| } |
| |
| @media (min-width: 768px) { |
| .main-container { |
| padding: 2rem; |
| margin-top: 2rem; |
| border-radius: 20px; |
| background: rgba(30, 41, 59, 0.7); |
| backdrop-filter: blur(10px); |
| box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); |
| border: 1px solid rgba(255, 255, 255, 0.1); |
| } |
| |
| .exercise-card { |
| max-width: 600px; |
| margin-left: auto; |
| margin-right: auto; |
| } |
| |
| .card { |
| max-width: 600px; |
| margin-left: auto; |
| margin-right: auto; |
| } |
| } |
| </style> |
| </head> |
| <body class="text-light"> |
| |
| <section id="landing" class="min-h-screen px-4 py-8 pb-24"> |
| <div class="max-w-6xl mx-auto"> |
| <nav class="flex justify-between items-center py-6"> |
| <div> |
| <h1 class="text-2xl font-bold">Fit<span class="text-primary">AI</span></h1> |
| </div> |
| <div id="user-button"></div> |
| </nav> |
|
|
| <div class="flex flex-col md:flex-row items-center justify-between mt-16"> |
| <div class="md:w-1/2 mb-12 md:mb-0"> |
| <h2 class="text-4xl md:text-5xl font-bold mb-6">Twój inteligentny <span class="text-primary">plan treningowy</span></h2> |
| <p class="text-lg opacity-80 mb-8">Odpowiedz na kilka pytań, a nasze AI stworzy dla Ciebie spersonalizowany plan treningowy dostosowany do Twoich celów i możliwości.</p> |
| <button id="getStartedBtn" class="px-8 py-3 bg-primary rounded-xl font-medium flex items-center space-x-2 floating-btn"> |
| <span>Rozpocznij teraz</span> |
| <i class="fas fa-arrow-right"></i> |
| </button> |
| </div> |
| <div class="md:w-1/2"> |
| <div class="card p-8 relative overflow-hidden"> |
| <div class="absolute -top-10 -right-10 w-32 h-32 rounded-full bg-primary/20"></div> |
| <div class="absolute -bottom-8 -left-8 w-24 h-24 rounded-full bg-secondary/20"></div> |
| <img src="https://images.unsplash.com/photo-1571019613454-1cb2f99b2d8b?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=800&q=80" |
| alt="Fitness" class="w-full h-auto rounded-lg"> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="mt-24"> |
| <h3 class="text-2xl font-bold mb-8 text-center">Dlaczego FitAI?</h3> |
| <div class="grid md:grid-cols-3 gap-8"> |
| <div class="card p-6"> |
| <div class="w-12 h-12 rounded-full bg-primary/20 flex items-center justify-center mb-4"> |
| <i class="fas fa-brain text-primary"></i> |
| </div> |
| <h4 class="font-bold mb-2">AI-Powered</h4> |
| <p class="opacity-80">Nasze algorytmy AI tworzą idealny plan treningowy dostosowany do Ciebie.</p> |
| </div> |
| <div class="card p-6"> |
| <div class="w-12 h-12 rounded-full bg-secondary/20 flex items-center justify-center mb-4"> |
| <i class="fas fa-bolt text-secondary"></i> |
| </div> |
| <h4 class="font-bold mb-2">Szybkie efekty</h4> |
| <p class="opacity-80">Optymalizowane plany treningowe dla maksymalnych rezultatów.</p> |
| </div> |
| <div class="card p-6"> |
| <div class="w-12 h-12 rounded-full bg-primary/20 flex items-center justify-center mb-4"> |
| <i class="fas fa-chart-line text-primary"></i> |
| </div> |
| <h4 class="font-bold mb-2">Śledź postępy</h4> |
| <p class="opacity-80">Monitoruj swoje osiągnięcia i dostosowuj plan do swoich potrzeb.</p> |
| </div> |
| </div> |
| </div> |
| </div> |
| </section> |
|
|
| |
| <div id="app" class="min-h-screen px-4 py-8 main-container hidden"> |
| |
| <header class="flex justify-between items-center mb-8"> |
| <div> |
| <h1 class="text-2xl font-bold">Fit<span class="text-primary">AI</span></h1> |
| <p class="text-sm opacity-75">Inteligentny plan treningowy</p> |
| </div> |
| <div class="flex space-x-3"> |
| <button class="w-10 h-10 rounded-full bg-primary/20 flex items-center justify-center"> |
| <i class="fas fa-user text-primary"></i> |
| </button> |
| <button class="w-10 h-10 rounded-full bg-primary/20 flex items-center justify-center"> |
| <i class="fas fa-cog text-primary"></i> |
| </button> |
| </div> |
| </header> |
|
|
| |
| <main> |
| |
| <section class="card p-6 mb-8 relative overflow-hidden"> |
| <div class="absolute -top-10 -right-10 w-32 h-32 rounded-full bg-primary/20"></div> |
| <div class="absolute -bottom-8 -left-8 w-24 h-24 rounded-full bg-secondary/20"></div> |
| |
| <div class="relative z-10"> |
| <h2 class="text-xl font-bold mb-2">Stwórz swój idealny plan treningowy</h2> |
| <p class="text-sm opacity-80 mb-4">Odpowiedz na kilka pytań, a nasze AI stworzy dla Ciebie spersonalizowany plan treningowy</p> |
| <button id="startBtn" class="w-full py-3 bg-primary rounded-xl font-medium flex items-center justify-center space-x-2 floating-btn"> |
| <span>Rozpocznij teraz</span> |
| <i class="fas fa-arrow-right"></i> |
| </button> |
| </div> |
| </section> |
|
|
| |
| <section id="questionnaire" class="hidden"> |
| <div class="card p-6 mb-6"> |
| <div class="flex justify-between items-center mb-4"> |
| <h3 class="font-medium">Twój plan treningowy</h3> |
| <span class="text-sm bg-primary/20 px-2 py-1 rounded">Krok <span id="currentStep">1</span>/5</span> |
| </div> |
| |
| <div class="progress-bar bg-gray-700 mb-6"> |
| <div id="progressFill" class="progress-fill bg-primary w-1/5"></div> |
| </div> |
| |
| <div id="questionContainer"> |
| |
| </div> |
| |
| <div class="flex justify-between mt-6"> |
| <button id="prevBtn" class="py-2 px-4 rounded-lg bg-gray-700 opacity-0 invisible"> |
| <i class="fas fa-arrow-left mr-2"></i>Wstecz |
| </button> |
| <button id="nextBtn" class="py-2 px-4 bg-primary rounded-lg flex items-center"> |
| <span>Dalej</span> |
| <i class="fas fa-arrow-right ml-2"></i> |
| </button> |
| </div> |
| </div> |
| </section> |
|
|
| |
| <section id="generating" class="hidden"> |
| <div class="card p-8 text-center"> |
| <div class="flex justify-center mb-6"> |
| <div class="w-16 h-16 rounded-full bg-primary/20 flex items-center justify-center animate-pulse-slow"> |
| <i class="fas fa-brain text-2xl text-primary"></i> |
| </div> |
| </div> |
| <h3 class="text-xl font-bold mb-2">Tworzę Twój plan...</h3> |
| <p class="opacity-80 mb-6">Nasze AI analizuje Twoje odpowiedzi i tworzy idealny plan treningowy</p> |
| <div class="flex space-x-2 justify-center"> |
| <div class="w-2 h-2 bg-primary rounded-full animate-pulse-slow"></div> |
| <div class="w-2 h-2 bg-primary rounded-full animate-pulse-slow delay-150"></div> |
| <div class="w-2 h-2 bg-primary rounded-full animate-pulse-slow delay-300"></div> |
| </div> |
| </div> |
| </section> |
|
|
| |
| <section id="results" class="hidden"> |
| <div class="flex justify-between items-center mb-6"> |
| <h2 class="text-xl font-bold">Twój plan treningowy</h2> |
| <button class="w-10 h-10 rounded-full bg-primary/20 flex items-center justify-center"> |
| <i class="fas fa-download text-primary"></i> |
| </button> |
| </div> |
| |
| <div class="card p-5 mb-6"> |
| <div class="flex justify-between mb-4"> |
| <div> |
| <h3 class="font-bold">Plan na 4 tygodnie</h3> |
| <p class="text-sm opacity-80">Dla średniozaawansowanych</p> |
| </div> |
| <div class="bg-secondary/20 text-secondary px-3 py-1 rounded-lg text-sm font-medium"> |
| 4 dni/tydzień |
| </div> |
| </div> |
| |
| <div class="grid grid-cols-2 gap-2 mb-4"> |
| <div class="bg-gray-800/50 rounded-lg p-3 text-center"> |
| <p class="text-sm opacity-80">Cel</p> |
| <p class="font-medium">Budowa mięśni</p> |
| </div> |
| <div class="bg-gray-800/50 rounded-lg p-3 text-center"> |
| <p class="text-sm opacity-80">Czas</p> |
| <p class="font-medium">45-60 min</p> |
| </div> |
| </div> |
| |
| <button class="w-full py-2 bg-primary/20 text-primary rounded-lg font-medium"> |
| <i class="fas fa-sync-alt mr-2"></i>Wygeneruj ponownie |
| </button> |
| </div> |
| |
| <h3 class="font-bold mb-4">Dzisiejszy trening: Górna część ciała</h3> |
| |
| <div class="space-y-4"> |
| |
| <div class="exercise-card card p-4 slide-in"> |
| <div class="flex items-start"> |
| <div class="bg-primary/20 w-12 h-12 rounded-lg flex items-center justify-center mr-3"> |
| <i class="fas fa-dumbbell text-primary"></i> |
| </div> |
| <div class="flex-1"> |
| <h4 class="font-bold">Wyciskanie sztangi leżąc</h4> |
| <div class="flex text-sm mt-1"> |
| <span class="bg-gray-700 px-2 py-1 rounded mr-2">4 serie</span> |
| <span class="bg-gray-700 px-2 py-1 rounded">8-12 powtórzeń</span> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| <div class="exercise-card card p-4 slide-in"> |
| <div class="flex items-start"> |
| <div class="bg-secondary/20 w-12 h-12 rounded-lg flex items-center justify-center mr-3"> |
| <i class="fas fa-dumbbell text-secondary"></i> |
| </div> |
| <div class="flex-1"> |
| <h4 class="font-bold">Wyciskanie hantli na skosie</h4> |
| <div class="flex text-sm mt-1"> |
| <span class="bg-gray-700 px-2 py-1 rounded mr-2">3 serie</span> |
| <span class="bg-gray-700 px-2 py-1 rounded">10-15 powtórzeń</span> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| <div class="exercise-card card p-4 slide-in"> |
| <div class="flex items-start"> |
| <div class="bg-primary/20 w-12 h-12 rounded-lg flex items-center justify-center mr-3"> |
| <i class="fas fa-dumbbell text-primary"></i> |
| </div> |
| <div class="flex-1"> |
| <h4 class="font-bold">Podciąganie na drążku</h4> |
| <div class="flex text-sm mt-1"> |
| <span class="bg-gray-700 px-2 py-1 rounded mr-2">4 serie</span> |
| <span class="bg-gray-700 px-2 py-1 rounded">6-10 powtórzeń</span> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| <div class="exercise-card card p-4 slide-in"> |
| <div class="flex items-start"> |
| <div class="bg-secondary/20 w-12 h-12 rounded-lg flex items-center justify-center mr-3"> |
| <i class="fas fa-dumbbell text-secondary"></i> |
| </div> |
| <div class="flex-1"> |
| <h4 class="font-bold">Uginanie ramion ze sztangą</h4> |
| <div class="flex text-sm mt-1"> |
| <span class="bg-gray-700 px-2 py-1 rounded mr-2">3 serie</span> |
| <span class="bg-gray-700 px-2 py-1 rounded">10-12 powtórzeń</span> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| <div class="exercise-card card p-4 slide-in"> |
| <div class="flex items-start"> |
| <div class="bg-primary/20 w-12 h-12 rounded-lg flex items-center justify-center mr-3"> |
| <i class="fas fa-dumbbell text-primary"></i> |
| </div> |
| <div class="flex-1"> |
| <h4 class="font-bold">Wyciskanie francuskie</h4> |
| <div class="flex text-sm mt-1"> |
| <span class="bg-gray-700 px-2 py-1 rounded mr-2">3 serie</span> |
| <span class="bg-gray-700 px-2 py-1 rounded">12-15 powtórzeń</span> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| <div class="mt-8 flex space-x-3"> |
| <button class="flex-1 py-3 bg-primary rounded-xl font-medium"> |
| <i class="fas fa-play-circle mr-2"></i>Rozpocznij trening |
| </button> |
| <button class="w-12 h-12 rounded-xl bg-gray-700 flex items-center justify-center"> |
| <i class="fas fa-calendar"></i> |
| </button> |
| </div> |
| </section> |
| </main> |
| |
| </div> |
|
|
| |
| <nav class="sticky-nav border-t border-gray-700" id="app-nav"> |
| <div class="nav-content px-4"> |
| <button class="flex flex-col items-center text-primary"> |
| <i class="fas fa-home text-lg"></i> |
| <span class="text-xs mt-1">Strona główna</span> |
| </button> |
| <button class="flex flex-col items-center opacity-60"> |
| <i class="fas fa-calendar text-lg"></i> |
| <span class="text-xs mt-1">Kalendarz</span> |
| </button> |
| <button class="flex flex-col items-center opacity-60"> |
| <i class="fas fa-chart-line text-lg"></i> |
| <span class="text-xs mt-1">Statystyki</span> |
| </button> |
| <div id="nav-user-button" class="flex flex-col items-center"> |
| <div class="w-6 h-6 flex items-center justify-center"> |
| <i class="fas fa-user text-lg"></i> |
| </div> |
| <span class="text-xs mt-1">Profil</span> |
| </div> |
| </nav> |
|
|
| <script> |
| |
| const state = { |
| currentStep: 0, |
| answers: {}, |
| questions: [ |
| { |
| question: "Jaki jest Twój główny cel treningowy?", |
| options: [ |
| {text: "Budowa mięśni", icon: "fas fa-dumbbell"}, |
| {text: "Redukcja tkanki tłuszczowej", icon: "fas fa-weight-scale"}, |
| {text: "Poprawa wytrzymałości", icon: "fas fa-heart-pulse"}, |
| {text: "Ogólna sprawność", icon: "fas fa-person-running"} |
| ] |
| }, |
| { |
| question: "Jaki jest Twój poziom zaawansowania?", |
| options: [ |
| {text: "Początkujący", icon: "fas fa-seedling"}, |
| {text: "Średniozaawansowany", icon: "fas fa-chart-line"}, |
| {text: "Zaawansowany", icon: "fas fa-fire"} |
| ] |
| }, |
| { |
| question: "Ile dni w tygodniu chcesz trenować?", |
| options: [ |
| {text: "2 dni", icon: "fas fa-calendar"}, |
| {text: "3 dni", icon: "fas fa-calendar"}, |
| {text: "4 dni", icon: "fas fa-calendar"}, |
| {text: "5+ dni", icon: "fas fa-calendar"} |
| ] |
| }, |
| { |
| question: "Jakie masz dostępne wyposażenie?", |
| options: [ |
| {text: "Brak sprzętu", icon: "fas fa-person"}, |
| {text: "Hantle", icon: "fas fa-dumbbell"}, |
| {text: "Sztanga", icon: "fas fa-weight-hanging"}, |
| {text: "Pełny zestaw siłowni", icon: "fas fa-building"} |
| ] |
| }, |
| { |
| question: "Ile czasu możesz poświęcić na trening?", |
| options: [ |
| {text: "30 minut", icon: "fas fa-clock"}, |
| {text: "45 minut", icon: "fas fa-clock"}, |
| {text: "60 minut", icon: "fas fa-clock"}, |
| {text: "90+ minut", icon: "fas fa-clock"} |
| ] |
| } |
| ] |
| }; |
| |
| |
| const startBtn = document.getElementById('startBtn'); |
| const questionnaire = document.getElementById('questionnaire'); |
| const generating = document.getElementById('generating'); |
| const results = document.getElementById('results'); |
| const questionContainer = document.getElementById('questionContainer'); |
| const prevBtn = document.getElementById('prevBtn'); |
| const nextBtn = document.getElementById('nextBtn'); |
| const currentStepEl = document.getElementById('currentStep'); |
| const progressFill = document.getElementById('progressFill'); |
| |
| |
| const frontendApi = 'YOUR_CLERK_FRONTEND_API_KEY'; |
| const clerkPubKey = 'YOUR_CLERK_PUBLISHABLE_KEY'; |
| |
| const clerk = new Clerk(frontendApi); |
| |
| |
| clerk.load({ |
| publishableKey: clerkPubKey |
| }).then(() => { |
| |
| clerk.mountUserButton('#user-button'); |
| clerk.mountUserButton('#nav-user-button'); |
| |
| |
| clerk.addListener(({ user }) => { |
| if (user) { |
| document.getElementById('landing').classList.add('hidden'); |
| document.getElementById('app').classList.remove('hidden'); |
| document.getElementById('app-nav').classList.remove('hidden'); |
| } else { |
| document.getElementById('landing').classList.remove('hidden'); |
| document.getElementById('app').classList.add('hidden'); |
| document.getElementById('app-nav').classList.add('hidden'); |
| } |
| }); |
| }); |
| |
| |
| function initApp() { |
| startBtn.addEventListener('click', startQuestionnaire); |
| nextBtn.addEventListener('click', goToNextStep); |
| prevBtn.addEventListener('click', goToPrevStep); |
| |
| |
| document.getElementById('getStartedBtn').addEventListener('click', () => { |
| clerk.openSignIn(); |
| }); |
| } |
| |
| |
| function startQuestionnaire() { |
| document.querySelector('section:first-child').classList.add('hidden'); |
| questionnaire.classList.remove('hidden'); |
| renderQuestion(); |
| } |
| |
| |
| function renderQuestion() { |
| const question = state.questions[state.currentStep]; |
| |
| let html = ` |
| <h4 class="font-bold mb-4">${question.question}</h4> |
| <div class="space-y-3"> |
| `; |
| |
| question.options.forEach((option, index) => { |
| html += ` |
| <div class="flex items-center p-3 bg-gray-800/50 rounded-lg cursor-pointer option" data-index="${index}"> |
| <div class="w-8 h-8 rounded-full bg-primary/20 flex items-center justify-center mr-3"> |
| <i class="${option.icon} text-primary"></i> |
| </div> |
| <span>${option.text}</span> |
| </div> |
| `; |
| }); |
| |
| html += `</div>`; |
| questionContainer.innerHTML = html; |
| |
| |
| currentStepEl.textContent = state.currentStep + 1; |
| progressFill.style.width = `${((state.currentStep + 1) / state.questions.length) * 100}%`; |
| |
| |
| prevBtn.classList.toggle('invisible', state.currentStep === 0); |
| prevBtn.classList.toggle('opacity-0', state.currentStep === 0); |
| |
| if (state.currentStep === state.questions.length - 1) { |
| nextBtn.innerHTML = '<i class="fas fa-check mr-2"></i> Wygeneruj plan'; |
| } else { |
| nextBtn.innerHTML = '<span>Dalej</span> <i class="fas fa-arrow-right ml-2"></i>'; |
| } |
| |
| |
| document.querySelectorAll('.option').forEach(option => { |
| option.addEventListener('click', function() { |
| |
| document.querySelectorAll('.option').forEach(opt => { |
| opt.classList.remove('bg-primary/20', 'border-primary'); |
| }); |
| |
| |
| this.classList.add('bg-primary/20', 'border-primary'); |
| |
| |
| state.answers[state.currentStep] = parseInt(this.dataset.index); |
| }); |
| }); |
| } |
| |
| |
| function goToNextStep() { |
| if (state.answers[state.currentStep] === undefined) { |
| alert('Proszę wybrać odpowiedź przed przejściem dalej'); |
| return; |
| } |
| |
| if (state.currentStep < state.questions.length - 1) { |
| state.currentStep++; |
| renderQuestion(); |
| } else { |
| |
| questionnaire.classList.add('hidden'); |
| generating.classList.remove('hidden'); |
| |
| |
| setTimeout(() => { |
| generating.classList.add('hidden'); |
| results.classList.remove('hidden'); |
| |
| |
| const cards = document.querySelectorAll('.exercise-card'); |
| cards.forEach((card, index) => { |
| card.style.animationDelay = `${index * 0.1}s`; |
| }); |
| }, 3000); |
| } |
| } |
| |
| |
| function goToPrevStep() { |
| if (state.currentStep > 0) { |
| state.currentStep--; |
| renderQuestion(); |
| } |
| } |
| |
| |
| document.addEventListener('DOMContentLoaded', () => { |
| initApp(); |
| |
| |
| clerk.addListener(({ user }) => { |
| if (user) { |
| document.getElementById('app-nav').classList.remove('hidden'); |
| } else { |
| document.getElementById('app-nav').classList.add('hidden'); |
| } |
| }); |
| }); |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Prochu/trainflow" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |