Initial commit: Fahrrad Verschleißteile Tracker
- Next.js SPA mit Bun Runtime - Prisma mit SQLite Datenbank - Vollständige CRUD-Operationen für Fahrräder, Verschleißteile und Wartungshistorie - Warnsystem für bevorstehende Wartungen - Statistik-Features (Gesamtkosten, durchschnittliche Lebensdauer) - Zod-Validierung für alle API-Requests - Umfassende Test-Suite (41 Tests)
This commit is contained in:
92
app/page.tsx
Normal file
92
app/page.tsx
Normal file
@@ -0,0 +1,92 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { BikeWithParts } from '@/types'
|
||||
import BikeCard from '@/app/components/BikeCard'
|
||||
import BikeForm from '@/app/components/BikeForm'
|
||||
import StatsCard from '@/app/components/StatsCard'
|
||||
|
||||
export default function Home() {
|
||||
const [bikes, setBikes] = useState<BikeWithParts[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [showForm, setShowForm] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
fetchBikes()
|
||||
}, [])
|
||||
|
||||
const fetchBikes = async () => {
|
||||
try {
|
||||
const response = await fetch('/api/bikes')
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
setBikes(data)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching bikes:', error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleBikeCreated = () => {
|
||||
setShowForm(false)
|
||||
fetchBikes()
|
||||
}
|
||||
|
||||
const handleBikeDeleted = () => {
|
||||
fetchBikes()
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center">
|
||||
<div className="text-lg">Lade...</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="min-h-screen p-8 bg-gray-50">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<div className="mb-8 flex justify-between items-center">
|
||||
<h1 className="text-4xl font-bold text-gray-900">
|
||||
Fahrrad Verschleißteile Tracker
|
||||
</h1>
|
||||
<button
|
||||
onClick={() => setShowForm(!showForm)}
|
||||
className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
{showForm ? 'Abbrechen' : '+ Neues Fahrrad'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{showForm && (
|
||||
<div className="mb-8">
|
||||
<BikeForm onSuccess={handleBikeCreated} onCancel={() => setShowForm(false)} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<StatsCard bikes={bikes} />
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mt-8">
|
||||
{bikes.length === 0 ? (
|
||||
<div className="col-span-full text-center py-12 text-gray-500">
|
||||
<p className="text-lg">Noch keine Fahrräder erfasst.</p>
|
||||
<p className="mt-2">Klicken Sie auf "Neues Fahrrad" um zu beginnen.</p>
|
||||
</div>
|
||||
) : (
|
||||
bikes.map((bike) => (
|
||||
<BikeCard
|
||||
key={bike.id}
|
||||
bike={bike}
|
||||
onDelete={handleBikeDeleted}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user