feat(frontend): Suche und Filter (Modul 14)
/search-Seite über Bienenstände, Völker, Königinnen, Aufgaben und Honigchargen. Filterleisten (Status/Standort) in den Listen-Views für Bienenstände, Völker, Königinnen und Honigchargen.
This commit is contained in:
1 parent
146b56d683
commit
166979ff3c
21 files changed
+314
-28
No files matched your search
@@ -0,0 +1,81 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { search } from "@/lib/search/api";
|
||||
import { SearchResult, SearchResultType } from "@/lib/search/types";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
|
||||
const TYPE_LABELS: Record<SearchResultType, string> = {
|
||||
Apiary: "Bienenstand",
|
||||
Colony: "Volk",
|
||||
Queen: "Königin",
|
||||
Task: "Aufgabe",
|
||||
HoneyBatch: "Honigcharge",
|
||||
};
|
||||
|
||||
function resultHref(result: SearchResult): string {
|
||||
switch (result.type) {
|
||||
case "Apiary":
|
||||
return `/apiaries/${result.id}`;
|
||||
case "Colony":
|
||||
return `/colonies/${result.id}`;
|
||||
case "Queen":
|
||||
return `/queens/${result.id}`;
|
||||
case "HoneyBatch":
|
||||
return `/honey-batches/${result.id}`;
|
||||
case "Task":
|
||||
return "/tasks";
|
||||
}
|
||||
}
|
||||
|
||||
export default function SearchPage() {
|
||||
const [query, setQuery] = useState("");
|
||||
|
||||
const { data: results, isLoading } = useQuery({
|
||||
queryKey: ["search", query],
|
||||
queryFn: () => search(query),
|
||||
enabled: query.trim().length > 0,
|
||||
});
|
||||
|
||||
return (
|
||||
<main className="flex flex-col gap-4 p-4 sm:p-6">
|
||||
<h1 className="text-xl font-semibold">Suche</h1>
|
||||
<Input
|
||||
className="h-11 max-w-lg"
|
||||
placeholder="Bienenstand, Volk, Königin, Aufgabe, Charge…"
|
||||
value={query}
|
||||
onChange={(event) => setQuery(event.target.value)}
|
||||
autoFocus
|
||||
/>
|
||||
|
||||
{isLoading ? <p className="text-sm text-muted-foreground">Lädt…</p> : null}
|
||||
{query.trim().length > 0 && !isLoading && (!results || results.length === 0) ? (
|
||||
<p className="text-sm text-muted-foreground">Keine Treffer.</p>
|
||||
) : null}
|
||||
|
||||
{results && results.length > 0 ? (
|
||||
<Card className="max-w-lg">
|
||||
<CardHeader>
|
||||
<CardTitle>Ergebnisse</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-2">
|
||||
{results.map((result) => (
|
||||
<Link
|
||||
key={`${result.type}-${result.id}`}
|
||||
href={resultHref(result)}
|
||||
className="flex items-center justify-between rounded-md border p-3 text-sm hover:bg-muted"
|
||||
>
|
||||
<span>{result.label}</span>
|
||||
<Badge variant="secondary">{TYPE_LABELS[result.type]}</Badge>
|
||||
</Link>
|
||||
))}
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : null}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user