Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions .forge/features/admin-member-dashboard/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ manage current dues status, and remove invalid member accounts.
identity/system-owned values.
- Editors can click a member's Paid or Unpaid status directly to toggle it, or
use the same controls from the full member view.
- Editors can enable or pause member-initiated dues payments from a compact
control in the page header. Read-only administrators can see the current
state but cannot change it.
- Editors can delete a member after a clear destructive-action confirmation.
- Member deletion requires typing `I am absolutely sure` and removes the Member
profile without deleting the Blade user or unrelated hacker/application data.
Expand Down Expand Up @@ -92,6 +95,7 @@ manage current dues status, and remove invalid member accounts.
- Server and page gates for member administration.
- Search, compound filtering, sorting, pagination, and page-size choices.
- Member detail, editing, deletion, and individual current-dues controls.
- Global member-payment availability control.
- Read-only member enrichment from retained event attendance, Discord archive,
employment, Guild profile, role assignment, and dues-history data.
- Filtered CSV export across all matching pages.
Expand All @@ -116,6 +120,8 @@ manage current dues status, and remove invalid member accounts.
linked to the signed-in user.
- `Current dues status`: Whether an active dues record currently counts for the
member under the academic-school-year rules.
- `Payment availability`: Whether unpaid members may start a new Stripe dues
payment. It does not change any member's paid status or history.
- `Revoke dues`: Mark every active row that would keep the member effectively
paid as inactive while preserving it for future history.
- `Mass invalidate dues`: Mark the dues records covered by the officer action
Expand All @@ -133,6 +139,8 @@ manage current dues status, and remove invalid member accounts.
- An editor can mark an unpaid member paid for the payable academic school year
and can click the status to revoke every record that would keep the member
paid without deleting history.
- An editor can pause or enable member-initiated payments; a read-only member
admin can see but cannot change that state.
- Search covers name, email, Discord username, and company.
- Search also covers school and tolerates minor search imprecision.
- Filters can be combined and cleared, and query state is reflected in the URL.
Expand Down
31 changes: 19 additions & 12 deletions .forge/features/admin-member-dashboard/srd.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ reusable inputs live in `@forge/validators`.
redirected to sign-in by the page.
- `READ_MEMBERS`, `EDIT_MEMBERS`, or `IS_OFFICER` may list/filter members and
read a member detail.
- `EDIT_MEMBERS` or `IS_OFFICER` may update/delete members and change current
dues status.
- `EDIT_MEMBERS` or `IS_OFFICER` may update/delete members, change current dues
status, and control member-initiated payment availability.
- `IS_OFFICER` remains the global override implemented by `controlPerms`.
- `READ_CLUB_DATA` alone does not grant access to member PII.
- Client-side visibility is UX only; every query and mutation enforces policy
Expand Down Expand Up @@ -59,30 +59,35 @@ reusable inputs live in `@forge/validators`.
typed boolean map.
- `roles.hasPermission`: permission-aware query accepting a validated `or` or
`and` expression and returning a boolean for navigation/page gates.
- `member.getAdminMembers`: requires read-member access and returns rows,
- `memberAdmin.getAdminMembers`: requires read-member access and returns rows,
total count, page count, normalized page, and available facet values/counts.
- `member.getAdminMember`: requires read-member access and returns one full
- `memberAdmin.getAdminMember`: requires read-member access and returns one full
member record with derived current dues status plus retained dues history,
event check-ins and operator attribution, event totals, normalized
employment/company/location history, Guild location, linked roles, and
archived Discord engagement.
- `member.updateAdminMember`: requires edit-member access, validates a member
- `memberAdmin.updateAdminMember`: requires edit-member access, validates a member
ID plus member profile values, updates the selected member rather than the
caller, and keeps the code-owned signup response consistent.
- `member.deleteAdminMember`: requires edit-member access and deletes the
- `memberAdmin.deleteAdminMember`: requires edit-member access and deletes the
selected Member row, signup response, dues rows, and unreferenced member
uploads. It retains the User, roles, permissions, sessions, and Hacker data.
- `member.setAdminDuesStatus`: requires edit-member access. Marking paid creates
- `memberAdmin.setAdminDuesStatus`: requires edit-member access. Marking paid creates
a manual active record for the payable year with the configured dues amount
and no Stripe intent, or reactivates an existing payable-year record without
rewriting its payment metadata. Revoking repeatedly resolves effective rows
and marks them inactive until the member is unpaid; it does not delete
history.
- `member.invalidateEffectiveDues`: requires officer access and, in one
- `memberAdmin.getDuesPaymentConfiguration`: requires read-member access and
returns the persisted global payment-availability setting.
- `memberAdmin.setDuesPaymentsEnabled`: requires edit-member access, upserts the
singleton setting transactionally, and records the before/after value in the
durable admin audit log.
- `memberAdmin.invalidateEffectiveDues`: requires officer access and, in one
transaction, repeatedly resolves and invalidates rows until every affected
member is unpaid. The returned count is distinct affected members rather than
modified rows.
- `member.exportAdminMembers`: requires read-member access and returns an
- `memberAdmin.exportAdminMembers`: requires read-member access and returns an
escaped CSV for every result matching the current filters and fuzzy search.
- Admin profile-picture and resume procedures resolve the target User from the
Member UUID, preserve object-prefix ownership, and require edit-member access.
Expand All @@ -105,9 +110,11 @@ reusable inputs live in `@forge/validators`.

## Data / migration / compatibility

- No schema migration is required. Existing `Roles`, `Permissions`, `Member`,
`FormResponse`, `DuesPayment`, `Event`, `EventAttendee`, `Company`,
`Employment`, and Discord archive rows remain authoritative.
- Existing `Roles`, `Permissions`, `Member`, `FormResponse`, `DuesPayment`,
`Event`, `EventAttendee`, `Company`, `Employment`, and Discord archive rows
remain authoritative for their existing domains.
- Add one `DuesConfiguration` singleton row keyed by `global`. It defaults to
`paymentsEnabled = false`; a missing row also reads as paused.
- Manual dues records use cents, the same configured price, academic-year
calculation, unique member/year rule, and active/stale semantics as Stripe
records.
Expand Down
10 changes: 8 additions & 2 deletions .forge/features/admin-member-dashboard/status.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ Current phase: Complete

## Decision log

- 2026-08-11: Human requested a persisted admin toggle on `/admin/members` to
replace the hardcoded dues-payment cutoff. Existing edit-member access owns
the mutation; readers can see the operational state without changing it.
- 2026-07-15: Human confirmed the feature bundle is complete; normalized all
artifact status fields to `Complete`.
- 2026-06-27: Work targets Blade and `@forge/api` on the
Expand Down Expand Up @@ -101,11 +104,11 @@ Current phase: Complete
destinations into one hamburger dropdown.
- [x] Anchor the responsive Edit member action to the dialog identity header.
- [x] Complete targeted and repository validation.
- [x] Add the persisted member-payment availability control and API contract.
- [x] Validate the payment-availability follow-up and review the admin UI.

## Validation / commands

- `git status --short --branch`: work remains on
`reforge/admin-member-dashboard`; no commit or merge was performed.
- `pnpm forge:feature admin-member-dashboard "Admin Member Dashboard"`: passed.
- Targeted package lint and typechecks pass for `@forge/api`, `@forge/blade`,
`@forge/consts`, `@forge/utils`, and `@forge/validators`.
Expand Down Expand Up @@ -175,6 +178,9 @@ Current phase: Complete
failures in current and legacy Blade. Repository formatting still reports the
unchanged `apps/guild/src/app/_components/globe-renderer.tsx`; all changed
files pass Prettier and `git diff --check`.
- 2026-08-11 payment-availability follow-up: API and Blade test suites passed;
the 6-scenario dues Playwright run passed; desktop and 390px mobile visual
review confirmed the header control remains compact and overflow-free.

## Links

Expand Down
40 changes: 31 additions & 9 deletions .forge/features/admin-member-dashboard/test-cases.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ Status: Complete

These cases cover effective permission evaluation, admin member access,
search/filter/pagination, enriched detail display, editing, deletion, and
current dues controls. Role configuration, bulk operations, club analytics,
and Discord role writes are excluded.
current dues controls, and global member-payment availability. Role
configuration, bulk operations, club analytics, and Discord role writes are
excluded.

## Test placement plan

Expand Down Expand Up @@ -177,6 +178,23 @@ Expected observations:
- Historical and unrelated active rows remain unchanged.
- The UI reports the affected-member count.

