+
+
+
+
+
+ >
+ );
+}
diff --git a/frontend/app/module/users/create/index.ts b/frontend/app/module/users/create/index.ts
new file mode 100644
index 000000000..5b4c0b1c1
--- /dev/null
+++ b/frontend/app/module/users/create/index.ts
@@ -0,0 +1,4 @@
+export * from "./UserCreateDetailsPage";
+export * from "./UserCreateLayout";
+export * from "./UserCreateRolePage";
+export * from "./UserCreateTypePage";
diff --git a/frontend/app/module/users/create/useUserCreateContext.ts b/frontend/app/module/users/create/useUserCreateContext.ts
new file mode 100644
index 000000000..9677df035
--- /dev/null
+++ b/frontend/app/module/users/create/useUserCreateContext.ts
@@ -0,0 +1,13 @@
+import { useContext } from "react";
+
+import { UserCreateContext } from "./UserCreateContext";
+
+export function useUserCreateContext() {
+ const context = useContext(UserCreateContext);
+
+ if (!context) {
+ throw new Error("useUserCreateContext must be used within an UserCreateContextProvider");
+ }
+
+ return context;
+}
diff --git a/frontend/app/module/users/index.ts b/frontend/app/module/users/index.ts
index 3a8edd09d..704f52d00 100644
--- a/frontend/app/module/users/index.ts
+++ b/frontend/app/module/users/index.ts
@@ -1 +1,2 @@
export * from "./UserListPage";
+export * from "./create";
diff --git a/frontend/app/routes.tsx b/frontend/app/routes.tsx
index 7d218cafc..2cf28aec6 100644
--- a/frontend/app/routes.tsx
+++ b/frontend/app/routes.tsx
@@ -13,7 +13,13 @@ import {
import { LogsHomePage } from "app/module/logs";
import { NotAvailableInMock } from "app/module/NotAvailableInMock";
import { PollingStationListPage, PollingStationsLayout } from "app/module/polling_stations";
-import { UserListPage } from "app/module/users";
+import {
+ UserCreateDetailsPage,
+ UserCreateLayout,
+ UserCreateRolePage,
+ UserCreateTypePage,
+ UserListPage,
+} from "app/module/users";
import { WorkstationsHomePage } from "app/module/workstations";
import { t } from "@kiesraad/i18n";
@@ -84,6 +90,11 @@ export const routes = createRoutesFromElements(
} />
} />
+ }>
+ } />
+ } />
+ } />
+ } />
diff --git a/frontend/lib/i18n/locales/nl/generic.json b/frontend/lib/i18n/locales/nl/generic.json
index 799c60457..9c791efa8 100644
--- a/frontend/lib/i18n/locales/nl/generic.json
+++ b/frontend/lib/i18n/locales/nl/generic.json
@@ -9,6 +9,7 @@
"close_message": "Melding sluiten",
"contains_error": "bevat een fout",
"contains_warning": "bevat een waarschuwing",
+ "continue": "Verder",
"coordinator": "Coördinator",
"counted_number": "Geteld aantal",
"date_locale": "nl-NL",
@@ -52,7 +53,6 @@
"totals_list": "Totaal lijst {group_number}",
"type": "Soort",
"typist": "Invoerder",
- "users": "Gebruikers",
"version": "Versie",
"vote_count": "Aantal stemmen",
"you_are_here": "je bent hier"
diff --git a/frontend/lib/i18n/locales/nl/nl.ts b/frontend/lib/i18n/locales/nl/nl.ts
index f31708538..7bd4054f0 100644
--- a/frontend/lib/i18n/locales/nl/nl.ts
+++ b/frontend/lib/i18n/locales/nl/nl.ts
@@ -15,12 +15,12 @@ import polling_station_choice from "./polling_station_choice.json";
import recounted from "./recounted.json";
import status from "./status.json";
import user from "./user.json";
+import users from "./users.json";
import voters_and_votes from "./voters_and_votes.json";
import workstations from "./workstations.json";
const nl = {
...generic,
- form_errors,
candidates_votes,
check_and_save,
data_entry,
@@ -30,12 +30,14 @@ const nl = {
election_status,
error,
feedback,
+ form_errors,
messages,
- polling_station_choice,
polling_station,
+ polling_station_choice,
recounted,
status,
user,
+ users,
voters_and_votes,
workstations,
};
diff --git a/frontend/lib/i18n/locales/nl/user.json b/frontend/lib/i18n/locales/nl/user.json
index 286a12098..78eba0580 100644
--- a/frontend/lib/i18n/locales/nl/user.json
+++ b/frontend/lib/i18n/locales/nl/user.json
@@ -1,13 +1,9 @@
{
"personalize_account": "Personaliseer je account",
"username": "Gebruikersnaam",
- "fullname": "Volledige naam",
- "last_activity": "Laatste activiteit",
- "not_used": "Nog niet gebruikt",
"username_hint": "Je kan deze niet aanpassen. Log volgende keer weer met deze gebruikersnaam in.",
"username_login_hint": "De naam op het briefje dat je van de coördinator hebt gekregen.",
"username_default": "Gebruiker01",
- "management": "Gebruikersbeheer",
"name": "Jouw naam",
"name_subtext": "(roepnaam + achternaam)",
"name_hint": "Bijvoorbeeld Karel van Tellingen. Je naam wordt opgenomen in het verslag van deze invoersessie.",
diff --git a/frontend/lib/i18n/locales/nl/users.json b/frontend/lib/i18n/locales/nl/users.json
new file mode 100644
index 000000000..363b4762c
--- /dev/null
+++ b/frontend/lib/i18n/locales/nl/users.json
@@ -0,0 +1,26 @@
+{
+ "add": "Gebruiker toevoegen",
+ "add_role": "{role} toevoegen",
+ "details_title": "Details van het account",
+ "fullname": "Volledige naam",
+ "fullname_hint": "Deze is terug te zien in de logs",
+ "last_activity": "Laatste activiteit",
+ "management": "Gebruikersbeheer",
+ "not_used": "Nog niet gebruikt",
+ "role_administrator_hint": "Verkiezingen voorbereiden, gebruikers aanmaken en uitslagen downloaden",
+ "role_coordinator_hint": "Problemen en fouten tijdens het invoeren oplossen",
+ "role_hint": "Kies de rol van de nieuwe gebruiker. De rol kan na het aanmaken van de gebruiker niet meer aangepast worden.",
+ "role_mandatory": "Dit is een verplichte vraag. Maak een keuze uit de opties hieronder.",
+ "role_title": "Welke rol krijgt de nieuwe gebruiker?",
+ "role_typist_hint": "Tellingen invoeren",
+ "temporary_password": "Tijdelijk wachtwoord",
+ "temporary_password_hint": "Gebruik minimaal {min_length} karakters. Het wachtwoord is hoofdlettergevoelig. De gebruiker moet na eerste keer inloggen zelf een nieuw wachtwoord kiezen.",
+ "temporary_password_error_min_length": "Dit wachtwoord is niet lang genoeg. Gebruik minimaal {min_length} karakters",
+ "type_anonymous": "Anonieme gebruikersnaam (bijvoorbeeld 'Invoerder01')",
+ "type_fullname": "Op naam (bijvoorbeeld 'MariekeDeJager')",
+ "type_hint": "Gebruikers met een anoniem account moeten bij de eerste keer inloggen hun echte naam invoeren. Anonieme accounts zijn vooral handig als je veel invoerders verwacht, en nog niet precies weet wie wel en wie niet aanwezig zal zijn.",
+ "type_title": "Type account",
+ "username": "Gebruikersnaam",
+ "username_hint": "Dit is de naam waarmee de gebruiker kan inloggen. De invoer is niet hoofdlettergevoelig",
+ "users": "Gebruikers"
+}
diff --git a/frontend/lib/ui/Form/FormLayout.module.css b/frontend/lib/ui/Form/FormLayout.module.css
index 193085f1a..0ce5e71e0 100644
--- a/frontend/lib/ui/Form/FormLayout.module.css
+++ b/frontend/lib/ui/Form/FormLayout.module.css
@@ -1,9 +1,13 @@
.form-layout {
- padding-bottom: var(--space-xl);
+ margin-bottom: var(--space-md);
+}
+
+.form-layout.medium-width {
+ max-width: 32rem;
}
.form-section {
- padding-bottom: var(--space-md);
+ margin-bottom: var(--space-md);
h2 {
font-size: 1.5rem;
diff --git a/frontend/lib/ui/Form/FormLayout.tsx b/frontend/lib/ui/Form/FormLayout.tsx
index 3f5c2dfe0..6155fe5ab 100644
--- a/frontend/lib/ui/Form/FormLayout.tsx
+++ b/frontend/lib/ui/Form/FormLayout.tsx
@@ -1,15 +1,18 @@
import * as React from "react";
+import { cn } from "@kiesraad/util";
+
import cls from "./FormLayout.module.css";
export interface FormLayoutProps {
children: React.ReactNode;
+ width?: "medium";
disabled?: boolean;
}
-export function FormLayout({ children, disabled }: FormLayoutProps) {
+export function FormLayout({ children, disabled, width }: FormLayoutProps) {
return (
-