feat: add TagsPage component and integrate it into the main application

This commit is contained in:
Felix Zett 2025-08-31 22:37:09 +02:00
parent 1cfaf70803
commit 067d8f2a6a
2 changed files with 160 additions and 2 deletions

View file

@ -4,6 +4,7 @@ import { getSeed, getTrips, getNextTripId } from "./api";
import ItemsPage from "./pages/ItemsPage";
import TripChecklist from "./pages/TripChecklist";
import TripsPage from "./pages/TripsPage";
import TagsPage from "./pages/TagsPage";
function NextTripRedirect({ trips }: { trips: any[] }) {
const [nextTripId, setNextTripId] = React.useState<string | null>(null);
@ -55,12 +56,17 @@ export default function App() {
</button>
<Link to="/trips">
<button className="bg-blue-500 text-white px-4 py-2 rounded">
Alle Trips
Trips
</button>
</Link>
<Link to="/items">
<button className="bg-green-500 text-white px-4 py-2 rounded">
Alle Items
Items
</button>
</Link>
<Link to="/tags">
<button className="bg-purple-500 text-white px-4 py-2 rounded">
Tags
</button>
</Link>
<button
@ -78,6 +84,7 @@ export default function App() {
<Route path="/trips" element={<TripsPage />} />
<Route path="/trips/:id" element={<TripChecklist trips={trips} />} />
<Route path="/items" element={<ItemsPage />} />
<Route path="/tags" element={<TagsPage />} />
<Route path="/" element={<NextTripRedirect trips={trips} />} />
</Routes>
</div>

View file

@ -0,0 +1,151 @@
import React, { useEffect, useState } from "react";
import { getTags } from "../api";
import type { Tag } from "../api";
const API_BASE = "http://localhost:8000";
export default function TagsPage() {
const [tags, setTags] = useState<Tag[]>([]);
const [editingId, setEditingId] = useState<string | null>(null);
const [editName, setEditName] = useState("");
const [editMandatory, setEditMandatory] = useState(false);
const [newName, setNewName] = useState("");
const [newMandatory, setNewMandatory] = useState(false);
async function loadTags() {
setTags(await getTags());
}
useEffect(() => {
loadTags();
}, []);
async function handleSave(id: string) {
await fetch(`${API_BASE}/tags/${id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: editName, mandatory: editMandatory }),
});
setEditingId(null);
await loadTags();
}
async function handleDelete(id: string) {
if (!window.confirm("Tag wirklich löschen?")) return;
await fetch(`${API_BASE}/tags/${id}`, { method: "DELETE" });
await loadTags();
}
async function handleCreate(e: React.FormEvent) {
e.preventDefault();
await fetch(`${API_BASE}/tags/`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: newName, mandatory: newMandatory }),
});
setNewName("");
setNewMandatory(false);
await loadTags();
}
return (
<div className="p-4 max-w-xl mx-auto">
<h1 className="text-2xl font-bold mb-4">Alle Tags</h1>
<form className="flex gap-2 mb-6" onSubmit={handleCreate}>
<input
className="border rounded px-2 py-1 flex-1"
placeholder="Neuer Tag..."
value={newName}
onChange={e => setNewName(e.target.value)}
required
/>
<label className="flex items-center gap-1 text-sm">
<input
type="checkbox"
checked={newMandatory}
onChange={e => setNewMandatory(e.target.checked)}
/>
mandatory
</label>
<button
className="bg-green-500 text-white px-3 py-1 rounded"
type="submit"
>
Hinzufügen
</button>
</form>
<ul>
{tags.map(tag =>
editingId === tag.id ? (
<li key={tag.id} className="flex gap-2 items-center mb-2">
<input
className="border rounded px-2 py-1 flex-1"
value={editName}
onChange={e => setEditName(e.target.value)}
/>
<label className="flex items-center gap-1 text-sm">
<input
type="checkbox"
checked={editMandatory}
onChange={e => setEditMandatory(e.target.checked)}
/>
mandatory
</label>
<button
className="bg-blue-500 text-white px-2 py-1 rounded"
onClick={() => handleSave(tag.id)}
type="button"
>
Speichern
</button>
<button
className="bg-gray-300 px-2 py-1 rounded"
onClick={() => setEditingId(null)}
type="button"
>
Abbrechen
</button>
</li>
) : (
<li
key={tag.id}
className="flex gap-2 items-center mb-2 group"
>
<span
className={
"px-2 py-0.5 rounded text-sm " +
(tag.mandatory
? "border-2 border-yellow-400 bg-yellow-50 font-bold"
: "bg-gray-100")
}
>
#{tag.name}
{tag.mandatory && (
<span className="ml-1 text-yellow-500 font-bold">*</span>
)}
</span>
<button
className="text-xs text-blue-500 underline"
onClick={() => {
setEditingId(tag.id);
setEditName(tag.name);
setEditMandatory(tag.mandatory);
}}
type="button"
>
Bearbeiten
</button>
<button
className="text-xs text-red-500 underline opacity-0 group-hover:opacity-100"
onClick={() => handleDelete(tag.id)}
type="button"
>
Löschen
</button>
</li>
)
)}
</ul>
</div>
);
}