### TC-010: Editor manages target-owned files

Setup:

- A member has a profile picture and a resume; their Hacker may reference the
same resume.

Action:

- Preview, replace, and remove files from the member dialog.

Expected observations:

- Short-lived previews work and replacement objects belong to the target User.
- Superseded profile pictures are removed.
- A resume still referenced by Hacker is preserved.

### TC-011: Admin shell remains stable across member and admin routes

Setup:
Expand Down Expand Up @@ -245,22 +263,26 @@ Expected observations:
history render from the existing sources.
- Dates are human-readable and neither Member ID nor User ID is presented.

### TC-010: Editor manages target-owned files
### TC-014: Editor controls member payment availability

Setup:

- A member has a profile picture and a resume; their Hacker may reference the
same resume.
- An editor and a read-only member admin open `/admin/members` while member
payments are paused.

Action:

- Preview, replace, and remove files from the member dialog.
- The editor enables member payments and reloads `/admin/members`.
- The editor pauses member payments and reloads `/admin/members` again.
- While payments are paused, an unpaid member attempts to start checkout.

Expected observations:

- Short-lived previews work and replacement objects belong to the target User.
- Superseded profile pictures are removed.
- A resume still referenced by Hacker is preserved.
- After each reload, the control reflects the persisted state.
- The paused checkout request returns `PRECONDITION_FAILED`.
- Stripe is not called and no PaymentIntent is created for the rejected request.
- The read-only admin sees the current state without an interactive switch.
- Existing dues payment rows and paid statuses are unchanged.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

## Negative / regression cases

Expand Down
17 changes: 14 additions & 3 deletions .forge/features/member-dues-payment/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ dues payment history instead of treating each year's rollover as deleted data.
## Users / actors

- Signed-in members with completed member profiles.
- Future admins/officers who need dues history and current dues status for the
admin member dashboard.
- Admin member editors and officers who control whether self-service payments
are available.

## User-visible interface

Expand All @@ -26,6 +26,9 @@ dues payment history instead of treating each year's rollover as deleted data.
`/member/dashboard` should show an unpaid dues section with a clear action to
pay.
- The payment action should take the member to `/member/dues`.
- Admin member editors can enable or pause self-service dues payments from the
member administration page. While paused, unpaid members see that payments
are paused until further notice instead of an active payment action.
- `/member/dues` should be a full payment page so it can be linked from the
dashboard and future member/admin surfaces.
- `/member/dues` should use the current Blade design system and embedded Stripe
Expand Down Expand Up @@ -61,6 +64,8 @@ dues payment history instead of treating each year's rollover as deleted data.
- Let unpaid members reach a dedicated dues payment page.
- Let completed members pay `$25` dues for the current academic school year.
- Show safe loading, processing, success, and failure states around payment.
- Show a clear, neutral paused state while member payments are disabled,
without changing paid members' current dues status.
- Show a five-second redirect countdown and an immediate dashboard action after
successful payment.
- Preserve dues history for future alumni/admin surfaces.
Expand All @@ -77,7 +82,7 @@ dues payment history instead of treating each year's rollover as deleted data.
### Out of scope

- Member-visible dues history.
- Admin member dashboard UI.
- Admin member management beyond the payment-availability control.
- Admin manual dues payment, comp, revoke, or rollover controls.
- Refund handling.
- Coupon codes or discounts.
Expand All @@ -104,6 +109,8 @@ dues payment history instead of treating each year's rollover as deleted data.
- `Late-year warning`: The May 31 through July 31 warning that paying now only
covers the current school year and another dues payment will be needed in the
fall semester.
- `Payment availability`: The admin-controlled setting that determines whether
an unpaid member may start a new Stripe dues payment.

## Acceptance criteria

Expand All @@ -128,6 +135,10 @@ dues payment history instead of treating each year's rollover as deleted data.
- Between May 31 and July 31, `/member/dues` shows the late-year warning before
the member pays.
- Old dues records are not deleted as part of this member-facing payment flow.
- When an admin pauses member payments, unpaid members see that dues payments
are paused until further notice and cannot start checkout.
- When an admin enables member payments, the existing dues payment flow becomes
available immediately without changing paid status or academic-year logic.

## Open questions

Expand Down
Loading
Loading