Skip to content

feat(members): add aliases as the display name across the app - #50

Merged
parameshjava merged 2 commits into
mainfrom
feat/member-aliases
Aug 9, 2026
Merged

feat(members): add aliases as the display name across the app#50
parameshjava merged 2 commits into
mainfrom
feat/member-aliases

Conversation

@parameshjava

Copy link
Copy Markdown
Owner

Members get an optional short handle — "Paramesh", "Sandeep", "Bunny" — that replaces their full name wherever they're rendered: chart axes, ledger tables, poll results, meeting attendees and the Member × Month grid. The canonical members.name stays the record, and admins can search by either half when recording a loan or contribution.

⚠️ Before merging

Run scripts/prod/migrations/055_member_aliases.sql in the Supabase SQL Editor. Nothing works until it lands.

Schema (055)

members.alias + a 2–20 char format check (letters, digits and spaces only — no dots, underscores or hyphens) + a partial unique index on lower(alias).

Writes go through two SECURITY DEFINER functions rather than direct UPDATEs, because members is admin-write under RLS but a member has to be able to change their own alias:

Function Who
fn_set_member_alias(member, alias) admin, or the member themselves
fn_set_member_aliases(rows) admin-only bulk save, all-or-nothing

Two details worth a reviewer's eye:

  • The bulk function clears the batch before applying it. Two members swapping aliases in one save would otherwise trip the unique index mid-UPDATE — an expression index can't back a DEFERRABLE constraint, so the check can't be postponed to COMMIT.
  • Rows with no member_id are rejected up front. A single NULL makes the id not in (batch) collision check evaluate to NULL for every row, which would let a real collision through to a raw 23505 instead of a readable message.

The four read-side views gain the alias appended at the end of their select lists — create or replace view may only add columns after the existing ones, never re-order them.

UI

  • /admin/aliases — set the whole roster in one submit, each box pre-filled, live duplicate/format flagging.
  • Profile page — members change their own alias later; clearing it brings the full name back everywhere.
  • Pickers show Alias · Full Name and match on either half (filterBy added to PrDropdown/PrMultiSelect).

On the suggestions

The roster is written surname-first (Korrakuti Paramesh), so taking the first token proposed the family name for all 23 members. The suggester now takes the given name — normally the second token — and skips suffixes shared across the batch (Reddy, Kumar, Gupta) that six members would otherwise all want. GIVEN_NAME_OVERRIDES covers Prakash Policherla, the one entry stored the other way round with no signal in the string. Suggestions are proposals only; nothing is written until save.

Testing

385 unit tests pass (48 new), plus typecheck, lint and build.

Migrations 001→055 were replayed on a local Postgres with the real seed (23 members, 1,283 transactions) under Supabase's privilege model, so RLS is the gate:

  • non-admin's direct UPDATE members SET alias0 rows changed
  • that same member sets their own alias through the function ✅
  • editing someone else → "You can only change your own alias"; bulk → "Admin role required"
  • admin bulk save wrote all 23, zero case-insensitive duplicates
  • all four views return the alias against real data
  • re-running the whole chain is a no-op

Not verified: no live Supabase run — the keys in .env are placeholders — so this hasn't been exercised in a browser.

🤖 Generated with Claude Code

Members get an optional short handle — "Paramesh", "Sandeep", "Bunny" —
that replaces their full name wherever they are rendered: chart axes,
ledger tables, poll results, meeting attendees and the Member × Month
grid. The canonical members.name stays the record, and admins can still
search by either half when recording a loan or a contribution.

Schema (055): members.alias, a 2–20 char format check (letters, digits
and spaces only) and a partial unique index on lower(alias). Writes go
through two SECURITY DEFINER functions rather than direct UPDATEs,
because members is admin-write under RLS but a member has to be able to
change their own alias:

  * fn_set_member_alias  — admin, or the member themselves
  * fn_set_member_aliases — admin-only bulk save, all-or-nothing

The bulk function clears the batch before applying it, so two members
swapping aliases in one save doesn't trip the unique index mid-UPDATE —
an expression index can't back a DEFERRABLE constraint, so the check
can't be postponed to COMMIT. It also rejects rows with no member_id:
a single NULL makes the `id not in (batch)` collision check evaluate to
NULL for every row, which would let a real collision through to a raw
23505 instead of a sentence the admin can act on.

The four read-side views gain the alias appended at the END of their
select lists — create or replace view may only add columns after the
existing ones, never re-order them.

Admin screen at /admin/aliases sets the whole roster in one submit, with
each box pre-filled from the member's name. The roster is written
surname-first ("Korrakuti Paramesh"), so the suggester takes the given
name — normally the second token — and skips family suffixes shared
across the batch (Reddy, Kumar, Gupta) that six members would otherwise
all want. GIVEN_NAME_OVERRIDES covers the one entry stored the other way
round. Suggestions are proposals: nothing is written until save.

Members change their own alias later from their profile page.

Verified by replaying migrations 001→055 on a local Postgres with the
real seed (23 members, 1,283 transactions) and Supabase's privilege
model, so RLS is the gate: a non-admin's direct UPDATE changes 0 rows,
the same member sets their own alias through the function, and the
admin's bulk save writes all 23 with no case-insensitive duplicates.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
aits-fcf-tracker Ready Ready Preview Aug 9, 2026 6:54am

The suggestion was only a placeholder, so the admin had to click "Fill
empty with suggestions" before it became real text — an extra step on a
screen whose whole purpose is not typing 23 names by hand. Seed the draft
state from the suggestion instead, falling back to the saved alias where
one exists.

Pre-filled is still not saved: these are form values only, nothing
reaches the database until Save, and Reset drops every unsaved
suggestion. The counter now separates the two so the distinction is
visible — "12 of 23 members have a saved alias · 23 filled in below, not
saved yet".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@parameshjava
parameshjava merged commit a369ac4 into main Aug 9, 2026
3 checks passed
@parameshjava
parameshjava deleted the feat/member-aliases branch August 9, 2026 08:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant