Skip to content

Member Directory — RBAC field visibility design ​

Companion ADR: ADR 0032(revised 2026-06-22 — corrected member-detail model)

Status: Accepted 2026-06-22 (revised 2026-06-22)


Purpose ​

Defines the visual design and field layout for the member detail page (/app/members/:id) and the member management screen (/app/members/:id/manage). This is the canonical spec for MemberDetailPage.tsx, MemberManagePage.tsx, the route additions to App.tsx, and the MemberDetailView API response shape from GET /members/:id.


Corrected member-detail model (owner, 2026-06-22) ​

The detail screen (/app/members/:id) is the same for every role that can view it. There are no inline "extra" fields that appear for admin or ministry leaders. Role-specific management capabilities are accessed via a "Manage" button that navigates to the separate management screen (/app/members/:id/manage).


Detail screen — all roles ​

Adult member fields (everyone who can view the member sees the same set) ​

SectionFields
HeaderAvatar (initials or photo), display name, role label, family group name
PersonalBirthday (month/day only), anniversary (month/day, if married and provided)
ContactPhone number, email address, address
BioBio / personal note
Manage buttonShown only if canManage === true (admin and ministry_leader callers)

Actions: None — read-only for all roles on the detail screen.

Child member fields (birthday only) ​

Children (account type child) show a minimal detail card:

SectionFields
HeaderAvatar (initials), display name, "Youth" role badge, family group name
PersonalBirthday (month/day only)
Manage buttonShown only if canManage === true

No contact information is shown for child accounts to any viewer.

Access rules ​

  • Any authenticated member can view an adult member's detail screen.
  • Peer members (member role) cannot view a child's detail screen — GET /members/:childId returns 403 for peer callers. Children visible only to admin, ministry_leader, and the child's own family head.
  • Admin and ministry_leader see a "Manage" button below the profile header (above the field sections). The button navigates to the management screen.

Management screen — admin (/app/members/:id/manage, admin role) ​

The management screen is a separate page, not an overlay on the detail screen.

SectionContent
Member summaryName, role, status badge (active / pending / suspended)
Profile editEdit fields: display name, phone, address, birthday, anniversary, bio
Account statusChange status: Suspend (behind confirmation), Reactivate
Role"Change role" → links to /app/admin/roles with this member pre-selected
Audit"View audit log" → links to /app/admin/audit-log?memberId=:id

API: PUT /members/:id for profile/status/role fields. All operations are admin-only; 403 for callers outside admin.


Management screen — minister (/app/members/:id/manage, ministry_leader role) ​

SectionContent
Member summaryName, role, family group, account type
Pastoral contextFamily members list; account type (Primary / Spouse / Child)
ApprovalsLink to Approval Queue for any pending actions related to this member

Actions: No write access to profile fields, status, or role. Read-only pastoral overview.


Screen mockups ​

FileDescription
screens/member-detail-adult.htmlDetail screen for an adult member — all roles see this
screens/member-detail-kid.htmlDetail screen for a child member — birthday only; access-restricted
screens/member-manage-admin.htmlAdmin management screen — edit, suspend, role, audit
screens/member-manage-minister.htmlMinister pastoral management screen — read-only pastoral view

Previous mockups member-detail-member-view.html, member-detail-minister-view.html, and member-detail-admin-view.html are superseded by the four files above.


Members list ​

The GET /members list page (/app/members):

  • Sorted alphabetically by last name (A → Z)
  • Members of the same family grouped together under their family name
  • Search bar (?q= parameter)
  • Clicking a member navigates to /app/members/:id (replaces the current toggleSelected panel)
  • Children excluded from peer-member view; visible to admin and ministry_leader

Route additions required in App.tsx ​

tsx
<Route path="members" element={<MembersPage />} />
<Route path="members/:id" element={<MemberDetailPage />} />
<Route path="members/:id/manage" element={<MemberManagePage />} />

MemberDetailPage.tsx calls GET /members/:id which returns MemberDetailView (see ADR 0032 §API contract). The component renders adult or kid fields based on accountType. It shows the "Manage" button when canManage === true.

MemberManagePage.tsx calls GET /members/:id/manage (returns 403 for non-admin/non-minister) and PUT /members/:id for mutations.


API type &ZeroWidthSpace;

typescript
// Returned for all authenticated callers viewing a member
type MemberDetailView = {
  id: string;
  displayName: string;
  firstName: string;
  lastName: string;
  role: AppRole;
  roleLabel: string;
  familyName: string;
  relationship: 'primary' | 'spouse' | 'child';
  accountType: 'primary' | 'spouse' | 'child';
  // Adult-only fields (absent for child accounts):
  birthdayMonthDay?: string;   // "March 12"
  anniversary?: string;        // "June 8"
  phone?: string;
  email?: string;
  address?: { street: string; city: string; state: string; zip: string };
  bio?: string;
  // Manage affordance:
  canManage: boolean;          // true for admin and ministry_leader callers
};

ADO items &ZeroWidthSpace;

ItemAB#Type
Member directory detail + management views (corrected model)AB#3693User Story (re-scoped)
Add /app/members/:id route + MemberDetailPage.tsxTask under AB#3693Task
Implement GET /members/:id unified response with canManageTask under AB#3693Task
Add /app/members/:id/manage route + MemberManagePage.tsx (admin + minister)Task under AB#3693Task
Add last-name sort + family grouping + navigate-to-detail in MembersPage.tsxTask under AB#3693Task
Register member mockups as DesignSync cardsTask under AB#3693Task
Export member directory to Excel/CSVRoadmap story, Priority 4User Story

Heritage Community Hub — Internal. Access restricted via Cloudflare Access + Entra ID.