Files
member-console/internal/identity/queries/persons.sql
T
cgalo5758 12f1d3fc00 Fix the four Slice 3 walk findings and page every operator list
Archives openspec change slice3-walk-fixes and syncs its five delta
specs (fedwiki-sites, entitlements, operator-panel-navigation,
operator-list-scale, ui-quality-gate).

- FedWiki site usage is read from active site rows in both quota
  readers; the reservation counter converges on the rows: raise-only
  after farm sync and inside the create quota check, exact at boot.
  The understated production counters repair on the first boot.
- The People tile caption excludes the reserved system person through
  the same query parameter the directory uses.
- The operator Domains live-claims list is a governed list: pages of
  50, true total, search over root name and organization, a
  pending/active facet.
- New lint rule table-without-list-controls refuses an unpaged
  page-body table unless it carries a list-scale exempt marker with a
  reason; six curated or detail tables carry one. Its first run caught
  the operator FedWiki sites list, which is now governed the same way.
- Entitlement-set rule copy: "Per unit", "Multiplied by the quantity
  purchased or granted."
2026-09-12 01:16:17 -05:00

93 lines
3.8 KiB
SQL

-- name: CreatePerson :one
INSERT INTO core.persons (user_id, display_name, primary_email, primary_email_verified)
VALUES ($1, $2, $3, $4)
RETURNING *;
-- name: EnsureSystemPerson :exec
-- Idempotently insert the reserved system person that owns the System tenant
-- (keyed on the unique user_id). Read back with GetPersonByUserID.
INSERT INTO core.persons (user_id, display_name, primary_email)
VALUES ($1, $2, $3)
ON CONFLICT (user_id) DO NOTHING;
-- name: GetPersonByUserID :one
SELECT * FROM core.persons
WHERE user_id = $1;
-- name: GetPersonByID :one
SELECT * FROM core.persons
WHERE person_id = $1;
-- name: UpdatePerson :one
UPDATE core.persons
SET display_name = $1, primary_email = $2, primary_email_verified = $3
WHERE person_id = $4
RETURNING *;
-- name: ListPersons :many
SELECT * FROM core.persons
ORDER BY display_name;
-- name: LookupPersons :many
-- Resolves a lookup term to one or more active persons for the operator
-- landing-surface lookup affordance. Exact email match wins (deterministic
-- — operators using email expect precision); otherwise falls through to
-- case-insensitive substring match against display_name. Status='active'
-- filters out removed accounts. LIMIT 20 caps the disambiguation list
-- without paging.
SELECT person_id, display_name, primary_email, status
FROM core.persons
WHERE status = 'active'
AND (primary_email = $1 OR display_name ILIKE '%' || $1 || '%')
ORDER BY
CASE WHEN primary_email = $1 THEN 0 ELSE 1 END,
display_name
LIMIT 20;
-- name: CountActivePersons :one
-- Operator overview tile: how many people the deployment currently serves.
-- Mirrors LookupPersons' status='active' filter so the headline number and
-- the lookup affordance above it are counting the same population.
SELECT COUNT(*) FROM core.persons
WHERE status = 'active';
-- name: CountPersonsJoinedLast30Days :one
-- The People tile's caption: velocity rather than a restatement of the
-- census. Same status filter as CountActivePersons so both numbers
-- describe the same population.
-- sqlc.narg(exclude_person_id): the reserved system person, in the shape
-- ListPersonsPage uses, so the caption cannot drift from the headline and
-- the directory total by counting it differently. NULL excludes nothing.
SELECT COUNT(*) FROM core.persons
WHERE status = 'active' AND created_at > now() - interval '30 days'
AND (sqlc.narg(exclude_person_id)::uuid IS NULL
OR person_id <> sqlc.narg(exclude_person_id)::uuid);
-- name: ListPersonsPage :many
-- People directory operator surface (operator-list-scale UX-4 /
-- operator-people-directory D6): the paged, searched query feeding
-- GET /operator/persons. Ordered newest-joined first so "who joined this
-- week?" is answered by page one.
--
-- sqlc.narg(q): NULL matches every row; set, a case-insensitive substring
-- match against display_name or primary_email (operator-list-scale:
-- "carries a scoped server-side search" -- "people: display name and
-- email").
-- sqlc.narg(exclude_person_id): the reserved system person (maintainer
-- decision 2026-08-23: infrastructure, never "joined", excluded from the
-- directory and its totals; it stays visible as the System tenant's owner
-- in the organizations context). NULL excludes nothing.
-- count(*) OVER() carries the true total for the filtered set alongside
-- the page's rows (design D2), so the stated total never reflects only
-- the page.
SELECT person_id, user_id, display_name, primary_email, primary_email_verified, status, created_at,
count(*) OVER() AS total_count
FROM core.persons
WHERE (sqlc.narg(exclude_person_id)::uuid IS NULL
OR person_id <> sqlc.narg(exclude_person_id)::uuid)
AND (sqlc.narg(q)::text IS NULL
OR display_name ILIKE '%' || sqlc.narg(q)::text || '%'
OR primary_email ILIKE '%' || sqlc.narg(q)::text || '%')
ORDER BY created_at DESC
LIMIT sqlc.arg(page_limit)::int OFFSET sqlc.arg(page_offset)::int;