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:
89
app/components/BikeDetail.tsx
Normal file
89
app/components/BikeDetail.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
'use client'
|
||||
|
||||
import { BikeWithParts } from '@/types'
|
||||
import { useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import WearPartList from './WearPartList'
|
||||
import WearPartForm from './WearPartForm'
|
||||
|
||||
interface BikeDetailProps {
|
||||
bike: BikeWithParts
|
||||
onUpdate: () => void
|
||||
}
|
||||
|
||||
export default function BikeDetail({ bike, onUpdate }: BikeDetailProps) {
|
||||
const [showPartForm, setShowPartForm] = useState(false)
|
||||
|
||||
return (
|
||||
<main className="min-h-screen p-8 bg-gray-50">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<Link
|
||||
href="/"
|
||||
className="inline-flex items-center text-blue-600 hover:text-blue-800 mb-6"
|
||||
>
|
||||
← Zurück zur Übersicht
|
||||
</Link>
|
||||
|
||||
<div className="bg-white rounded-lg shadow-md p-6 mb-6">
|
||||
<h1 className="text-3xl font-bold text-gray-900 mb-4">{bike.name}</h1>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
|
||||
{bike.brand && (
|
||||
<div>
|
||||
<span className="text-gray-500">Marke:</span>
|
||||
<p className="font-medium">{bike.brand}</p>
|
||||
</div>
|
||||
)}
|
||||
{bike.model && (
|
||||
<div>
|
||||
<span className="text-gray-500">Modell:</span>
|
||||
<p className="font-medium">{bike.model}</p>
|
||||
</div>
|
||||
)}
|
||||
{bike.purchaseDate && (
|
||||
<div>
|
||||
<span className="text-gray-500">Kaufdatum:</span>
|
||||
<p className="font-medium">
|
||||
{new Date(bike.purchaseDate).toLocaleDateString('de-DE')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{bike.notes && (
|
||||
<div className="mt-4">
|
||||
<span className="text-gray-500">Notizen:</span>
|
||||
<p className="mt-1">{bike.notes}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mb-6 flex justify-between items-center">
|
||||
<h2 className="text-2xl font-semibold text-gray-900">
|
||||
Verschleißteile
|
||||
</h2>
|
||||
<button
|
||||
onClick={() => setShowPartForm(!showPartForm)}
|
||||
className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
{showPartForm ? 'Abbrechen' : '+ Neues Verschleißteil'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{showPartForm && (
|
||||
<div className="mb-6">
|
||||
<WearPartForm
|
||||
bikeId={bike.id}
|
||||
onSuccess={() => {
|
||||
setShowPartForm(false)
|
||||
onUpdate()
|
||||
}}
|
||||
onCancel={() => setShowPartForm(false)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<WearPartList bikeId={bike.id} parts={bike.wearParts} onUpdate={onUpdate} />
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user