Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1b45c50
docs(people): add People area handoff docs and prototype
claude Oct 14, 2026
48bd91f
feat(people): add People permissions, restricted access and sample data
claude Oct 14, 2026
7dd9cb4
feat(people): add People navigation, routes and agent-mode API
claude Oct 14, 2026
c12724a
feat(people): add People overview and person record
claude Oct 14, 2026
561f9c7
feat(people): add the member register
claude Oct 14, 2026
13171de
feat(people): add the admins list and admin role actions
claude Oct 14, 2026
8e00f6f
feat(people): add teams, team pages and team membership actions
claude Oct 14, 2026
fb6e158
feat(people): add census campaigns and the responses import wizard
claude Oct 14, 2026
78f5cc9
feat(people): add the linking queue and pending invites
claude Oct 14, 2026
8f4765f
feat(people): add the Capo Admin page
claude Oct 14, 2026
299052f
feat(people): add My area for volunteers and members
claude Oct 14, 2026
efc0238
docs(people): document the People area and finish the handoff notes
claude Oct 14, 2026
0e8578a
Merge branch 'main' into claude/eager-mayer-bsom7v
claude Oct 14, 2026
dde8f3a
fix(people): resolve UI and access audit findings
viganogabriele Oct 14, 2026
feb7f0d
refactor(ui): restrict audit changes to dashboard visuals
viganogabriele Oct 14, 2026
2fd756a
Merge branch 'main' into claude/eager-mayer-bsom7v
Diubii Oct 15, 2026
bd21599
fix(people): close the gaps from the PR security review
claude Oct 15, 2026
7ffc364
test: cover the People access fixes from the security review
Diubii Oct 15, 2026
7c20fa8
fix(people): keep member details behind people:members:read
claude Oct 15, 2026
782d54e
fix(ui): keep record and alert actions off the text on phones
claude Oct 15, 2026
51cfd1c
fix(people): keep the import stepper on one line on phones
claude Oct 15, 2026
075893c
fix(ui): stop narrow cards from breaking values and titles
claude Oct 15, 2026
99dc0ea
fix(people): fit the member register filters on phones
claude Oct 15, 2026
2431dc5
fix(people): wrap Capo Admin request buttons under the request on phones
claude Oct 15, 2026
e1f8bce
fix(people): show the course under every name in team tables
claude Oct 15, 2026
d19af1f
fix(people): stack the census card buttons on phones
claude Oct 15, 2026
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
5 changes: 5 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ What the app is and how to run it: [`README.md`](README.md). UI rules: [`docs/de
the backend's fixed new-member workflow. List groups with `azure:groups:read`; add or remove existing directory users
in a group with `azure:groups:write`; the add picker lists the directory, so it is disabled without
`azure:members:read`. Users and groups themselves cannot be edited from the dashboard.
- **People** (`src/features/people/`): every page reads and writes through `peopleApi(context)`, which parses the
backend's `people.*` responses with `people.contract.ts` and, in agent mode, uses the in-memory fake
(`AGENT_PERMISSIONS`, `AGENT_PERSON` pick the viewer). `/dashboard` admits `people:self` without `admin:access`;
Capo Admin and Team Lead come from the person's own record (`peopleRoles` in the route context) and only open their
sections. Handoff notes, decisions and the plan: `docs/people/`.
- **Shared UI**: `src/components/shell`, `src/components/primitives`, `src/components/ui`.

## Conventions
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ Microsoft 365 supports a read-only member directory, creating a new association
workflow, and group membership management. Directory access requires `azure:members:read`; creation independently
requires `azure:members:create`. Groups require `azure:groups:read`; adding and removing members `azure:groups:write`.

**People** keeps one record per volunteer and member (Overview, Member register, Admins, Teams, Census, Linking), plus
Telegram › Capo Admin and Account › My area for volunteers themselves. The dashboard talks to a typed `people.*`
contract (`src/features/people/people.contract.ts`, documented in `docs/people/contract.md`); until the backend
implements it, agent mode serves sample data from `people.fake.server.ts`. Permissions: `people:read`, `people:write`,
`people:members:read`, `people:members:write`, `people:appoint`, and `people:self`, which alone admits non-admins to My
area and, by the role on their record, Capo Admin or their team. See `docs/people/README.md`.

Built with TanStack Start (React 19), Vite+, Nitro, Tailwind CSS v4 and shadcn/Base UI. The server is an OIDC BFF:
only it holds the IdP access/refresh/ID tokens. Browsers hold an opaque, HttpOnly session cookie; sessions live in a
**dedicated Redis**, separate from the shared backend Redis. Backend requests carry a user bearer token, and permissions
Expand Down Expand Up @@ -34,6 +41,8 @@ Set the following runtime variables (the build does not need secrets):
| `BACKEND_RESOURCE` | Backend OAuth resource/audience; defaults to `https://backend.internal.polinetwork.org`. Sent on every authorize, code exchange and refresh request. |
| `REDIS_URL` | Dedicated dashboard Redis connection. Required outside development; development defaults to `redis://localhost:6379`. Never point to shared backend Redis. |
| `SESSION_SECRET` | Independent random secret of at least 32 characters, encrypting the 10-minute state/nonce/PKCE cookie. Rotation invalidates pending login attempts. |
| `AGENT_PERMISSIONS` | Development only. Comma-separated permissions for the agent-mode user (default: all). `people:self` alone previews the restricted People view. |
| `AGENT_PERSON` | Development only. The agent-mode user's People record (default `P-0007`, Direttivo). `P-0098` is a Capo Admin, `P-0034` the IT Team Lead, `P-0115` an IT member; `match:P-0115` previews the first sign-in, `none` a sign-in without a record. |
| `AGENT_MODE` | Fake all-permission user in development only. Backend must be a local fake or local legacy test instance. Production ignores this mode. |
| `PORT` | Local listening port (3001 by default); use a port ≥10000 for agent previews. |
| `TEST_REDIS_URL` | Opt-in disposable Redis for integration tests. Tests create/delete only UUID session keys. |
Expand Down
26 changes: 24 additions & 2 deletions docs/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,9 @@ the `h1`.
**Small screens** (below 760px). Rail and panel leave the layout. A 56px top bar holds the logo (Overview), the
service title ("PoliNetwork Admin" on Overview), Search, the theme toggle and the avatar; the service's sections follow
as horizontally scrolling pills that scroll away with the page. The current pill stays in view after navigation,
font loading and changes to the bar's width, including when it first appears below 760px. A 64px bottom nav lists the five services (current one
accent with a top bar, Reports with the open-reports dot). Section pills and bottom nav are the only navigation; there
font loading and changes to the bar's width, including when it first appears below 760px. A 64px bottom nav lists the visible services (current one
accent with a top bar, Reports with the open-reports dot). The device's bottom safe area adds space below the 64px
bar. Below 360px, WhatsApp uses "WA" so all labels remain readable. Section pills and bottom nav are the only navigation; there
is no sheet.

