Initial Commit

This commit is contained in:
2026-06-06 13:23:33 +01:00
commit 1a0d45dd67
58 changed files with 5268 additions and 0 deletions
+74
View File
@@ -0,0 +1,74 @@
import { Outlet, NavLink, useNavigate } from 'react-router-dom'
import { useAuthStore } from '../../hooks/useAuth'
const nav = [
{ to: '/', label: 'Dashboard', icon: '📊', exact: true },
{ to: '/activities', label: 'Activities', icon: '🏃' },
{ to: '/health', label: 'Health', icon: '❤️' },
{ to: '/routes', label: 'Routes', icon: '🗺️' },
{ to: '/records', label: 'Records', icon: '🏆' },
{ to: '/upload', label: 'Import', icon: '⬆️' },
]
export default function Layout() {
const { user, logout } = useAuthStore()
const navigate = useNavigate()
const handleLogout = () => {
logout()
navigate('/login')
}
return (
<div className="flex h-screen overflow-hidden bg-gray-950">
{/* Sidebar */}
<aside className="w-56 flex-shrink-0 bg-gray-900 border-r border-gray-800 flex flex-col">
{/* Logo */}
<div className="px-4 py-5 border-b border-gray-800">
<h1 className="text-lg font-bold text-white tracking-tight">
<span className="text-blue-400">Fit</span>Tracker
</h1>
{user && (
<p className="text-xs text-gray-500 mt-0.5">@{user.username}</p>
)}
</div>
{/* Nav */}
<nav className="flex-1 py-4 overflow-y-auto">
{nav.map(({ to, label, icon, exact }) => (
<NavLink
key={to}
to={to}
end={exact}
className={({ isActive }) =>
`flex items-center gap-3 px-4 py-2.5 text-sm transition-colors ${
isActive
? 'bg-blue-600/20 text-blue-400 border-r-2 border-blue-400'
: 'text-gray-400 hover:text-gray-100 hover:bg-gray-800'
}`
}
>
<span>{icon}</span>
{label}
</NavLink>
))}
</nav>
{/* Footer */}
<div className="px-4 py-4 border-t border-gray-800">
<button
onClick={handleLogout}
className="w-full text-left text-xs text-gray-500 hover:text-gray-300 transition-colors"
>
Sign out
</button>
</div>
</aside>
{/* Main content */}
<main className="flex-1 overflow-y-auto">
<Outlet />
</main>
</div>
)
}
+18
View File
@@ -0,0 +1,18 @@
const accentColors = {
default: 'text-white',
red: 'text-red-400',
blue: 'text-blue-400',
green: 'text-green-400',
orange: 'text-orange-400',
purple: 'text-purple-400',
}
export default function StatCard({ label, value, accent = 'default', sub }) {
return (
<div className="bg-gray-800/60 rounded-xl p-3 border border-gray-700/50">
<p className="text-xs text-gray-500 mb-1">{label}</p>
<p className={`text-lg font-semibold ${accentColors[accent]}`}>{value}</p>
{sub && <p className="text-xs text-gray-600 mt-0.5">{sub}</p>}
</div>
)
}