Files
member-console/internal/identity/persons.sql.go
T
cgalo5758 257955c9d3 Add operator list-scale contract and People directory
Governed operator lists (organizations, grants, people, billing×4) gain
server-side search, status filters, and 50-row pages with true totals
from count(*) OVER(); state is URL-addressable, out-of-range pages
clamp,
and no-match is distinct from true-empty.

People is the eighth flat sidebar entry: /operator/persons lists persons
newest-joined first (excluding the reserved system person), rows linking
to the existing detail.

Billing gains an operator invoice detail at
/operator/billing/invoices/{invoiceID} reusing the member projection;
open invoices past due present as Overdue (derived, filterable, stored
status untouched); all four views lead with the linked organization and
mute object IDs.

Grants filter over the derived Live/Superseded/Inactive state, the SQL
HAVING predicate pinned to the Go derivation by test. Embedded lists
(org composite ledger, Tier changes) adopt the shared controls under
namespaced params with sibling-state-preserving URLs and scoped htmx
swaps that hold the viewport.

Review corrections: blocked ladder Delete renders disabled with tooltip
and mutations fire toasts; collapse triggers paint their open state;
sections use outside headings; plan topology drops the orphan-product
check; domains policy collapses behind a disclosure.
2026-08-24 03:58:18 -05:00

348 lines
9.9 KiB
Go

// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// source: persons.sql
package identity
import (
"context"
"database/sql"
"time"
"github.com/google/uuid"
)
const countActivePersons = `-- name: CountActivePersons :one
SELECT COUNT(*) FROM core.persons
WHERE status = 'active'
`
// 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.
func (q *Queries) CountActivePersons(ctx context.Context) (int64, error) {
row := q.db.QueryRowContext(ctx, countActivePersons)
var count int64
err := row.Scan(&count)
return count, err
}
const countPersonsJoinedLast30Days = `-- name: CountPersonsJoinedLast30Days :one
SELECT COUNT(*) FROM core.persons
WHERE status = 'active' AND created_at > now() - interval '30 days'
`
// 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.
func (q *Queries) CountPersonsJoinedLast30Days(ctx context.Context) (int64, error) {
row := q.db.QueryRowContext(ctx, countPersonsJoinedLast30Days)
var count int64
err := row.Scan(&count)
return count, err
}
const createPerson = `-- name: CreatePerson :one
INSERT INTO core.persons (user_id, display_name, primary_email, primary_email_verified)
VALUES ($1, $2, $3, $4)
RETURNING person_id, user_id, display_name, primary_email, primary_email_verified, status, created_at, updated_at
`
type CreatePersonParams struct {
UserID string `json:"user_id"`
DisplayName string `json:"display_name"`
PrimaryEmail string `json:"primary_email"`
PrimaryEmailVerified bool `json:"primary_email_verified"`
}
func (q *Queries) CreatePerson(ctx context.Context, arg CreatePersonParams) (Person, error) {
row := q.db.QueryRowContext(ctx, createPerson,
arg.UserID,
arg.DisplayName,
arg.PrimaryEmail,
arg.PrimaryEmailVerified,
)
var i Person
err := row.Scan(
&i.PersonID,
&i.UserID,
&i.DisplayName,
&i.PrimaryEmail,
&i.PrimaryEmailVerified,
&i.Status,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const ensureSystemPerson = `-- name: EnsureSystemPerson :exec
INSERT INTO core.persons (user_id, display_name, primary_email)
VALUES ($1, $2, $3)
ON CONFLICT (user_id) DO NOTHING
`
type EnsureSystemPersonParams struct {
UserID string `json:"user_id"`
DisplayName string `json:"display_name"`
PrimaryEmail string `json:"primary_email"`
}
// Idempotently insert the reserved system person that owns the System tenant
// (keyed on the unique user_id). Read back with GetPersonByUserID.
func (q *Queries) EnsureSystemPerson(ctx context.Context, arg EnsureSystemPersonParams) error {
_, err := q.db.ExecContext(ctx, ensureSystemPerson, arg.UserID, arg.DisplayName, arg.PrimaryEmail)
return err
}
const getPersonByID = `-- name: GetPersonByID :one
SELECT person_id, user_id, display_name, primary_email, primary_email_verified, status, created_at, updated_at FROM core.persons
WHERE person_id = $1
`
func (q *Queries) GetPersonByID(ctx context.Context, personID string) (Person, error) {
row := q.db.QueryRowContext(ctx, getPersonByID, personID)
var i Person
err := row.Scan(
&i.PersonID,
&i.UserID,
&i.DisplayName,
&i.PrimaryEmail,
&i.PrimaryEmailVerified,
&i.Status,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const getPersonByUserID = `-- name: GetPersonByUserID :one
SELECT person_id, user_id, display_name, primary_email, primary_email_verified, status, created_at, updated_at FROM core.persons
WHERE user_id = $1
`
func (q *Queries) GetPersonByUserID(ctx context.Context, userID string) (Person, error) {
row := q.db.QueryRowContext(ctx, getPersonByUserID, userID)
var i Person
err := row.Scan(
&i.PersonID,
&i.UserID,
&i.DisplayName,
&i.PrimaryEmail,
&i.PrimaryEmailVerified,
&i.Status,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const listPersons = `-- name: ListPersons :many
SELECT person_id, user_id, display_name, primary_email, primary_email_verified, status, created_at, updated_at FROM core.persons
ORDER BY display_name
`
func (q *Queries) ListPersons(ctx context.Context) ([]Person, error) {
rows, err := q.db.QueryContext(ctx, listPersons)
if err != nil {
return nil, err
}
defer rows.Close()
items := []Person{}
for rows.Next() {
var i Person
if err := rows.Scan(
&i.PersonID,
&i.UserID,
&i.DisplayName,
&i.PrimaryEmail,
&i.PrimaryEmailVerified,
&i.Status,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listPersonsPage = `-- name: ListPersonsPage :many
SELECT person_id, user_id, display_name, primary_email, primary_email_verified, status, created_at,
count(*) OVER() AS total_count
FROM core.persons
WHERE ($1::uuid IS NULL
OR person_id <> $1::uuid)
AND ($2::text IS NULL
OR display_name ILIKE '%' || $2::text || '%'
OR primary_email ILIKE '%' || $2::text || '%')
ORDER BY created_at DESC
LIMIT $4::int OFFSET $3::int
`
type ListPersonsPageParams struct {
ExcludePersonID uuid.NullUUID `json:"exclude_person_id"`
Q sql.NullString `json:"q"`
PageOffset int32 `json:"page_offset"`
PageLimit int32 `json:"page_limit"`
}
type ListPersonsPageRow struct {
PersonID string `json:"person_id"`
UserID string `json:"user_id"`
DisplayName string `json:"display_name"`
PrimaryEmail string `json:"primary_email"`
PrimaryEmailVerified bool `json:"primary_email_verified"`
Status string `json:"status"`
CreatedAt time.Time `json:"created_at"`
TotalCount int64 `json:"total_count"`
}
// 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.
func (q *Queries) ListPersonsPage(ctx context.Context, arg ListPersonsPageParams) ([]ListPersonsPageRow, error) {
rows, err := q.db.QueryContext(ctx, listPersonsPage,
arg.ExcludePersonID,
arg.Q,
arg.PageOffset,
arg.PageLimit,
)
if err != nil {
return nil, err
}
defer rows.Close()
items := []ListPersonsPageRow{}
for rows.Next() {
var i ListPersonsPageRow
if err := rows.Scan(
&i.PersonID,
&i.UserID,
&i.DisplayName,
&i.PrimaryEmail,
&i.PrimaryEmailVerified,
&i.Status,
&i.CreatedAt,
&i.TotalCount,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const lookupPersons = `-- name: LookupPersons :many
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
`
type LookupPersonsRow struct {
PersonID string `json:"person_id"`
DisplayName string `json:"display_name"`
PrimaryEmail string `json:"primary_email"`
Status string `json:"status"`
}
// 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.
func (q *Queries) LookupPersons(ctx context.Context, primaryEmail string) ([]LookupPersonsRow, error) {
rows, err := q.db.QueryContext(ctx, lookupPersons, primaryEmail)
if err != nil {
return nil, err
}
defer rows.Close()
items := []LookupPersonsRow{}
for rows.Next() {
var i LookupPersonsRow
if err := rows.Scan(
&i.PersonID,
&i.DisplayName,
&i.PrimaryEmail,
&i.Status,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const updatePerson = `-- name: UpdatePerson :one
UPDATE core.persons
SET display_name = $1, primary_email = $2, primary_email_verified = $3
WHERE person_id = $4
RETURNING person_id, user_id, display_name, primary_email, primary_email_verified, status, created_at, updated_at
`
type UpdatePersonParams struct {
DisplayName string `json:"display_name"`
PrimaryEmail string `json:"primary_email"`
PrimaryEmailVerified bool `json:"primary_email_verified"`
PersonID string `json:"person_id"`
}
func (q *Queries) UpdatePerson(ctx context.Context, arg UpdatePersonParams) (Person, error) {
row := q.db.QueryRowContext(ctx, updatePerson,
arg.DisplayName,
arg.PrimaryEmail,
arg.PrimaryEmailVerified,
arg.PersonID,
)
var i Person
err := row.Scan(
&i.PersonID,
&i.UserID,
&i.DisplayName,
&i.PrimaryEmail,
&i.PrimaryEmailVerified,
&i.Status,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}