Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some improvements to Tables and OverviewPage #990

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
da36f84
Add missing footer to Election Report Page
Lionqueen94 Feb 7, 2025
79a57cb
Make sure table linkrow has separate column for the chevron with a se…
Lionqueen94 Feb 7, 2025
a6990f0
Show a message that no elections exist on overview page instead of a …
Lionqueen94 Feb 7, 2025
41ece77
Forgot UserListPage LinkRow change
Lionqueen94 Feb 7, 2025
f58f162
Corrected error logo color in Alert, ProgressList and StatusList and …
Lionqueen94 Feb 7, 2025
2c2435b
Merge branch 'main' into some_small_fixes
Lionqueen94 Feb 7, 2025
2d26841
Make sure there is no empty table when there are no polling stations …
Lionqueen94 Feb 8, 2025
ffba221
Adjusted no elections text, to be expanded in other epic
Lionqueen94 Feb 8, 2025
ce7cbca
Fix test
Lionqueen94 Feb 9, 2025
baf5ca6
Merge branch 'main' into some_small_fixes
Lionqueen94 Feb 10, 2025
9f9e9ea
Rename Table.Column to Table.HeaderCell and undid empty cell for chev…
Lionqueen94 Feb 10, 2025
558485a
Added Footer to OverviewLayout and removed from all child routes and …
Lionqueen94 Feb 10, 2025
5fe59c8
Merge branch 'main' into some_small_fixes
Lionqueen94 Feb 10, 2025
9d96510
Merge branch 'main' into some_small_fixes
Lionqueen94 Feb 11, 2025
e72177b
Added separate alert when all polling stations data entries have been…
Lionqueen94 Feb 11, 2025
eecd93d
Merge branch 'main' into some_small_fixes
Lionqueen94 Feb 12, 2025
a06a9ac
Re-added the Footer back to the pages instead of in the Layout, we sh…
Lionqueen94 Feb 12, 2025
20d7838
Moved footer to OverviewPage instead of OverviewLayout to be more con…
Lionqueen94 Feb 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions frontend/app/component/election/status/ElectionStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,18 +159,18 @@ function getTableHeaderForCategory(category: StatusCategory): React.ReactNode {
function CategoryHeader({ children }: { children?: React.ReactNode[] }) {
return (
<Table.Header key={category} className="bg-gray">
<Table.Column key={`${category}-number`}>{t("number")}</Table.Column>
<Table.Column key={`${category}-name`}>{t("polling_station.title.singular")}</Table.Column>
<Table.HeaderCell key={`${category}-number`}>{t("number")}</Table.HeaderCell>
<Table.HeaderCell key={`${category}-name`}>{t("polling_station.title.singular")}</Table.HeaderCell>
{children}
</Table.Header>
);
}

const finishedAtColumn = <Table.Column key={`${category}-time`}>{t("finished_at")}</Table.Column>;
const finishedAtColumn = <Table.HeaderCell key={`${category}-time`}>{t("finished_at")}</Table.HeaderCell>;
const progressColumn = (
<Table.Column key={`${category}-progress`} className="w-13">
<Table.HeaderCell key={`${category}-progress`} className="w-13">
{t("progress")}
</Table.Column>
</Table.HeaderCell>
);

switch (category) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
});

