feat: enhance TripsPage to include tag selection and marking for new trips

This commit is contained in:
Felix Zett 2025-08-31 19:59:45 +02:00
parent c966009ab5
commit 7e1bb2f77b

View file

@ -1,17 +1,26 @@
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { getSeed, getTrips, deleteTrip, createTrip } from "../api";
import { getSeed, getTrips, deleteTrip, createTrip, getTags } from "../api";
export default function TripsPage() {
const [trips, setTrips] = useState<any[]>([]);
const [allTags, setAllTags] = useState<any[]>([]);
const [newTrip, setNewTrip] = useState({ name: "", start_date: "", end_date: "" });
const [selectedTags, setSelectedTags] = useState<string[]>([]);
const [markedTags, setMarkedTags] = useState<string[]>([]);
const [creating, setCreating] = useState(false);
const [showForm, setShowForm] = useState(false);
async function loadTrips() {
const data = await getTrips();
setTrips(data);
}
useEffect(() => {
loadTrips();
getTags().then(setAllTags);
}, []);
async function handleDeleteTrip(tripId: string) {
if (window.confirm("Diesen Trip wirklich löschen?")) {
await deleteTrip(tripId);
@ -27,20 +36,19 @@ export default function TripsPage() {
name: newTrip.name,
start_date: newTrip.start_date,
end_date: newTrip.end_date,
selected_tag_ids: [],
marked_tag_ids: [],
selected_tag_ids: selectedTags,
marked_tag_ids: markedTags,
});
setNewTrip({ name: "", start_date: "", end_date: "" });
setSelectedTags([]);
setMarkedTags([]);
setShowForm(false);
await loadTrips();
} finally {
setCreating(false);
}
}
useEffect(() => {
loadTrips();
}, []);
// Markiere nächsten anstehenden Trip
const today = new Date().toISOString().slice(0, 10);
const nextTripIdx = trips.findIndex(
@ -48,40 +56,104 @@ export default function TripsPage() {
);
const nextTripId = nextTripIdx !== -1 ? trips[nextTripIdx].id : null;
function toggleTag(tagId: string) {
setSelectedTags((prev) =>
prev.includes(tagId) ? prev.filter((id) => id !== tagId) : [...prev, tagId]
);
// Wenn ein Tag abgewählt wird, auch aus markedTags entfernen
setMarkedTags((prev) => prev.filter((id) => id !== tagId));
}
function toggleMarkTag(tagId: string) {
if (!selectedTags.includes(tagId)) return;
setMarkedTags((prev) =>
prev.includes(tagId) ? prev.filter((id) => id !== tagId) : [...prev, tagId]
);
}
return (
<div>
<h1 className="text-2xl font-bold mb-4">Packlist</h1>
<form className="mb-4 flex gap-2 flex-wrap items-end" onSubmit={handleCreateTrip}>
<input
type="text"
placeholder="Trip-Name"
value={newTrip.name}
onChange={e => setNewTrip(t => ({ ...t, name: e.target.value }))}
className="border rounded px-2 py-1"
required
/>
<input
type="date"
value={newTrip.start_date}
onChange={e => setNewTrip(t => ({ ...t, start_date: e.target.value }))}
className="border rounded px-2 py-1"
required
/>
<input
type="date"
value={newTrip.end_date}
onChange={e => setNewTrip(t => ({ ...t, end_date: e.target.value }))}
className="border rounded px-2 py-1"
required
/>
<button
type="submit"
className="bg-green-500 text-white px-4 py-2 rounded"
disabled={creating}
>
{creating ? "Anlegen..." : "Neuen Trip anlegen"}
</button>
</form>
<button
className="mb-4 bg-blue-500 text-white px-4 py-2 rounded"
onClick={() => setShowForm((v) => !v)}
>
{showForm ? "Abbrechen" : "Neuen Trip anlegen"}
</button>
{showForm && (
<form className="mb-4 flex flex-col gap-2 p-4 border rounded bg-gray-50" onSubmit={handleCreateTrip}>
<input
type="text"
placeholder="Trip-Name"
value={newTrip.name}
onChange={e => setNewTrip(t => ({ ...t, name: e.target.value }))}
className="border rounded px-2 py-1"
required
/>
<div className="flex gap-2">
<input
type="date"
value={newTrip.start_date}
onChange={e => setNewTrip(t => ({ ...t, start_date: e.target.value }))}
className="border rounded px-2 py-1"
required
/>
<input
type="date"
value={newTrip.end_date}
onChange={e => setNewTrip(t => ({ ...t, end_date: e.target.value }))}
className="border rounded px-2 py-1"
required
/>
</div>
<div>
<div className="mb-1 font-semibold">Tags auswählen:</div>
<div className="flex flex-wrap gap-2">
{allTags.map((tag: any) => {
const isSelected = selectedTags.includes(tag.id);
const isMarked = markedTags.includes(tag.id);
return (
<span
key={tag.id}
className={
"px-2 py-0.5 rounded cursor-pointer select-none transition " +
(isSelected
? isMarked
? "bg-yellow-200 text-yellow-900 font-bold border border-yellow-400"
: "bg-blue-100 text-blue-800 border border-blue-300"
: "bg-gray-100 text-gray-500 border border-gray-200")
}
onClick={() => toggleTag(tag.id)}
onDoubleClick={() => toggleMarkTag(tag.id)}
title={
isSelected
? isMarked
? "Doppelklick: Markierung entfernen"
: "Doppelklick: Tag markieren"
: "Klick: Tag auswählen"
}
>
#{tag.name}
{isMarked && isSelected && (
<span className="ml-1 text-xs"></span>
)}
</span>
);
})}
</div>
<div className="text-xs text-gray-500 mt-1">
Klick: auswählen/abwählen, Doppelklick: markieren
</div>
</div>
<button
type="submit"
className="bg-green-500 text-white px-4 py-2 rounded mt-2"
disabled={creating}
>
{creating ? "Anlegen..." : "Trip anlegen"}
</button>
</form>
)}
{trips.map(trip => {
const isPast = trip.start_date < today;