Adopt shared WebUI layout primitives

This commit is contained in:
2026-08-18 10:42:51 +02:00
parent 1351338590
commit 0e03f6a70c
3 changed files with 16 additions and 26 deletions
@@ -10,7 +10,7 @@ import {
Vote Vote
} from "lucide-react"; } from "lucide-react";
import { useCallback, useEffect, useMemo, useState, type ReactNode } from "react"; import { useCallback, useEffect, useMemo, useState, type ReactNode } from "react";
import { import { ActionToolbar,
ActionBlockerHint, ActionBlockerHint,
Button, Button,
DocumentationHelpLink, DocumentationHelpLink,
@@ -183,7 +183,7 @@ export default function CommitteePage({ settings, auth }: PlatformRouteContext)
return ( return (
<main className="committee-page"> <main className="committee-page">
<div className="committee-shell"> <div className="committee-shell">
<div className="committee-toolbar"> <ActionToolbar className="committee-toolbar">
<form onSubmit={(event) => { event.preventDefault(); void refresh(); }} className="committee-search"> <form onSubmit={(event) => { event.preventDefault(); void refresh(); }} className="committee-search">
<Search size={16} aria-hidden="true" /> <Search size={16} aria-hidden="true" />
<input value={query} onChange={(event) => setQuery(event.target.value)} placeholder="Search bodies" aria-label="Search committee bodies" /> <input value={query} onChange={(event) => setQuery(event.target.value)} placeholder="Search bodies" aria-label="Search committee bodies" />
@@ -202,7 +202,7 @@ export default function CommitteePage({ settings, auth }: PlatformRouteContext)
New body New body
</Button> </Button>
<DocumentationHelpLink reference={COMMITTEE_DOCUMENTATION} /> <DocumentationHelpLink reference={COMMITTEE_DOCUMENTATION} />
</div> </ActionToolbar>
{error ? <DismissibleAlert tone="danger" resetKey={error} className="committee-alert">{error}</DismissibleAlert> : null} {error ? <DismissibleAlert tone="danger" resetKey={error} className="committee-alert">{error}</DismissibleAlert> : null}
{loading && bodies.length === 0 ? <LoadingIndicator label="Loading committee workspace" /> : null} {loading && bodies.length === 0 ? <LoadingIndicator label="Loading committee workspace" /> : null}
@@ -1,5 +1,5 @@
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { import { FormGrid,
Button, Button,
ConfirmDialog, ConfirmDialog,
Dialog, Dialog,
@@ -176,7 +176,7 @@ export default function CommitteeRecordDialog({
<div className="committee-record-form"> <div className="committee-record-form">
<div className="committee-dialog-help"><DocumentationHelpLink reference={COMMITTEE_FIELD_DOCUMENTATION} /></div> <div className="committee-dialog-help"><DocumentationHelpLink reference={COMMITTEE_FIELD_DOCUMENTATION} /></div>
{error ? <DismissibleAlert tone="danger" resetKey={error}>{error}</DismissibleAlert> : null} {error ? <DismissibleAlert tone="danger" resetKey={error}>{error}</DismissibleAlert> : null}
<div className="committee-form-grid"> <FormGrid columns={2} gap="compact" collapseAt="narrow">
<FormField label="Title" documentation={COMMITTEE_FIELD_DOCUMENTATION}> <FormField label="Title" documentation={COMMITTEE_FIELD_DOCUMENTATION}>
<input <input
value={draft.title} value={draft.title}
@@ -194,7 +194,7 @@ export default function CommitteeRecordDialog({
{stateOptions.map((state) => <option key={state} value={state}>{stateLabel(state)}</option>)} {stateOptions.map((state) => <option key={state} value={state}>{stateLabel(state)}</option>)}
</select> </select>
</FormField> </FormField>
</div> </FormGrid>
{kind === "body" ? ( {kind === "body" ? (
<FormField label="Responsible organization unit ID" documentation={COMMITTEE_FIELD_DOCUMENTATION}> <FormField label="Responsible organization unit ID" documentation={COMMITTEE_FIELD_DOCUMENTATION}>
@@ -207,19 +207,19 @@ export default function CommitteeRecordDialog({
) : null} ) : null}
{kind === "meeting" ? ( {kind === "meeting" ? (
<div className="committee-form-grid"> <FormGrid columns={2} gap="compact" collapseAt="narrow">
<FormField label="Starts"> <FormField label="Starts">
<input type="datetime-local" value={draft.startsAt} disabled={busy} onChange={(event) => setDraft({ ...draft, startsAt: event.target.value })} /> <input type="datetime-local" value={draft.startsAt} disabled={busy} onChange={(event) => setDraft({ ...draft, startsAt: event.target.value })} />
</FormField> </FormField>
<FormField label="Ends"> <FormField label="Ends">
<input type="datetime-local" value={draft.endsAt} disabled={busy} onChange={(event) => setDraft({ ...draft, endsAt: event.target.value })} /> <input type="datetime-local" value={draft.endsAt} disabled={busy} onChange={(event) => setDraft({ ...draft, endsAt: event.target.value })} />
</FormField> </FormField>
</div> </FormGrid>
) : null} ) : null}
{kind === "agenda_item" ? ( {kind === "agenda_item" ? (
<> <>
<div className="committee-form-grid committee-form-grid-three"> <FormGrid columns={3} gap="compact" collapseAt="narrow">
<FormField label="Position"> <FormField label="Position">
<input type="number" min="1" value={draft.position} disabled={busy} onChange={(event) => setDraft({ ...draft, position: event.target.value })} /> <input type="number" min="1" value={draft.position} disabled={busy} onChange={(event) => setDraft({ ...draft, position: event.target.value })} />
</FormField> </FormField>
@@ -234,7 +234,7 @@ export default function CommitteeRecordDialog({
<FormField label="Subject ID"> <FormField label="Subject ID">
<input value={draft.subjectId} disabled={busy} onChange={(event) => setDraft({ ...draft, subjectId: event.target.value })} /> <input value={draft.subjectId} disabled={busy} onChange={(event) => setDraft({ ...draft, subjectId: event.target.value })} />
</FormField> </FormField>
</div> </FormGrid>
{draft.state === "decided" ? ( {draft.state === "decided" ? (
<FormField label="Formal Decision ID" documentation={COMMITTEE_FIELD_DOCUMENTATION}> <FormField label="Formal Decision ID" documentation={COMMITTEE_FIELD_DOCUMENTATION}>
<input value={draft.decisionId} disabled={busy} onChange={(event) => setDraft({ ...draft, decisionId: event.target.value })} /> <input value={draft.decisionId} disabled={busy} onChange={(event) => setDraft({ ...draft, decisionId: event.target.value })} />
@@ -245,7 +245,7 @@ export default function CommitteeRecordDialog({
{kind === "vote" ? ( {kind === "vote" ? (
<> <>
<div className="committee-form-grid committee-form-grid-three"> <FormGrid columns={3} gap="compact" collapseAt="narrow">
<FormField label="Method"> <FormField label="Method">
<select value={draft.method} disabled={busy} onChange={(event) => setDraft({ ...draft, method: event.target.value })}> <select value={draft.method} disabled={busy} onChange={(event) => setDraft({ ...draft, method: event.target.value })}>
<option value="recorded">Recorded</option> <option value="recorded">Recorded</option>
@@ -259,7 +259,7 @@ export default function CommitteeRecordDialog({
<FormField label="Ballot provider (optional)" documentation={COMMITTEE_FIELD_DOCUMENTATION}> <FormField label="Ballot provider (optional)" documentation={COMMITTEE_FIELD_DOCUMENTATION}>
<input value={draft.providerId} disabled={busy} onChange={(event) => setDraft({ ...draft, providerId: event.target.value })} /> <input value={draft.providerId} disabled={busy} onChange={(event) => setDraft({ ...draft, providerId: event.target.value })} />
</FormField> </FormField>
</div> </FormGrid>
<FormField label="Voting ballot ID (optional)" documentation={COMMITTEE_FIELD_DOCUMENTATION}> <FormField label="Voting ballot ID (optional)" documentation={COMMITTEE_FIELD_DOCUMENTATION}>
<input value={draft.votingBallotId} disabled={busy} onChange={(event) => setDraft({ ...draft, votingBallotId: event.target.value })} /> <input value={draft.votingBallotId} disabled={busy} onChange={(event) => setDraft({ ...draft, votingBallotId: event.target.value })} />
</FormField> </FormField>
@@ -268,7 +268,7 @@ export default function CommitteeRecordDialog({
</FormField> </FormField>
{draft.state === "closed" ? ( {draft.state === "closed" ? (
<div className="committee-vote-result-fields"> <div className="committee-vote-result-fields">
<div className="committee-form-grid"> <FormGrid columns={2} gap="compact" collapseAt="narrow">
<FormField label="Votes cast"> <FormField label="Votes cast">
<input type="number" min="0" value={draft.castCount} disabled={busy} onChange={(event) => setDraft({ ...draft, castCount: event.target.value })} /> <input type="number" min="0" value={draft.castCount} disabled={busy} onChange={(event) => setDraft({ ...draft, castCount: event.target.value })} />
</FormField> </FormField>
@@ -278,7 +278,7 @@ export default function CommitteeRecordDialog({
<option value="not-met">Not met</option> <option value="not-met">Not met</option>
</select> </select>
</FormField> </FormField>
</div> </FormGrid>
<div className="committee-count-grid"> <div className="committee-count-grid">
{choices.map((choice) => ( {choices.map((choice) => (
<FormField key={choice} label={`${choice} votes`}> <FormField key={choice} label={`${choice} votes`}>
@@ -344,14 +344,14 @@ export default function CommitteeRecordDialog({
function EvidenceFields({ draft, busy, setDraft }: { draft: Draft; busy: boolean; setDraft: (draft: Draft) => void }) { function EvidenceFields({ draft, busy, setDraft }: { draft: Draft; busy: boolean; setDraft: (draft: Draft) => void }) {
return ( return (
<div className="committee-form-grid"> <FormGrid columns={2} gap="compact" collapseAt="narrow">
<FormField label="Approval ID" documentation={COMMITTEE_FIELD_DOCUMENTATION}> <FormField label="Approval ID" documentation={COMMITTEE_FIELD_DOCUMENTATION}>
<input value={draft.approvalId} disabled={busy} onChange={(event) => setDraft({ ...draft, approvalId: event.target.value })} /> <input value={draft.approvalId} disabled={busy} onChange={(event) => setDraft({ ...draft, approvalId: event.target.value })} />
</FormField> </FormField>
<FormField label="Evidence record ID" documentation={COMMITTEE_FIELD_DOCUMENTATION}> <FormField label="Evidence record ID" documentation={COMMITTEE_FIELD_DOCUMENTATION}>
<input value={draft.evidenceId} disabled={busy} onChange={(event) => setDraft({ ...draft, evidenceId: event.target.value })} /> <input value={draft.evidenceId} disabled={busy} onChange={(event) => setDraft({ ...draft, evidenceId: event.target.value })} />
</FormField> </FormField>
</div> </FormGrid>
); );
} }
-10
View File
@@ -268,17 +268,11 @@
justify-content: flex-end; justify-content: flex-end;
} }
.committee-form-grid,
.committee-count-grid { .committee-count-grid {
display: grid; display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px; gap: 10px;
} }
.committee-form-grid-three {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.committee-count-grid { .committee-count-grid {
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
} }
@@ -328,8 +322,4 @@
grid-row: 1 / span 2; grid-row: 1 / span 2;
} }
.committee-form-grid,
.committee-form-grid-three {
grid-template-columns: 1fr;
}
} }