feat(frontend): PageHeader mit Icon auf allen 13 "Neu"-Formularseiten

Ersetzt die bisherige schlichte CardTitle-Überschrift durch die
gemeinsame PageHeader-Komponente (Icon-Badge im passenden Gradient,
Titel, kurze Beschreibung) auf allen Formularseiten zum Anlegen neuer
Datensätze (Bienenstände, Völker, Königinnen, Aufgaben, Produkte,
Inventar, Schleuderungen, Abfüllungen, Durchsichten, Varroamessungen,
Behandlungen, Fütterungen, Ernten).

Konsistent zum bereits überarbeiteten Look der Listen- und Detailseiten;
die Beschreibungszeile gibt zusätzlich beim Öffnen sofort Kontext, was
die Seite tut – hilfreich für Nutzer, die schnell zwischen vielen
Formularen wechseln.
This commit is contained in:
Nick Szaunig committed 2026-08-24 00:21:41 +02:00
1 parent ebfbd2ef6d
commit e889edd6c5
13 files changed
+123 -80

No files matched your search

@@ -2,18 +2,15 @@
import { FormEvent, useState } from "react";
import { useRouter } from "next/navigation";
import { MapPin } from "lucide-react";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { createApiary } from "@/lib/apiaries/api";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import {
Card,
CardContent,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { PageHeader } from "@/components/page-header";
import { Card, CardContent } from "@/components/ui/card";
export default function NewApiaryPage() {
const router = useRouter();
@@ -47,11 +44,13 @@ export default function NewApiaryPage() {
}
return (
<main className="p-4 sm:p-6">
<main className="flex flex-col gap-4 p-4 sm:p-6">
<PageHeader
icon={MapPin}
title="Neuer Bienenstand"
description="Lege einen neuen Standort für deine Völker an."
/>
<Card className="max-w-lg">
<CardHeader>
<CardTitle>Neuer Bienenstand</CardTitle>
</CardHeader>
<CardContent>
<form className="flex flex-col gap-4" onSubmit={handleSubmit}>
<div className="flex flex-col gap-2">
@@ -5,6 +5,7 @@ import { useRouter, useSearchParams } from "next/navigation";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { fetchHoneyBatches } from "@/lib/honey-batches/api";
import { fetchProducts } from "@/lib/products/api";
import { Package } from "lucide-react";
import { createBottlingBatch } from "@/lib/bottling/api";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
@@ -16,7 +17,8 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { PageHeader } from "@/components/page-header";
import { Card, CardContent } from "@/components/ui/card";
export default function NewBottlingBatchPage() {
const router = useRouter();
@@ -68,11 +70,13 @@ export default function NewBottlingBatchPage() {
}
return (
<main className="p-4 sm:p-6">
<main className="flex flex-col gap-4 p-4 sm:p-6">
<PageHeader
icon={Package}
title="Neue Abfüllung"
description="Fülle eine Honigcharge in Gläser ab."
/>
<Card className="max-w-lg">
<CardHeader>
<CardTitle>Neue Abfüllung</CardTitle>
</CardHeader>
<CardContent>
<form className="flex flex-col gap-4" onSubmit={handleSubmit}>
<div className="flex flex-col gap-2">
@@ -5,6 +5,7 @@ import { useRouter } from "next/navigation";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { createColony } from "@/lib/colonies/api";
import { ColonyType } from "@/lib/colonies/types";
import { Layers } from "lucide-react";
import { fetchApiaries } from "@/lib/apiaries/api";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
@@ -16,12 +17,8 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
Card,
CardContent,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { PageHeader } from "@/components/page-header";
import { Card, CardContent } from "@/components/ui/card";
const COLONY_TYPES: ColonyType[] = [
"PRODUCTION_COLONY",
@@ -64,11 +61,13 @@ export default function NewColonyPage() {
}
return (
<main className="p-4 sm:p-6">
<main className="flex flex-col gap-4 p-4 sm:p-6">
<PageHeader
icon={Layers}
title="Neues Volk"
description="Erfasse ein neues Volk an einem Bienenstand."
/>
<Card className="max-w-lg">
<CardHeader>
<CardTitle>Neues Volk</CardTitle>
</CardHeader>
<CardContent>
<form className="flex flex-col gap-4" onSubmit={handleSubmit}>
<div className="flex flex-col gap-2">
@@ -3,11 +3,13 @@
import { FormEvent, useState } from "react";
import { useRouter } from "next/navigation";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { Wrench } from "lucide-react";
import { createEquipment } from "@/lib/equipment/api";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { PageHeader } from "@/components/page-header";
import { Card, CardContent } from "@/components/ui/card";
export default function NewEquipmentPage() {
const router = useRouter();
@@ -47,11 +49,14 @@ export default function NewEquipmentPage() {
}
return (
<main className="p-4 sm:p-6">
<main className="flex flex-col gap-4 p-4 sm:p-6">
<PageHeader
icon={Wrench}
title="Neuer Inventargegenstand"
description="Erfasse einen neuen Gegenstand für dein Inventar."
gradient="var(--gradient-sky)"
/>
<Card className="max-w-lg">
<CardHeader>
<CardTitle>Neuer Inventargegenstand</CardTitle>
</CardHeader>
<CardContent>
<form className="flex flex-col gap-4" onSubmit={handleSubmit}>
<div className="flex flex-col gap-2">
@@ -3,13 +3,15 @@
import { FormEvent, useState } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { Droplet } from "lucide-react";
import { fetchHarvests } from "@/lib/harvests/api";
import { createExtraction } from "@/lib/extractions/api";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { PageHeader } from "@/components/page-header";
import { Card, CardContent } from "@/components/ui/card";
function todayIsoDate(): string {
return new Date().toISOString().slice(0, 10);
@@ -68,11 +70,13 @@ export default function NewExtractionPage() {
}
return (
<main className="p-4 sm:p-6">
<main className="flex flex-col gap-4 p-4 sm:p-6">
<PageHeader
icon={Droplet}
title="Neue Schleuderung"
description="Erfasse eine neue Schleuderung aus einer oder mehreren Ernten."
/>
<Card className="max-w-lg">
<CardHeader>
<CardTitle>Neue Schleuderung</CardTitle>
</CardHeader>
<CardContent>
<form className="flex flex-col gap-5" onSubmit={handleSubmit}>
<div className="flex flex-col gap-2">
@@ -4,6 +4,7 @@ import { FormEvent, useState } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { toast } from "sonner";
import { Utensils } from "lucide-react";
import { fetchColonies } from "@/lib/colonies/api";
import { createFeeding } from "@/lib/feedings/api";
import { FeedType } from "@/lib/feedings/types";
@@ -18,7 +19,8 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { PageHeader } from "@/components/page-header";
import { Card, CardContent } from "@/components/ui/card";
const FEED_TYPE_OPTIONS: FeedType[] = [
"SUGAR_WATER",
@@ -92,11 +94,13 @@ export default function NewFeedingPage() {
}
return (
<main className="p-4 sm:p-6">
<main className="flex flex-col gap-4 p-4 sm:p-6">
<PageHeader
icon={Utensils}
title="Neue Fütterung"
description="Dokumentiere eine Fütterung für ein Volk."
/>
<Card className="max-w-lg">
<CardHeader>
<CardTitle>Neue Fütterung</CardTitle>
</CardHeader>
<CardContent>
<form className="flex flex-col gap-5" onSubmit={handleSubmit}>
<div className="flex flex-col gap-2">
@@ -4,6 +4,7 @@ import { FormEvent, useState } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { toast } from "sonner";
import { Boxes } from "lucide-react";
import { fetchApiaries } from "@/lib/apiaries/api";
import { fetchColonies } from "@/lib/colonies/api";
import { createHarvest } from "@/lib/harvests/api";
@@ -18,7 +19,8 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { PageHeader } from "@/components/page-header";
import { Card, CardContent } from "@/components/ui/card";
function todayIsoDate(): string {
return new Date().toISOString().slice(0, 10);
@@ -84,11 +86,13 @@ export default function NewHarvestPage() {
}
return (
<main className="p-4 sm:p-6">
<main className="flex flex-col gap-4 p-4 sm:p-6">
<PageHeader
icon={Boxes}
title="Neue Ernte"
description="Erfasse eine neue Ernte für einen Bienenstand."
/>
<Card className="max-w-lg">
<CardHeader>
<CardTitle>Neue Ernte</CardTitle>
</CardHeader>
<CardContent>
<form className="flex flex-col gap-5" onSubmit={handleSubmit}>
<div className="flex flex-col gap-2">
@@ -4,6 +4,7 @@ import { FormEvent, useState } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { toast } from "sonner";
import { ClipboardCheck } from "lucide-react";
import { fetchColonies } from "@/lib/colonies/api";
import { createInspection } from "@/lib/inspections/api";
import { Button } from "@/components/ui/button";
@@ -17,7 +18,8 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { PageHeader } from "@/components/page-header";
import { Card, CardContent } from "@/components/ui/card";
function todayIsoDate(): string {
return new Date().toISOString().slice(0, 10);
@@ -82,11 +84,14 @@ export default function NewInspectionPage() {
}
return (
<main className="p-4 sm:p-6">
<main className="flex flex-col gap-4 p-4 sm:p-6">
<PageHeader
icon={ClipboardCheck}
title="Neue Durchsicht"
description="Dokumentiere den Zustand eines Volkes."
gradient="var(--gradient-sage)"
/>
<Card className="max-w-lg">
<CardHeader>
<CardTitle>Neue Durchsicht</CardTitle>
</CardHeader>
<CardContent>
<form className="flex flex-col gap-5" onSubmit={handleSubmit}>
<div className="flex flex-col gap-2">
@@ -3,12 +3,14 @@
import { FormEvent, useState } from "react";
import { useRouter } from "next/navigation";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { ShoppingBag } from "lucide-react";
import { createProduct } from "@/lib/products/api";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { PageHeader } from "@/components/page-header";
import { Card, CardContent } from "@/components/ui/card";
export default function NewProductPage() {
const router = useRouter();
@@ -46,11 +48,13 @@ export default function NewProductPage() {
}
return (
<main className="p-4 sm:p-6">
<main className="flex flex-col gap-4 p-4 sm:p-6">
<PageHeader
icon={ShoppingBag}
title="Neues Produkt"
description="Lege ein neues verkaufsfertiges Produkt an."
/>
<Card className="max-w-lg">
<CardHeader>
<CardTitle>Neues Produkt</CardTitle>
</CardHeader>
<CardContent>
<form className="flex flex-col gap-4" onSubmit={handleSubmit}>
<div className="flex flex-col gap-2">
@@ -3,16 +3,13 @@
import { FormEvent, useState } from "react";
import { useRouter } from "next/navigation";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { Crown } from "lucide-react";
import { createQueen } from "@/lib/queens/api";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Card,
CardContent,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { PageHeader } from "@/components/page-header";
import { Card, CardContent } from "@/components/ui/card";
export default function NewQueenPage() {
const router = useRouter();
@@ -40,11 +37,14 @@ export default function NewQueenPage() {
}
return (
<main className="p-4 sm:p-6">
<main className="flex flex-col gap-4 p-4 sm:p-6">
<PageHeader
icon={Crown}
title="Neue Königin"
description="Erfasse eine neue Königin für deine Zuchtlinie."
gradient="var(--gradient-sage)"
/>
<Card className="max-w-lg">
<CardHeader>
<CardTitle>Neue Königin</CardTitle>
</CardHeader>
<CardContent>
<form className="flex flex-col gap-4" onSubmit={handleSubmit}>
<div className="flex flex-col gap-2">
@@ -4,6 +4,7 @@ import { FormEvent, useState } from "react";
import { useRouter } from "next/navigation";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { toast } from "sonner";
import { ListTodo } from "lucide-react";
import { createTask } from "@/lib/tasks/api";
import { TaskPriority } from "@/lib/tasks/types";
import { Button } from "@/components/ui/button";
@@ -17,7 +18,8 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { PageHeader } from "@/components/page-header";
import { Card, CardContent } from "@/components/ui/card";
const PRIORITY_OPTIONS: TaskPriority[] = ["LOW", "NORMAL", "HIGH", "URGENT"];
@@ -54,11 +56,14 @@ export default function NewTaskPage() {
}
return (
<main className="p-4 sm:p-6">
<main className="flex flex-col gap-4 p-4 sm:p-6">
<PageHeader
icon={ListTodo}
title="Neue Aufgabe"
description="Lege eine neue Aufgabe an."
gradient="var(--gradient-sky)"
/>
<Card className="max-w-lg">
<CardHeader>
<CardTitle>Neue Aufgabe</CardTitle>
</CardHeader>
<CardContent>
<form className="flex flex-col gap-4" onSubmit={handleSubmit}>
<div className="flex flex-col gap-2">
@@ -4,6 +4,7 @@ import { FormEvent, useState } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { toast } from "sonner";
import { Syringe } from "lucide-react";
import { fetchColonies } from "@/lib/colonies/api";
import { createTreatment } from "@/lib/treatments/api";
import { Button } from "@/components/ui/button";
@@ -17,7 +18,8 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { PageHeader } from "@/components/page-header";
import { Card, CardContent } from "@/components/ui/card";
function todayIsoDate(): string {
return new Date().toISOString().slice(0, 10);
@@ -76,11 +78,14 @@ export default function NewTreatmentPage() {
}
return (
<main className="p-4 sm:p-6">
<main className="flex flex-col gap-4 p-4 sm:p-6">
<PageHeader
icon={Syringe}
title="Neue Behandlung"
description="Dokumentiere eine Behandlung für ein Volk."
gradient="var(--gradient-sky)"
/>
<Card className="max-w-lg">
<CardHeader>
<CardTitle>Neue Behandlung</CardTitle>
</CardHeader>
<CardContent>
<form className="flex flex-col gap-5" onSubmit={handleSubmit}>
<div className="flex flex-col gap-2">
@@ -4,6 +4,7 @@ import { FormEvent, useState } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { toast } from "sonner";
import { Bug } from "lucide-react";
import { fetchColonies } from "@/lib/colonies/api";
import { createVarroaMeasurement } from "@/lib/varroa/api";
import { VarroaMethod } from "@/lib/varroa/types";
@@ -18,7 +19,8 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { PageHeader } from "@/components/page-header";
import { Card, CardContent } from "@/components/ui/card";
const METHOD_OPTIONS: VarroaMethod[] = [
"NATURAL_MITE_FALL",
@@ -84,11 +86,14 @@ export default function NewVarroaMeasurementPage() {
}
return (
<main className="p-4 sm:p-6">
<main className="flex flex-col gap-4 p-4 sm:p-6">
<PageHeader
icon={Bug}
title="Neue Varroamessung"
description="Erfasse eine neue Varroamessung für ein Volk."
gradient="var(--gradient-sky)"
/>
<Card className="max-w-lg">
<CardHeader>
<CardTitle>Neue Varroamessung</CardTitle>
</CardHeader>
<CardContent>
<form className="flex flex-col gap-5" onSubmit={handleSubmit}>
<div className="flex flex-col gap-2">