feat: T4 wave 5 - colony end cleanup, withdrawal chain, next steps, inspection takeover UI
- Colony end releases the queen in the same transaction; open suggestions become obsolete and a CLEANUP suggestion lists open tasks and the queen; apply-cleanup validates evidence and blocks reactivated colonies - Withdrawal chain: plausibility warnings on harvest and bottling, conflict notifications also when a treatment is entered or corrected after the harvest (naming bottled lots), withdrawal spans in the calendar and M365 sync, Berlin-day end events - Next 3 steps per colony and the "Heute" endpoint with ranking and an explicit merge table by intent - Inspection-triggered rule suggestions follow the colony's latest inspection (resolve when the finding is gone, refresh when it stays) - Frontend: colony card takeover in the inspection form, feed status as condition, occupied frames 0-40; legacy offline entries keep syncing - Notifications and preferences are deleted with their user Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
02eb16924e
commit
ef33e5549b
122 files changed
+10707
-504
No files matched your search
@@ -0,0 +1,108 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { ColonyTakeoverSection } from "./colony-takeover-section";
|
||||
import { initialInspectionValues } from "@/app/(app)/inspections/new/inspection-form-values";
|
||||
|
||||
const colony = { occupiedFrames: 6, broodStatus: "AVERAGE", feedStatus: "GOOD" } as const;
|
||||
|
||||
function values(overrides = {}) {
|
||||
return { ...initialInspectionValues("colony-1"), ...overrides };
|
||||
}
|
||||
|
||||
function renderSection(props: Partial<React.ComponentProps<typeof ColonyTakeoverSection>> = {}) {
|
||||
const onChange = vi.fn();
|
||||
render(
|
||||
<ColonyTakeoverSection
|
||||
colony={colony}
|
||||
values={values({ colonyStrength: "9", broodPattern: 4, feedStatus: "WEAK" })}
|
||||
selected={["occupiedFrames", "broodStatus", "feedStatus"]}
|
||||
onChange={onChange}
|
||||
canUpdateColony
|
||||
{...props}
|
||||
/>,
|
||||
);
|
||||
return onChange;
|
||||
}
|
||||
|
||||
describe("ColonyTakeoverSection", () => {
|
||||
it("shows each filled field as „bisher → neu“ with a checked box", () => {
|
||||
renderSection();
|
||||
|
||||
expect(screen.getByRole("checkbox", { name: "Besetzte Waben: 6 → 9" })).toBeChecked();
|
||||
expect(screen.getByRole("checkbox", { name: "Brut: Mittel → Gut" })).toBeChecked();
|
||||
expect(screen.getByRole("checkbox", { name: "Futter: Ausreichend → Niedrig" })).toBeChecked();
|
||||
});
|
||||
|
||||
it("lists only fields filled in this inspection", () => {
|
||||
renderSection({ values: values({ feedStatus: "CRITICAL" }), selected: ["feedStatus"] });
|
||||
|
||||
expect(screen.getAllByRole("checkbox")).toHaveLength(1);
|
||||
expect(screen.getByRole("checkbox", { name: "Futter: Ausreichend → Kritisch" })).toBeChecked();
|
||||
});
|
||||
|
||||
it("says „noch nicht erfasst“ for a colony card without a value", () => {
|
||||
renderSection({
|
||||
colony: { occupiedFrames: null, broodStatus: null, feedStatus: null },
|
||||
values: values({ feedStatus: "WEAK" }),
|
||||
selected: [],
|
||||
});
|
||||
|
||||
expect(
|
||||
screen.getByRole("checkbox", { name: "Futter: noch nicht erfasst → Niedrig" }),
|
||||
).not.toBeChecked();
|
||||
});
|
||||
|
||||
it("reports the new selection when a box is toggled", async () => {
|
||||
const onChange = renderSection();
|
||||
|
||||
await userEvent.click(screen.getByRole("checkbox", { name: "Brut: Mittel → Gut" }));
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith(["occupiedFrames", "feedStatus"]);
|
||||
});
|
||||
|
||||
it("adds a box back in the fixed field order", async () => {
|
||||
const onChange = renderSection({ selected: ["feedStatus"] });
|
||||
|
||||
await userEvent.click(screen.getByRole("checkbox", { name: "Besetzte Waben: 6 → 9" }));
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith(["occupiedFrames", "feedStatus"]);
|
||||
});
|
||||
|
||||
it("announces how many values are taken over", () => {
|
||||
renderSection({ selected: ["feedStatus"] });
|
||||
|
||||
expect(screen.getByRole("status")).toHaveTextContent("1 Wert wird auf die Volkskarte übernommen.");
|
||||
});
|
||||
|
||||
it("is hidden without colonies:update", () => {
|
||||
renderSection({ canUpdateColony: false });
|
||||
|
||||
expect(screen.queryByRole("group", { name: /Volkskarte/ })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("checkbox")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("is hidden when nothing was filled in, but keeps its status region mounted", () => {
|
||||
renderSection({ values: values(), selected: [] });
|
||||
|
||||
expect(screen.queryByRole("group", { name: /Volkskarte/ })).not.toBeInTheDocument();
|
||||
expect(screen.getByRole("status")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("says „bisher unbekannt“ when the card values are not known (cache empty)", () => {
|
||||
renderSection({ colony: null, selected: [] });
|
||||
|
||||
expect(screen.getByRole("checkbox", { name: "Futter: bisher unbekannt → Niedrig" })).toBeInTheDocument();
|
||||
expect(screen.getByRole("checkbox", { name: "Besetzte Waben: bisher unbekannt → 9" })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("can be operated with Tab and Space", async () => {
|
||||
const onChange = renderSection();
|
||||
|
||||
await userEvent.tab();
|
||||
expect(screen.getByRole("checkbox", { name: "Besetzte Waben: 6 → 9" })).toHaveFocus();
|
||||
await userEvent.keyboard(" ");
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith(["broodStatus", "feedStatus"]);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user