praseodym marked this conversation as resolved.
Show resolved Hide resolved
return (
<form
onSubmit={(e) => {
Expand Down Expand Up @@ -145,12 +150,16 @@ export function PollingStationChoiceForm({ anotherEntry }: PollingStationChoiceF
</span>
</summary>
<h2 className={cls.formTitle}>{t("polling_station_choice.choose_polling_station")}</h2>
{pollingStations.length === 0 ? (
{!pollingStations.length ? (
<Alert type="error" variant="small">
<p>{t("polling_station_choice.no_polling_stations_found")}</p>
</Alert>
) : !pollingStationsForDataEntry.length ? (
<Alert type="notify" variant="small">
<p>{t("polling_station_choice.all_polling_stations_filled_in_twice")}</p>
</Alert>
) : (
<PollingStationsList pollingStations={pollingStations} />
<PollingStationsList pollingStations={pollingStationsForDataEntry} />
)}
</details>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,14 @@ export function PollingStationsList({ pollingStations }: PollingStationsListProp
return (
<Table id="polling_station_list">
<Table.Header>
<Table.Column>{t("number")}</Table.Column>
<Table.Column>{t("polling_station.title.singular")}</Table.Column>
<Table.HeaderCell>{t("number")}</Table.HeaderCell>
<Table.HeaderCell>{t("polling_station.title.singular")}</Table.HeaderCell>
</Table.Header>
<Table.Body>
{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;
}
praseodym marked this conversation as resolved.
Show resolved Hide resolved

return (
<Table.LinkRow
Expand Down
2 changes: 0 additions & 2 deletions frontend/app/module/data_entry/page/DataEntryHomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { useEffect } from "react";
import { useLocation, useNavigate } from "react-router";

import { ElectionProgress } from "app/component/election/ElectionProgress";
import { Footer } from "app/component/footer/Footer";
import { PollingStationChoiceForm } from "app/component/form/data_entry/polling_station_choice/PollingStationChoiceForm";

import { DEFAULT_CANCEL_REASON, useElection, useElectionStatus } from "@kiesraad/api";
Expand Down Expand Up @@ -73,7 +72,6 @@ export function DataEntryHomePage() {
</article>
<ElectionProgress />
</main>
<Footer />
</>
);
}
3 changes: 3 additions & 0 deletions frontend/app/module/election/OverviewLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Outlet } from "react-router";

import { Footer } from "app/component/footer/Footer";
Lionqueen94 marked this conversation as resolved.
Show resolved Hide resolved

import { ElectionListProvider } from "@kiesraad/api";
import { AppLayout } from "@kiesraad/ui";

Expand All @@ -8,6 +10,7 @@ export function OverviewLayout() {
<ElectionListProvider>
<AppLayout>
<Outlet />
<Footer />
</AppLayout>
</ElectionListProvider>
);
Expand Down
3 changes: 0 additions & 3 deletions frontend/app/module/election/page/ElectionHomePage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { Link } from "react-router";

import { Footer } from "app/component/footer/Footer";

import { useElection } from "@kiesraad/api";
import { t } from "@kiesraad/i18n";
import { PageTitle } from "@kiesraad/ui";
Expand Down Expand Up @@ -42,7 +40,6 @@ export function ElectionHomePage() {
</ul>
</article>
</main>
<Footer />
</>
);
}
2 changes: 0 additions & 2 deletions frontend/app/module/election/page/ElectionStatusPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { useNavigate } from "react-router";

import { HeaderElectionStatusWithIcon } from "app/component/election/ElectionStatusWithIcon";
import { ElectionStatus } from "app/component/election/status/ElectionStatus";
import { Footer } from "app/component/footer/Footer";

import { useElection, useElectionStatus } from "@kiesraad/api";
import { t } from "@kiesraad/i18n";
Expand Down Expand Up @@ -49,7 +48,6 @@ export function ElectionStatusPage() {
navigate={(path) => void navigate(path)}
/>
</main>
<Footer />
</>
);
}
49 changes: 49 additions & 0 deletions frontend/app/module/election/page/OverviewPage.test.tsx
Original file line number Diff line number Diff line change
@@ -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(
<ElectionListProvider>
<OverviewPage />
</ElectionListProvider>,
);

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(
<ElectionListProvider>
<OverviewPage />
</ElectionListProvider>,
);

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();
});
});
60 changes: 35 additions & 25 deletions frontend/app/module/election/page/OverviewPage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { To, useLocation, useNavigate } from "react-router";

import { ElectionStatusWithIcon } from "app/component/election/ElectionStatusWithIcon";
import { Footer } from "app/component/footer/Footer";
import { NavBar } from "app/component/navbar/NavBar";

import { Election, useElectionList } from "@kiesraad/api";
Expand Down Expand Up @@ -50,32 +49,43 @@ export function OverviewPage() {
)}
<main>
<article>
<Table id="overview">
<Table.Header>
<Table.Column>{t("election.title.singular")}</Table.Column>
<Table.Column>
{!isAdminOrCoordinator ? t("election.location") : t("election.level_polling_station")}
</Table.Column>
<Table.Column>{t("election_status.label")}</Table.Column>
</Table.Header>
<Table.Body className="fs-md">
{electionList.map((election) => (
<Table.LinkRow key={election.id} to={electionLink(election)}>
<Table.Cell className="fs-body">{election.name}</Table.Cell>
<Table.Cell>{!isAdminOrCoordinator ? election.location : ""}</Table.Cell>
<Table.Cell>
<ElectionStatusWithIcon
status={election.status}
userRole={isAdminOrCoordinator ? "coordinator" : "typist"}
/>
</Table.Cell>
</Table.LinkRow>
))}
</Table.Body>
</Table>
{!electionList.length ? (
!isAdminOrCoordinator ? (
<>
<h2 className="mb-lg">{t("election.not_ready_for_use")}</h2>
<p className="md form-paragraph">{t("election.please_wait_for_coordinator")}</p>
</>
) : (
<h2>{t("election.no_elections_added")}</h2>
// TODO: To be expanded in issue #888
)
) : (
<Table id="overview">
<Table.Header>
<Table.HeaderCell>{t("election.title.singular")}</Table.HeaderCell>
<Table.HeaderCell>
{!isAdminOrCoordinator ? t("election.location") : t("election.level_polling_station")}
</Table.HeaderCell>
<Table.HeaderCell>{t("election_status.label")}</Table.HeaderCell>
</Table.Header>
<Table.Body className="fs-md">
{electionList.map((election) => (
<Table.LinkRow key={election.id} to={electionLink(election)}>
<Table.Cell className="fs-body">{election.name}</Table.Cell>
<Table.Cell>{!isAdminOrCoordinator ? election.location : ""}</Table.Cell>
<Table.Cell>
<ElectionStatusWithIcon
status={election.status}
userRole={isAdminOrCoordinator ? "coordinator" : "typist"}
/>
</Table.Cell>
</Table.LinkRow>
))}
</Table.Body>
</Table>
)}
</article>
</main>
<Footer />
</>
);
}
14 changes: 0 additions & 14 deletions frontend/app/module/polling_stations/PollingStationsLayout.tsx