**Command palette** (`⌘K`/`Ctrl+K`, the rail and top bar Search, the Overview search): sections, "Find “…” in
Expand Down Expand Up @@ -331,6 +332,10 @@ keystroke. Errors: danger border, 12px danger text with an icon, `aria-invalid`
are text in a `KeyValueList`, never disabled inputs. Length counters (`FieldCounter`) appear only in the last 20% of the
limit.

Calendar dates use a custom picker at every viewport, display the same day–month–year format as read-only dates,
and retain ISO values for the server. Month and year selectors are custom controls; calendars start on Monday.
Grant date/time fields use the calendar and time wheels on touch devices too, with no native date/time fallback.

**Alerts and toasts.** `InlineAlert` for what persists or needs action: load errors, dialog submit errors, partial
saves. It defaults to `tone="danger"` and `role="alert"`, so pass another tone for notices that aren't errors. Toasts
(`appToast.success/info/warning/error`, never raw `toast`) confirm actions; untinted, tone on the icon only, at most
Expand Down Expand Up @@ -440,3 +445,20 @@ Rules that the templates above don't imply.
their own icon. The rail and bottom nav show a dot on Reports while any report is open.
- **Account**: permissions are grouped by area (Dashboard, Telegram, WhatsApp, group labels, Microsoft 365, Web) with
the service icon.
- **People**: one record per person whether or not they have an account; Overview, Member register, Admins, Teams,
Census and Linking all open the same record (`/dashboard/people/$personId`). Account status always shows a badge,
Not linked included. Mobile list cards replace unlabelled column values with one line of role chips or status
badges in the name cell. Dialogs that need fields and are destructive (End admin role, End membership, I'm leaving)
use `FormDialog` with `submitTone="danger"`; plain confirmations stay `ConfirmDialog`.
- **Member register**: status segments scope the table; Number leads on desktop, the name on mobile. Add member from a
record opens the register's dialog via `?add=P-…`. Export confirms before downloading.
- **Admins**: Groups, Capo Admin of reference, Member and Census never hide; the rest follow the table width. The four
quick filters scroll sideways on phones.
- **Teams**: index cards then all members; a Team Lead without `people:read` sees only their team, as the section
itself. Team-field columns come from `TEAMS` in `people.contract.ts`.
- **Census**: answer segments become a filter menu on phones. The import wizard skips steps with nothing to settle and
saves nothing before Confirm. Reminders report sent and skipped counts after sending.
- **Linking**: Needs review is a queue (its count sits next to the section like Reports › Open); each review kind has
its own resolutions and a required reason.
- **Capo Admin** (Telegram) and **My area** (Account): self-service pages that open with `people:self` and a role on
the person's own record; no personal emails of others, only phone, Telegram and PoliNetwork addresses.
29 changes: 29 additions & 0 deletions docs/people/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# People area: handoff

Everything an agent needs to build, or continue building, the People area of the dashboard.

| File | What it is |
| ------------------------------ | ---------------------------------------------------------------------------------------- |
| [`brief.md`](brief.md) | The original brief, verbatim |
| [`decisions.md`](decisions.md) | What was settled after the brief: owner decisions, navigation, per-page rules, roles |
| [`contract.md`](contract.md) | The `people.*` backend contract the dashboard codes against |
| [`plan.md`](plan.md) | Phases, checklist and progress log: **start here to know what is done and what is next** |
| [`prototype/`](prototype/) | The clickable prototype's source (structure and copy reference only) |

## Continuing the work

1. Read `CLAUDE.md` and `docs/design.md`, then `decisions.md`, `contract.md` and `plan.md` in that order.
2. Check out branch `claude/eager-mayer-bsom7v` and read the open pull request and its latest commits.
3. Pick the first unchecked item in `plan.md`. Commit small and often; tick the box and add a progress-log line in the
commit that finishes an item.
4. Run `PORT=1xxxx AGENT_MODE=true pnpm dev` to see the pages with sample data; set `AGENT_PERMISSIONS` and
`AGENT_PERSON` (once phase 1 adds them) to preview restricted roles.

## The prototype has visual errors

The prototype fixes what each page shows and does, not how it measures. Its spacing, paddings, column widths and some
components are hand-made and wrong in places. Do not copy its CSS or markup: build from the repo's shell and primitives
so spacing and behaviour match the rest of the dashboard, and fix anything that looks off. See "Prototype is a
reference" in `decisions.md` for the known defects.

Prototype canvas (owner only, all pages in desktop/mobile × light/dark): <https://claude.ai/artifact/7rwGssnepqJ2nWuLvGLrjT>
Loading
Loading