Initial commit - Meal Tracker app
This commit is contained in:
@@ -1,73 +1,97 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useParams, useNavigate, Link } from 'react-router-dom'
|
||||
import { getEntries, deleteEntry } from '../utils/api'
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams, useSearchParams, Link } from 'react-router-dom';
|
||||
import Layout from '../components/Layout';
|
||||
|
||||
export default function EntryDetail() {
|
||||
const { id } = useParams()
|
||||
const navigate = useNavigate()
|
||||
const [entry, setEntry] = useState(null)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [deleting, setDeleting] = useState(false)
|
||||
const { id } = useParams();
|
||||
const [searchParams] = useSearchParams();
|
||||
const returnDate = searchParams.get('date') || '';
|
||||
const [entry, setEntry] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [darkMode, setDarkMode] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
loadEntry()
|
||||
}, [id])
|
||||
setDarkMode(document.documentElement.classList.contains('dark'));
|
||||
}, []);
|
||||
|
||||
const loadEntry = async () => {
|
||||
try {
|
||||
const entries = await getEntries()
|
||||
const found = entries.find(e => (e._id || e.id) === id)
|
||||
setEntry(found)
|
||||
} catch (err) {
|
||||
console.error('Failed to load entry', err)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
useEffect(() => {
|
||||
async function load() {
|
||||
try {
|
||||
const res = await fetch('/api/entries');
|
||||
const entries = await res.json();
|
||||
const found = entries.find(e => String(e.id) === String(id));
|
||||
setEntry(found);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
load();
|
||||
}, [id]);
|
||||
|
||||
async function handleDelete() {
|
||||
if (!confirm('Delete this entry?')) return;
|
||||
await fetch('/api/entries/' + id, { method: 'DELETE' });
|
||||
window.location.href = returnDate ? '/?date=' + returnDate : '/';
|
||||
}
|
||||
|
||||
const handleDelete = async () => {
|
||||
if (!confirm('Delete this entry?')) return
|
||||
setDeleting(true)
|
||||
try {
|
||||
await deleteEntry(id)
|
||||
navigate('/')
|
||||
} catch (err) {
|
||||
console.error('Failed to delete', err)
|
||||
alert('Failed to delete')
|
||||
setDeleting(false)
|
||||
}
|
||||
}
|
||||
const bgCard = darkMode ? 'bg-gray-800' : 'bg-white';
|
||||
const textMain = darkMode ? 'text-white' : 'text-gray-900';
|
||||
const textMuted = darkMode ? 'text-gray-400' : 'text-gray-500';
|
||||
|
||||
if (loading) return <div className="text-center py-8 text-gray-500">Loading...</div>
|
||||
if (!entry) return <div className="text-center py-8 text-gray-500">Entry not found</div>
|
||||
if (loading) return <Layout><div className="p-4">Loading...</div></Layout>;
|
||||
if (!entry) return <Layout><div className={"p-4 " + textMain}>Entry not found</div></Layout>;
|
||||
|
||||
const returnUrl = returnDate ? '/?date=' + returnDate : '/';
|
||||
|
||||
return (
|
||||
<div>
|
||||
{entry.image && <img src={entry.image} alt={entry.name} className="w-full h-64 object-cover rounded-lg mb-4" />}
|
||||
<Layout>
|
||||
<Link to={returnUrl} className="text-emerald-500 mb-4 block">← Back</Link>
|
||||
|
||||
<div className="bg-white rounded-lg p-4 shadow-sm mb-4">
|
||||
<h1 className="text-xl font-semibold text-gray-800 mb-2">{entry.name}</h1>
|
||||
{entry.description && <p className="text-gray-600 mb-4">{entry.description}</p>}
|
||||
|
||||
<div className="grid grid-cols-4 gap-2 text-center py-3 border-t">
|
||||
<div><div className="text-lg font-semibold">{entry.calories || 0}</div><div className="text-xs text-gray-500">cal</div></div>
|
||||
<div><div className="text-lg font-semibold">{entry.protein || 0}g</div><div className="text-xs text-gray-500">protein</div></div>
|
||||
<div><div className="text-lg font-semibold">{entry.carbs || 0}g</div><div className="text-xs text-gray-500">carbs</div></div>
|
||||
<div><div className="text-lg font-semibold">{entry.fat || 0}g</div><div className="text-xs text-gray-500">fat</div></div>
|
||||
<h1 className={"text-2xl font-bold mb-2 " + textMain}>{entry.name}</h1>
|
||||
{entry.description && <p className={textMuted + " mb-4"}>{entry.description}</p>}
|
||||
|
||||
{entry.image_url && (
|
||||
<img
|
||||
src={'http://10.10.10.143:3000' + entry.image_url}
|
||||
alt=""
|
||||
className="w-full rounded-xl mb-4"
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className={bgCard + " rounded-xl shadow-md p-4 mb-4"}>
|
||||
<h2 className={"font-semibold mb-2 " + textMain}>Macros</h2>
|
||||
<div className="grid grid-cols-4 gap-2 text-center">
|
||||
<div>
|
||||
<div className={"text-xs " + textMuted}>Cal</div>
|
||||
<div className={"font-bold text-lg " + textMain}>{entry.calories || 0}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className={"text-xs " + textMuted}>Protein</div>
|
||||
<div className={"font-bold text-lg " + textMain}>{entry.protein || 0}g</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className={"text-xs " + textMuted}>Carbs</div>
|
||||
<div className={"font-bold text-lg " + textMain}>{entry.carbs || 0}g</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className={"text-xs " + textMuted}>Fat</div>
|
||||
<div className={"font-bold text-lg " + textMain}>{entry.fat || 0}g</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{entry.notes && (
|
||||
<div className="bg-white rounded-lg p-4 shadow-sm mb-4">
|
||||
<h2 className="text-sm font-medium text-gray-500 mb-2">Notes</h2>
|
||||
<p className="text-gray-700">{entry.notes}</p>
|
||||
<div className={bgCard + " rounded-xl shadow-md p-4 mb-4"}>
|
||||
<h2 className={"font-semibold mb-1 " + textMain}>Notes</h2>
|
||||
<p className={textMuted}>{entry.notes}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex gap-3">
|
||||
<Link to="/" className="flex-1 py-3 bg-gray-200 text-center rounded-lg font-medium hover:bg-gray-300">Back</Link>
|
||||
<button onClick={handleDelete} disabled={deleting} className="flex-1 py-3 bg-red-600 text-white rounded-lg font-medium hover:bg-red-700 disabled:opacity-50">{deleting ? 'Deleting...' : 'Delete'}</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
<button onClick={handleDelete} className="w-full bg-red-500 hover:bg-red-600 text-white py-3 rounded-xl font-semibold">
|
||||
Delete Entry
|
||||
</button>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user