feat(frontend): Honigchargen-Ansichten (Modul 11)
Chargenliste mit Anlage-Dialog sowie Detailseite mit Honigmessungen und Rückverfolgbarkeits-Ansicht bis zu den Ursprungsvölkern.
This commit is contained in:
1 parent
cf625552d1
commit
f3d99524ff
8 files changed
+525
-1
No files matched your search
@@ -0,0 +1,110 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { FormEvent, useState } from "react";
|
||||||
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
|
import { addHoneyMeasurement } from "@/lib/honey-batches/api";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
|
||||||
|
function todayIsoDate(): string {
|
||||||
|
return new Date().toISOString().slice(0, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AddMeasurementDialog({ honeyBatchId }: { honeyBatchId: string }) {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [waterContent, setWaterContent] = useState("");
|
||||||
|
const [measuredAt, setMeasuredAt] = useState(todayIsoDate());
|
||||||
|
const [device, setDevice] = useState("");
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationFn: (input: { waterContent: number; measuredAt: string; device?: string }) =>
|
||||||
|
addHoneyMeasurement(honeyBatchId, input),
|
||||||
|
onSuccess: () => {
|
||||||
|
void queryClient.invalidateQueries({ queryKey: ["honey-batches", honeyBatchId] });
|
||||||
|
setOpen(false);
|
||||||
|
setWaterContent("");
|
||||||
|
setDevice("");
|
||||||
|
setError(null);
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
setError("Messung konnte nicht gespeichert werden.");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleSubmit(event: FormEvent<HTMLFormElement>) {
|
||||||
|
event.preventDefault();
|
||||||
|
setError(null);
|
||||||
|
if (!waterContent) {
|
||||||
|
setError("Bitte einen Wassergehalt angeben.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mutation.mutate({
|
||||||
|
waterContent: Number(waterContent),
|
||||||
|
measuredAt: new Date(measuredAt).toISOString(),
|
||||||
|
device: device || undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onOpenChange={setOpen}>
|
||||||
|
<DialogTrigger render={<Button size="sm">Neue Messung</Button>} />
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Honigmessung</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<form className="flex flex-col gap-4" onSubmit={handleSubmit}>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="measurement-water">Wassergehalt (%)</Label>
|
||||||
|
<Input
|
||||||
|
id="measurement-water"
|
||||||
|
type="number"
|
||||||
|
inputMode="decimal"
|
||||||
|
required
|
||||||
|
value={waterContent}
|
||||||
|
onChange={(event) => setWaterContent(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="measurement-date">Datum</Label>
|
||||||
|
<Input
|
||||||
|
id="measurement-date"
|
||||||
|
type="date"
|
||||||
|
required
|
||||||
|
value={measuredAt}
|
||||||
|
onChange={(event) => setMeasuredAt(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="measurement-device">Messgerät</Label>
|
||||||
|
<Input
|
||||||
|
id="measurement-device"
|
||||||
|
value={device}
|
||||||
|
onChange={(event) => setDevice(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{error ? (
|
||||||
|
<p role="alert" className="text-sm text-red-600">
|
||||||
|
{error}
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
|
<DialogFooter>
|
||||||
|
<Button type="submit" disabled={mutation.isPending}>
|
||||||
|
{mutation.isPending ? "Wird gespeichert…" : "Speichern"}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</form>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useParams } from "next/navigation";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { fetchHoneyBatch, fetchHoneyBatchTraceability } from "@/lib/honey-batches/api";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import { AddMeasurementDialog } from "./add-measurement-dialog";
|
||||||
|
|
||||||
|
export default function HoneyBatchDetailPage() {
|
||||||
|
const params = useParams<{ id: string }>();
|
||||||
|
const { data: honeyBatch, isLoading, isError } = useQuery({
|
||||||
|
queryKey: ["honey-batches", params.id],
|
||||||
|
queryFn: () => fetchHoneyBatch(params.id),
|
||||||
|
});
|
||||||
|
const { data: traceability } = useQuery({
|
||||||
|
queryKey: ["honey-batches", params.id, "traceability"],
|
||||||
|
queryFn: () => fetchHoneyBatchTraceability(params.id),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <p className="p-4 text-sm text-muted-foreground sm:p-6">Lädt…</p>;
|
||||||
|
}
|
||||||
|
if (isError || !honeyBatch) {
|
||||||
|
return (
|
||||||
|
<p role="alert" className="p-4 text-sm text-red-600 sm:p-6">
|
||||||
|
Honigcharge konnte nicht geladen werden.
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="flex flex-col gap-4 p-4 sm:p-6">
|
||||||
|
<Card className="max-w-lg">
|
||||||
|
<CardHeader className="flex flex-row items-center justify-between">
|
||||||
|
<CardTitle>{honeyBatch.batchNumber}</CardTitle>
|
||||||
|
<Badge>{honeyBatch.status}</Badge>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="flex flex-col gap-3 text-sm">
|
||||||
|
{honeyBatch.honeyType ? <p>Sorte: {honeyBatch.honeyType}</p> : null}
|
||||||
|
{honeyBatch.amount !== null ? <p>Menge: {honeyBatch.amount} kg</p> : null}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card className="max-w-lg">
|
||||||
|
<CardHeader className="flex flex-row items-center justify-between">
|
||||||
|
<CardTitle>Honigmessungen</CardTitle>
|
||||||
|
<AddMeasurementDialog honeyBatchId={honeyBatch.id} />
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="flex flex-col gap-3 text-sm">
|
||||||
|
{honeyBatch.measurements.length === 0 ? (
|
||||||
|
<p className="text-muted-foreground">Noch keine Messung dokumentiert.</p>
|
||||||
|
) : null}
|
||||||
|
{honeyBatch.measurements.map((measurement) => (
|
||||||
|
<div key={measurement.id} className="rounded-md border p-3">
|
||||||
|
<p className="font-medium">
|
||||||
|
{new Date(measurement.measuredAt).toLocaleDateString("de-DE")} ·{" "}
|
||||||
|
{measurement.waterContent}% Wassergehalt
|
||||||
|
</p>
|
||||||
|
{measurement.device ? <p className="text-muted-foreground">{measurement.device}</p> : null}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card className="max-w-lg">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Rückverfolgbarkeit</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="flex flex-col gap-3 text-sm">
|
||||||
|
{!traceability ? <p className="text-muted-foreground">Lädt…</p> : null}
|
||||||
|
{traceability ? (
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
Schleuderung am{" "}
|
||||||
|
{new Date(traceability.extraction.date).toLocaleDateString("de-DE")}
|
||||||
|
{traceability.extraction.room ? ` · ${traceability.extraction.room}` : ""}
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
{traceability.harvests.map((harvest) => (
|
||||||
|
<div key={harvest.id} className="rounded-md border p-3">
|
||||||
|
<p className="font-medium">
|
||||||
|
Ernte am {new Date(harvest.date).toLocaleDateString("de-DE")}
|
||||||
|
</p>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
Völker: {harvest.colonies.map((colony) => colony.code).join(", ")}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { FormEvent, useState } from "react";
|
||||||
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
|
import { fetchExtractions } from "@/lib/extractions/api";
|
||||||
|
import { createHoneyBatch } from "@/lib/honey-batches/api";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
|
|
||||||
|
export function CreateHoneyBatchDialog() {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [batchNumber, setBatchNumber] = useState("");
|
||||||
|
const [extractionId, setExtractionId] = useState("");
|
||||||
|
const [honeyType, setHoneyType] = useState("");
|
||||||
|
const [amount, setAmount] = useState("");
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const { data: extractions } = useQuery({
|
||||||
|
queryKey: ["extractions"],
|
||||||
|
queryFn: fetchExtractions,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationFn: createHoneyBatch,
|
||||||
|
onSuccess: () => {
|
||||||
|
void queryClient.invalidateQueries({ queryKey: ["honey-batches"] });
|
||||||
|
setOpen(false);
|
||||||
|
setBatchNumber("");
|
||||||
|
setExtractionId("");
|
||||||
|
setHoneyType("");
|
||||||
|
setAmount("");
|
||||||
|
setError(null);
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
setError("Honigcharge konnte nicht angelegt werden.");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleSubmit(event: FormEvent<HTMLFormElement>) {
|
||||||
|
event.preventDefault();
|
||||||
|
setError(null);
|
||||||
|
if (!extractionId) {
|
||||||
|
setError("Bitte eine Schleuderung auswählen.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mutation.mutate({
|
||||||
|
batchNumber,
|
||||||
|
extractionId,
|
||||||
|
honeyType: honeyType || undefined,
|
||||||
|
amount: amount ? Number(amount) : undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onOpenChange={setOpen}>
|
||||||
|
<DialogTrigger render={<Button>+ Neue Charge</Button>} />
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Neue Honigcharge</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<form className="flex flex-col gap-4" onSubmit={handleSubmit}>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="batch-number">Chargennummer</Label>
|
||||||
|
<Input
|
||||||
|
id="batch-number"
|
||||||
|
required
|
||||||
|
value={batchNumber}
|
||||||
|
onChange={(event) => setBatchNumber(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="batch-extraction">Schleuderung</Label>
|
||||||
|
<Select value={extractionId} onValueChange={(value) => setExtractionId(value ?? "")}>
|
||||||
|
<SelectTrigger id="batch-extraction">
|
||||||
|
<SelectValue placeholder="Schleuderung wählen" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{extractions?.map((extraction) => (
|
||||||
|
<SelectItem key={extraction.id} value={extraction.id}>
|
||||||
|
{new Date(extraction.date).toLocaleDateString("de-DE")} ·{" "}
|
||||||
|
{extraction.harvests
|
||||||
|
.flatMap((entry) => entry.harvest.colonies.map((c) => c.colony.code))
|
||||||
|
.join(", ")}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="batch-honey-type">Honigsorte</Label>
|
||||||
|
<Input
|
||||||
|
id="batch-honey-type"
|
||||||
|
value={honeyType}
|
||||||
|
onChange={(event) => setHoneyType(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="batch-amount">Menge (kg)</Label>
|
||||||
|
<Input
|
||||||
|
id="batch-amount"
|
||||||
|
type="number"
|
||||||
|
inputMode="decimal"
|
||||||
|
value={amount}
|
||||||
|
onChange={(event) => setAmount(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{error ? (
|
||||||
|
<p role="alert" className="text-sm text-red-600">
|
||||||
|
{error}
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
|
<DialogFooter>
|
||||||
|
<Button type="submit" disabled={mutation.isPending}>
|
||||||
|
{mutation.isPending ? "Wird angelegt…" : "Anlegen"}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</form>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import Link from "next/link";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { fetchHoneyBatches } from "@/lib/honey-batches/api";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import { CreateHoneyBatchDialog } from "./create-honey-batch-dialog";
|
||||||
|
|
||||||
|
export default function HoneyBatchesPage() {
|
||||||
|
const { data: honeyBatches, isLoading, isError } = useQuery({
|
||||||
|
queryKey: ["honey-batches"],
|
||||||
|
queryFn: fetchHoneyBatches,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="flex flex-col gap-4 p-4 sm:p-6">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<h1 className="text-xl font-semibold">Honigchargen</h1>
|
||||||
|
<CreateHoneyBatchDialog />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isLoading ? <p className="text-sm text-muted-foreground">Lädt…</p> : null}
|
||||||
|
{isError ? (
|
||||||
|
<p role="alert" className="text-sm text-red-600">
|
||||||
|
Honigchargen konnten nicht geladen werden.
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{honeyBatches ? (
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
<TableHead>Chargennummer</TableHead>
|
||||||
|
<TableHead>Sorte</TableHead>
|
||||||
|
<TableHead>Menge</TableHead>
|
||||||
|
<TableHead>Status</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{honeyBatches.map((honeyBatch) => (
|
||||||
|
<TableRow key={honeyBatch.id}>
|
||||||
|
<TableCell>
|
||||||
|
<Link className="underline" href={`/honey-batches/${honeyBatch.id}`}>
|
||||||
|
{honeyBatch.batchNumber}
|
||||||
|
</Link>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>{honeyBatch.honeyType ?? "—"}</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{honeyBatch.amount !== null ? `${honeyBatch.amount} kg` : "—"}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Badge>{honeyBatch.status}</Badge>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
) : null}
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -37,6 +37,12 @@ export default function AppLayout({ children }: { children: ReactNode }) {
|
|||||||
<Link className="text-sm text-muted-foreground hover:text-foreground" href="/tasks">
|
<Link className="text-sm text-muted-foreground hover:text-foreground" href="/tasks">
|
||||||
Aufgaben
|
Aufgaben
|
||||||
</Link>
|
</Link>
|
||||||
|
<Link
|
||||||
|
className="text-sm text-muted-foreground hover:text-foreground"
|
||||||
|
href="/honey-batches"
|
||||||
|
>
|
||||||
|
Honigchargen
|
||||||
|
</Link>
|
||||||
{user.role === "ADMIN" ? (
|
{user.role === "ADMIN" ? (
|
||||||
<Link
|
<Link
|
||||||
className="text-sm text-muted-foreground hover:text-foreground"
|
className="text-sm text-muted-foreground hover:text-foreground"
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { apiFetch } from "@/lib/auth/api-client";
|
||||||
|
import {
|
||||||
|
CreateHoneyBatchInput,
|
||||||
|
CreateHoneyMeasurementInput,
|
||||||
|
HoneyBatch,
|
||||||
|
Traceability,
|
||||||
|
} from "./types";
|
||||||
|
|
||||||
|
export function fetchHoneyBatches(): Promise<HoneyBatch[]> {
|
||||||
|
return apiFetch<HoneyBatch[]>("/honey-batches");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchHoneyBatch(id: string): Promise<HoneyBatch> {
|
||||||
|
return apiFetch<HoneyBatch>(`/honey-batches/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchHoneyBatchTraceability(id: string): Promise<Traceability> {
|
||||||
|
return apiFetch<Traceability>(`/honey-batches/${id}/traceability`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createHoneyBatch(input: CreateHoneyBatchInput): Promise<HoneyBatch> {
|
||||||
|
return apiFetch<HoneyBatch>("/honey-batches", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify(input),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addHoneyMeasurement(
|
||||||
|
id: string,
|
||||||
|
input: CreateHoneyMeasurementInput,
|
||||||
|
): Promise<HoneyBatch> {
|
||||||
|
return apiFetch<HoneyBatch>(`/honey-batches/${id}/measurements`, {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify(input),
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
export type HoneyBatchStatus =
|
||||||
|
| "RAW"
|
||||||
|
| "EXTRACTED"
|
||||||
|
| "CLARIFIED"
|
||||||
|
| "CREAMED"
|
||||||
|
| "STORED"
|
||||||
|
| "BOTTLED"
|
||||||
|
| "SOLD"
|
||||||
|
| "CONSUMED";
|
||||||
|
|
||||||
|
export interface HoneyMeasurement {
|
||||||
|
id: string;
|
||||||
|
waterContent: number;
|
||||||
|
measuredAt: string;
|
||||||
|
device: string | null;
|
||||||
|
temperature: number | null;
|
||||||
|
notes: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HoneyBatch {
|
||||||
|
id: string;
|
||||||
|
batchNumber: string;
|
||||||
|
extractionId: string;
|
||||||
|
honeyType: string | null;
|
||||||
|
amount: number | null;
|
||||||
|
waterContent: number | null;
|
||||||
|
storageContainerId: string | null;
|
||||||
|
status: HoneyBatchStatus;
|
||||||
|
createdAt: string;
|
||||||
|
measurements: HoneyMeasurement[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Traceability {
|
||||||
|
honeyBatch: {
|
||||||
|
id: string;
|
||||||
|
batchNumber: string;
|
||||||
|
honeyType: string | null;
|
||||||
|
amount: number | null;
|
||||||
|
status: HoneyBatchStatus;
|
||||||
|
};
|
||||||
|
extraction: {
|
||||||
|
id: string;
|
||||||
|
date: string;
|
||||||
|
room: string | null;
|
||||||
|
};
|
||||||
|
harvests: {
|
||||||
|
id: string;
|
||||||
|
date: string;
|
||||||
|
apiaryId: string;
|
||||||
|
colonies: { id: string; code: string }[];
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateHoneyBatchInput {
|
||||||
|
batchNumber: string;
|
||||||
|
extractionId: string;
|
||||||
|
honeyType?: string;
|
||||||
|
amount?: number;
|
||||||
|
waterContent?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateHoneyMeasurementInput {
|
||||||
|
waterContent: number;
|
||||||
|
measuredAt: string;
|
||||||
|
device?: string;
|
||||||
|
temperature?: number;
|
||||||
|
notes?: string;
|
||||||
|
}
|
||||||
@@ -99,7 +99,7 @@ Abgeleitet aus [`docs/lastenheft.md`](docs/lastenheft.md) Abschnitt 26. Reihenfo
|
|||||||
- Endpunkte: `GET/POST /api/v1/honey-batches`, `GET /api/v1/honey-batches/:id`, `GET /api/v1/honey-batches/:id/traceability`, `POST /api/v1/honey-batches/:id/measurements`
|
- Endpunkte: `GET/POST /api/v1/honey-batches`, `GET /api/v1/honey-batches/:id`, `GET /api/v1/honey-batches/:id/traceability`, `POST /api/v1/honey-batches/:id/measurements`
|
||||||
- Frontend: `/honey-batches`, `/honey-batches/[id]` (inkl. Rückverfolgbarkeits-Ansicht)
|
- Frontend: `/honey-batches`, `/honey-batches/[id]` (inkl. Rückverfolgbarkeits-Ansicht)
|
||||||
- [x] Backend-Modul fertig, getestet, grün, committet
|
- [x] Backend-Modul fertig, getestet, grün, committet
|
||||||
- [ ] Frontend-Views fertig, getestet, grün, committet
|
- [x] Frontend-Views fertig, getestet, grün, committet
|
||||||
|
|
||||||
### 12. Grundlegende Lagerverwaltung
|
### 12. Grundlegende Lagerverwaltung
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user