Initial commit
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Link, useNavigate, useParams } from 'react-router-dom'
|
||||
import apiClient from '../api/client'
|
||||
import ProductFoundModal from '../components/ProductFoundModal'
|
||||
import ConfirmDialog from '../components/ConfirmDialog'
|
||||
import DishShareManager from '../components/DishShareManager'
|
||||
import ThemeToggle from '../components/ThemeToggle'
|
||||
|
||||
export default function DishDetailPage() {
|
||||
const { id } = useParams()
|
||||
const navigate = useNavigate()
|
||||
const [dish, setDish] = useState(null)
|
||||
const [isLogging, setIsLogging] = useState(false)
|
||||
const [isDeleting, setIsDeleting] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
apiClient.get(`/api/dishes/${id}`).then((res) => setDish(res.data))
|
||||
}, [id])
|
||||
|
||||
async function handleConfirmDelete() {
|
||||
await apiClient.delete(`/api/dishes/${id}`)
|
||||
navigate('/cookbook')
|
||||
}
|
||||
|
||||
if (!dish) {
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center bg-gray-50 text-gray-500 dark:bg-gray-900 dark:text-gray-400">
|
||||
Lädt…
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 pb-28 dark:bg-gray-900">
|
||||
<header className="flex items-center justify-between bg-white px-4 py-4 shadow-sm dark:bg-gray-800">
|
||||
<h1 className="text-base font-semibold text-gray-900 dark:text-gray-50">{dish.name}</h1>
|
||||
<div className="flex items-center gap-4">
|
||||
<Link to="/cookbook" className="text-sm font-medium text-emerald-600 dark:text-emerald-400">
|
||||
Zurück
|
||||
</Link>
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="mx-auto max-w-md space-y-4 p-4">
|
||||
<section className="rounded-xl bg-white p-4 shadow-sm dark:bg-gray-800">
|
||||
<p className="mb-3 text-sm text-gray-500 dark:text-gray-400">
|
||||
Ergibt insgesamt {Math.round(dish.total_weight_g)}g · Nährwerte pro 100g
|
||||
</p>
|
||||
<dl className="mb-4 grid grid-cols-4 gap-2 text-center text-sm">
|
||||
<div>
|
||||
<dt className="text-gray-500 dark:text-gray-400">Kcal</dt>
|
||||
<dd className="font-medium text-gray-900 dark:text-gray-50">{Math.round(dish.calories)}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt className="text-gray-500 dark:text-gray-400">Carbs</dt>
|
||||
<dd className="font-medium text-gray-900 dark:text-gray-50">{Math.round(dish.carbs)}g</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt className="text-gray-500 dark:text-gray-400">Protein</dt>
|
||||
<dd className="font-medium text-gray-900 dark:text-gray-50">{Math.round(dish.protein)}g</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt className="text-gray-500 dark:text-gray-400">Fett</dt>
|
||||
<dd className="font-medium text-gray-900 dark:text-gray-50">{Math.round(dish.fat)}g</dd>
|
||||
</div>
|
||||
</dl>
|
||||
|
||||
<h2 className="mb-2 text-sm font-semibold text-gray-900 dark:text-gray-50">Zutaten</h2>
|
||||
<ul className="mb-4 divide-y divide-gray-100 dark:divide-gray-700">
|
||||
{dish.ingredients.map((ing) => (
|
||||
<li key={ing.barcode} className="flex items-center justify-between py-1.5 text-sm">
|
||||
<span className="text-gray-700 dark:text-gray-300">{ing.name}</span>
|
||||
<span className="text-gray-500 dark:text-gray-400">{Math.round(ing.amount_g)}g</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
{dish.instructions && (
|
||||
<>
|
||||
<h2 className="mb-2 text-sm font-semibold text-gray-900 dark:text-gray-50">Zubereitung</h2>
|
||||
<p className="whitespace-pre-wrap text-sm text-gray-700 dark:text-gray-300">
|
||||
{dish.instructions}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
|
||||
{dish.is_owner ? (
|
||||
<>
|
||||
<div className="flex gap-3">
|
||||
<Link
|
||||
to={`/cookbook/${id}/edit`}
|
||||
className="flex-1 rounded-lg border border-gray-300 py-2.5 text-center text-sm font-medium text-gray-700 dark:border-gray-600 dark:text-gray-300"
|
||||
>
|
||||
Bearbeiten
|
||||
</Link>
|
||||
<button
|
||||
onClick={() => setIsDeleting(true)}
|
||||
className="flex-1 rounded-lg border border-red-200 py-2.5 text-sm font-medium text-red-600 dark:border-red-900 dark:text-red-400"
|
||||
>
|
||||
Löschen
|
||||
</button>
|
||||
</div>
|
||||
<DishShareManager dishId={id} />
|
||||
</>
|
||||
) : (
|
||||
<p className="text-center text-sm text-gray-500 dark:text-gray-400">
|
||||
Geteilt von {dish.owner_username}
|
||||
</p>
|
||||
)}
|
||||
</main>
|
||||
|
||||
<button
|
||||
onClick={() => setIsLogging(true)}
|
||||
className="fixed bottom-6 left-1/2 -translate-x-1/2 rounded-full bg-emerald-600 px-6 py-3 text-base font-medium text-white shadow-lg active:bg-emerald-700"
|
||||
>
|
||||
Essen loggen
|
||||
</button>
|
||||
|
||||
{isLogging && (
|
||||
<ProductFoundModal
|
||||
product={dish}
|
||||
onLogged={() => navigate('/dashboard')}
|
||||
onClose={() => setIsLogging(false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isDeleting && (
|
||||
<ConfirmDialog
|
||||
title="Gericht löschen?"
|
||||
message={`"${dish.name}" wird dauerhaft aus dem Kochbuch entfernt.`}
|
||||
onConfirm={handleConfirmDelete}
|
||||
onCancel={() => setIsDeleting(false)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user