Skip to content

feature/decouple donations from membership#100

Merged
petehayes merged 8 commits into
mainfrom
feature/decouple-donations-from-membership
Mar 15, 2026
Merged

feature/decouple donations from membership#100
petehayes merged 8 commits into
mainfrom
feature/decouple-donations-from-membership

Conversation

@petehayes

Copy link
Copy Markdown
Contributor
  • feat: decouple donations from org membership
  • test: add E2E tests for donation/membership decoupling

petehayes and others added 2 commits March 14, 2026 21:25
Any authenticated platform user can now donate to any verified
organization without becoming an org member. Membership is only
granted via explicit invitation or admin-approved request.

Changes:
- Add withPlatformAuth helper (auth without membership check)
- Remove auto-join logic from /my-donations/new page
- Update donation API routes to use withPlatformAuth
- Update donation sub-routes ([id], receipt) for non-member access
- Update public org page CTA: separate Donate and Request Membership
- Update RBAC tests for new auth patterns

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 26 E2E tests across 3 test files covering:

Public tests (donation-access.public.spec.ts):
- Anonymous users see 'Sign In to Get Started' CTA on public org page
- My-donations pages redirect unauthenticated users to login
- Public donate page is accessible without auth

Authenticated admin tests (donation-access.spec.ts):
- My-donations pages render for authenticated members
- Donation form works with campaign pre-selection
- No auto-join messaging on donation page
- Public org page shows 'Dashboard' CTA for members

Non-member donor tests (donor-nonmember.donor.spec.ts):
- Non-member can access my-donations pages (no Access Denied)
- Non-member sees 'Donate Now' + 'Request Membership' CTAs
- Non-member cannot access admin routes (dashboard, accounts, etc.)
- Visiting donation pages does NOT create org membership

Infrastructure changes:
- Add 'donor' Playwright project using Clerk emailAddress sign-in
- Seed script creates donor user (authenticated, non-member)
- Fix auth-setup testMatch regex to avoid matching donor setup

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@vercel

vercel Bot commented Mar 14, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
transparency Ready Ready Preview, Comment Mar 15, 2026 1:39pm

@@ -0,0 +1,77 @@
import { test, expect, Page } from '@playwright/test';
The POST /api/organizations/[slug]/access-requests endpoint was using
withOrgAuth which requires existing org membership — contradicting its
purpose of letting non-members request access.

Changed to withPlatformAuth which only requires authentication, allowing
any logged-in user to submit a membership request.

Also adds E2E test verifying the full membership request flow works for
non-member donors, and cleans up AccessRequest records in seed script.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
petehayes and others added 2 commits March 15, 2026 12:24
When a user was approved, then removed from the org, they couldn't
re-request membership because old resolved AccessRequest records
blocked the new insert.

Fix: clean up resolved (APPROVED/DENIED) access requests before
creating a new one, allowing the re-request flow to work.

Also adds:
- E2E test for the re-request flow (request → cleanup → re-request)
- E2E cleanup endpoint for test state management
- Fix unit test mock for deleteMany

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…eanup on removal

- Remove /api/e2e/cleanup-donor route and middleware entry — test-only
  code should not ship to production
- E2E tests now use direct DB access via e2e/helpers/db.ts instead
- Denied users can no longer re-request membership (must contact admin)
- When an admin removes a user, their access requests are cleaned up
  so they can re-request if desired
- Add unit test for denied request rejection

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace fragile string-matching error handling with typed ServiceError
that carries HTTP status codes. Business logic violations (denied
request, duplicate request, etc.) are now returned as proper HTTP
responses with user-friendly messages instead of logging as system
errors and returning generic 500s.

- Add ServiceError class in src/lib/errors/service-error.ts
- Service layer throws ServiceError with appropriate status codes
- API route catches ServiceError and returns message + status directly
- UI shows contextual 'Request Previously Denied' title for denied cases
- Only true unexpected errors hit console.error and return 500

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ServiceError now carries both a user-facing title and description so
the API response includes both fields and the UI can display them
directly without string-matching error messages.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Since denied users are now blocked from re-requesting, admins need a
way to reverse a denial. Changes:

- Service: approveAccessRequest accepts both PENDING and DENIED status
- Service: new getDeniedRequests() function
- API: GET returns both pending and denied requests
- UI: collapsible 'previously denied' section below pending requests
  with an Approve button — low-profile since it's not a typical action
- Unit test for approving a denied request

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@petehayes petehayes merged commit b14dcef into main Mar 15, 2026
11 checks passed
@petehayes petehayes deleted the feature/decouple-donations-from-membership branch March 15, 2026 14:28
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