Add Dashboard with weekly calendar and today's workout

- Dashboard.jsx: main landing page after login
- Coach greeting based on time of day
- Weekly calendar showing workout days
- Today's workout card with exercises
- Quick stats (workouts/week, streak)
- Upcoming workouts list
- Full responsive CSS
- App.jsx updated to show Dashboard first
This commit is contained in:
2026-02-01 11:09:16 +01:00
parent 03b1327160
commit a2dc8c7c12
3 changed files with 659 additions and 69 deletions
+15 -69
View File
@@ -1,34 +1,30 @@
import { useState, useEffect } from 'react'
import { useAuth } from './context/AuthContext'
import Dashboard from './pages/Dashboard'
import './App.css'
const API_URL = '/api'
function App() {
const { user, logout } = useAuth()
const [view, setView] = useState('program')
const [view, setView] = useState('dashboard')
const [program, setProgram] = useState(null)
const [selectedDay, setSelectedDay] = useState(null)
const [currentWeek, setCurrentWeek] = useState(1)
const [logs, setLogs] = useState({})
const [loading, setLoading] = useState(true)
const [loading, setLoading] = useState(false)
const userId = user?.id || 1
const today = new Date().toISOString().split('T')[0]
useEffect(() => {
fetchProgram()
}, [])
const fetchProgram = async () => {
if (program) return // Already loaded
try {
const res = await fetch(`${API_URL}/programs/1`)
const data = await res.json()
setProgram(data)
setLoading(false)
} catch (err) {
console.error('Failed to fetch program:', err)
setLoading(false)
}
}
@@ -90,21 +86,19 @@ function App() {
}
}
const startWorkout = (day) => {
const startWorkout = async (day) => {
await fetchProgram() // Ensure program is loaded
setSelectedDay(day)
setView('workout')
fetchLogs(day.id)
}
if (loading) {
return (
<div className="app loading">
<div className="spinner"></div>
<p>Laddar program...</p>
</div>
)
// Dashboard view (default after login)
if (view === 'dashboard') {
return <Dashboard onStartWorkout={startWorkout} />
}
// Workout view
if (view === 'workout' && selectedDay) {
return (
<WorkoutView
@@ -112,65 +106,17 @@ function App() {
week={currentWeek}
logs={logs}
onLogSet={logSet}
onBack={() => setView('program')}
onBack={() => setView('dashboard')}
fetchProgression={fetchProgression}
/>
)
}
// Fallback loading
return (
<div className="app">
<header className="header">
<div className="header-left">
<h1>🏋 Gravl</h1>
<button className="logout-btn" onClick={logout}>Logga ut</button>
</div>
<div className="week-selector">
<button
onClick={() => setCurrentWeek(w => Math.max(1, w - 1))}
disabled={currentWeek === 1}
>
</button>
<span>Vecka {currentWeek}</span>
<button
onClick={() => setCurrentWeek(w => Math.min(program?.weeks || 6, w + 1))}
disabled={currentWeek === (program?.weeks || 6)}
>
</button>
</div>
</header>
<main className="main">
<section className="program-info">
<h2>{program?.name || 'Laddar...'}</h2>
<p>{program?.description}</p>
</section>
<section className="days-list">
<h3>Veckans pass</h3>
{program?.days?.map((day, idx) => (
<div key={day.id} className="day-card" onClick={() => startWorkout(day)}>
<div className="day-header">
<span className="day-number">Dag {day.day_number}</span>
<span className="day-name">{day.name}</span>
</div>
<div className="day-exercises">
{day.exercises?.filter(e => e.name).slice(0, 3).map((ex, i) => (
<span key={i} className="exercise-tag">{ex.name}</span>
))}
{day.exercises?.filter(e => e.name).length > 3 && (
<span className="exercise-tag more">+{day.exercises.filter(e => e.name).length - 3}</span>
)}
</div>
<div className="day-action">
Starta
</div>
</div>
))}
</section>
</main>
<div className="app loading">
<div className="spinner"></div>
<p>Laddar...</p>
</div>
)
}