This file was deleted.

1 change: 0 additions & 1 deletion frontend/app/module/polling_stations/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export * from "./PollingStationsLayout";
export * from "./page";
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,9 @@ export function PollingStationListPage() {

<Table id="polling_stations">
<Table.Header>
<Table.Column>{t("number")}</Table.Column>
<Table.Column>{t("name")}</Table.Column>
<Table.Column>{t("type")}</Table.Column>
<Table.HeaderCell>{t("number")}</Table.HeaderCell>
<Table.HeaderCell>{t("name")}</Table.HeaderCell>
<Table.HeaderCell>{t("type")}</Table.HeaderCell>
</Table.Header>
<Table.Body className="fs-md">
{data.polling_stations.map((station) => (
Expand Down
8 changes: 4 additions & 4 deletions frontend/app/module/users/UserListPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ export function UserListPage() {

<Table id="users">
<Table.Header>
<Table.Column>{t("users.username")}</Table.Column>
<Table.Column>{t("role")}</Table.Column>
<Table.Column>{t("users.fullname")}</Table.Column>
<Table.Column>{t("users.last_activity")}</Table.Column>
<Table.HeaderCell>{t("users.username")}</Table.HeaderCell>
<Table.HeaderCell>{t("role")}</Table.HeaderCell>
<Table.HeaderCell>{t("users.fullname")}</Table.HeaderCell>
<Table.HeaderCell>{t("users.last_activity")}</Table.HeaderCell>
</Table.Header>
<Table.Body className="fs-md">
{users.map((user) => (
Expand Down
4 changes: 2 additions & 2 deletions frontend/app/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
} from "app/module/election";
import { LogsHomePage } from "app/module/logs";
import { NotAvailableInMock } from "app/module/NotAvailableInMock";
import { PollingStationListPage, PollingStationsLayout } from "app/module/polling_stations";
import { PollingStationListPage } from "app/module/polling_stations";
import {
UserCreateDetailsPage,
UserCreateLayout,
Expand Down Expand Up @@ -66,7 +66,7 @@ export const routes = createRoutesFromElements(
}
/>
<Route path="status" element={<ElectionStatusPage />} />
<Route path="polling-stations" element={<PollingStationsLayout />}>
<Route path="polling-stations" element={null}>
<Route index element={<PollingStationListPage />} />
<Route path="create" element={<PollingStationCreatePage />} />
<Route path=":pollingStationId/update" element={<PollingStationUpdatePage />} />
Expand Down
2 changes: 0 additions & 2 deletions frontend/lib/api/election/ElectionListProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ export function ElectionListProvider({ children }: ElectionListProviderProps) {
return (
<RequestStateHandler
requestState={requestState}
notFoundMessage="error.elections_not_found"
isFoundCheck={(data) => data.elections.length > 0}
renderOnSuccess={(data) => (
<ElectionListProviderContext.Provider value={{ electionList: data.elections }}>
{children}
Expand Down
3 changes: 3 additions & 0 deletions frontend/lib/i18n/locales/nl/election.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
},
"level_polling_station": "Niveau stembureau",
"location": "Gebied",
"no_elections_added": "Nog geen verkiezingen ingesteld",
"not_ready_for_use": "Abacus is nog niet klaar voor gebruik",
"please_wait_for_coordinator": "Je kan als invoerder nog niks doen. Wacht tot de coördinator het systeem openstelt voor het invoeren van telresultaten.",
"status": "Status",
"start_when_count_list_received": "Zodra je een tellijst van een stembureau hebt gekregen kan je beginnen met invoeren."
}
Loading
Loading