diff --git a/frontend/app/component/election/status/ElectionStatus.tsx b/frontend/app/component/election/status/ElectionStatus.tsx index 001c8c0af..59be5c43c 100644 --- a/frontend/app/component/election/status/ElectionStatus.tsx +++ b/frontend/app/component/election/status/ElectionStatus.tsx @@ -159,18 +159,18 @@ function getTableHeaderForCategory(category: StatusCategory): React.ReactNode { function CategoryHeader({ children }: { children?: React.ReactNode[] }) { return ( - {t("number")} - {t("polling_station.title.singular")} + {t("number")} + {t("polling_station.title.singular")} {children} ); } - const finishedAtColumn = {t("finished_at")}; + const finishedAtColumn = {t("finished_at")}; const progressColumn = ( - + {t("progress")} - + ); switch (category) { diff --git a/frontend/app/component/form/data_entry/polling_station_choice/PollingStationChoiceForm.test.tsx b/frontend/app/component/form/data_entry/polling_station_choice/PollingStationChoiceForm.test.tsx index 5e4678123..33d00268a 100644 --- a/frontend/app/component/form/data_entry/polling_station_choice/PollingStationChoiceForm.test.tsx +++ b/frontend/app/component/form/data_entry/polling_station_choice/PollingStationChoiceForm.test.tsx @@ -281,6 +281,40 @@ describe("Test PollingStationChoiceForm", () => { expect(screen.getByText("Geen stembureaus gevonden")).toBeVisible(); }); + test("All data entries of polling stations are finished, polling station list shows message", async () => { + overrideOnce("get", "/api/elections/1", 200, electionDetailsMockResponse); + overrideOnce("get", "/api/elections/1/status", 200, { + statuses: [ + { + polling_station_id: 1, + status: "definitive", + }, + { + polling_station_id: 2, + status: "definitive", + }, + { + polling_station_id: 3, + status: "definitive", + }, + { + polling_station_id: 4, + status: "entries_different", + }, + ], + } satisfies ElectionStatusResponse); + + const user = userEvent.setup(); + renderPollingStationChoicePage(); + + const openPollingStationList = await screen.findByTestId("openPollingStationList"); + await user.click(openPollingStationList); + expect(screen.getByText("Kies het stembureau")).toBeVisible(); + + // Check if the error message is visible + expect(screen.getByText("Alle stembureaus zijn twee keer ingevoerd")).toBeVisible(); + }); + test("Second data entry has correct link", async () => { overrideOnce("get", "/api/elections/1", 200, electionDetailsMockResponse); overrideOnce("get", "/api/elections/1/status", 200, { diff --git a/frontend/app/component/form/data_entry/polling_station_choice/PollingStationChoiceForm.tsx b/frontend/app/component/form/data_entry/polling_station_choice/PollingStationChoiceForm.tsx index acb43ae41..07532364b 100644 --- a/frontend/app/component/form/data_entry/polling_station_choice/PollingStationChoiceForm.tsx +++ b/frontend/app/component/form/data_entry/polling_station_choice/PollingStationChoiceForm.tsx @@ -77,6 +77,11 @@ export function PollingStationChoiceForm({ anotherEntry }: PollingStationChoiceF status: status.status, })); + const pollingStationsForDataEntry = pollingStations.filter((pollingStation: PollingStation) => { + const status = electionStatus.statuses.find((status) => status.polling_station_id === pollingStation.id)?.status; + return status !== "definitive" && status !== "entries_different"; + }); + return (
{ @@ -145,12 +150,16 @@ export function PollingStationChoiceForm({ anotherEntry }: PollingStationChoiceF

{t("polling_station_choice.choose_polling_station")}

- {pollingStations.length === 0 ? ( + {!pollingStations.length ? (

{t("polling_station_choice.no_polling_stations_found")}

+ ) : !pollingStationsForDataEntry.length ? ( + +

{t("polling_station_choice.all_polling_stations_filled_in_twice")}

+
) : ( - + )} diff --git a/frontend/app/component/form/data_entry/polling_station_choice/PollingStationsList.tsx b/frontend/app/component/form/data_entry/polling_station_choice/PollingStationsList.tsx index 15776957e..98a83d1e4 100644 --- a/frontend/app/component/form/data_entry/polling_station_choice/PollingStationsList.tsx +++ b/frontend/app/component/form/data_entry/polling_station_choice/PollingStationsList.tsx @@ -13,17 +13,14 @@ export function PollingStationsList({ pollingStations }: PollingStationsListProp return ( - {t("number")} - {t("polling_station.title.singular")} + {t("number")} + {t("polling_station.title.singular")} {pollingStations.map((pollingStation: PollingStation) => { const status = electionStatus.statuses.find( (status) => status.polling_station_id === pollingStation.id, )?.status; - if (status === "definitive" || status === "entries_different") { - return null; - } return ( +
); } diff --git a/frontend/app/module/election/page/OverviewPage.test.tsx b/frontend/app/module/election/page/OverviewPage.test.tsx new file mode 100644 index 000000000..3fe79d41a --- /dev/null +++ b/frontend/app/module/election/page/OverviewPage.test.tsx @@ -0,0 +1,49 @@ +import { screen } from "@testing-library/react"; +import { beforeEach, describe, expect, test } from "vitest"; + +import { ElectionListProvider, ElectionListResponse } from "@kiesraad/api"; +import { ElectionListRequestHandler } from "@kiesraad/api-mocks"; +import { overrideOnce, render, server } from "@kiesraad/test"; + +import { OverviewPage } from "./OverviewPage"; + +describe("OverviewPage", () => { + beforeEach(() => { + server.use(ElectionListRequestHandler); + }); + + test("Show elections", async () => { + render( + + + , + ); + + const table = await screen.findByRole("table"); + expect(table).toBeVisible(); + expect(table).toHaveTableContent([ + ["Verkiezing", "Gebied", "Status"], + ["Gemeenteraadsverkiezingen 2026", "Heemdamseburg", "Steminvoer bezig"], + ]); + }); + + test("Show no elections message", async () => { + overrideOnce("get", "/api/elections", 200, { + elections: [], + } satisfies ElectionListResponse); + + render( + + + , + ); + + expect(await screen.findByText(/Abacus is nog niet klaar voor gebruik/)).toBeVisible(); + expect( + await screen.findByText( + /Je kan als invoerder nog niks doen. Wacht tot de coördinator het systeem openstelt voor het invoeren van telresultaten./, + ), + ).toBeVisible(); + expect(screen.queryByRole("table")).toBeNull(); + }); +}); diff --git a/frontend/app/module/election/page/OverviewPage.tsx b/frontend/app/module/election/page/OverviewPage.tsx index ac54c79d0..87a54f691 100644 --- a/frontend/app/module/election/page/OverviewPage.tsx +++ b/frontend/app/module/election/page/OverviewPage.tsx @@ -45,29 +45,41 @@ export function OverviewPage() { )}
-
- - {t("election.title.singular")} - - {!isAdminOrCoordinator ? t("election.location") : t("election.level_polling_station")} - - {t("election_status.label")} - - - {electionList.map((election) => ( - - {election.name} - {!isAdminOrCoordinator ? election.location : ""} - - - - - ))} - -
+ {!electionList.length ? ( + !isAdminOrCoordinator ? ( + <> +

{t("election.not_ready_for_use")}

+

{t("election.please_wait_for_coordinator")}

+ + ) : ( +

{t("election.no_elections_added")}

+ // TODO: To be expanded in issue #888 + ) + ) : ( + + + {t("election.title.singular")} + + {!isAdminOrCoordinator ? t("election.location") : t("election.level_polling_station")} + + {t("election_status.label")} + + + {electionList.map((election) => ( + + {election.name} + {!isAdminOrCoordinator ? election.location : ""} + + + + + ))} + +
+ )}