01
What it does
A donor engagement and event management CRM built for the BC Cancer Foundation industry capstone. Fundraisers can run their donor directory, create and track events, score which donors match an event invitation, and review a full audit trail of every administrative action.
02
Matching pipeline
Each donor and event pairing scores against six weighted factors. Weights are configurable per event type so a small in-person gala can prioritise different signals than a broad mail campaign:
| Factor | Signal |
|---|---|
| Interest alignment | Overlap between donor causes and event theme |
| Geographic proximity | Postal code radius |
| Donation history | Total contributions and channel mix |
| Recency | Months since last contribution |
| Engagement level | Email, event attendance, response rate |
| Capacity | Giving tier signal |
The ranked donor list comes back with a per-factor breakdown rather than just a final score, so a fundraiser can see exactly why a donor ranked where they did and adjust weights with intuition rather than guesswork.
03
Schema and access patterns
TypeORM models map cleanly to a normalised relational schema (User, Donor, Event, EventDonor, AuditLog) without N+1 fetches. Indexes target the four hottest access patterns: donor lookup by ID, event roster, donations by donor over a time window, and matches by event.
Local development uses SQLite for fast boots. Production deploys against PostgreSQL or MySQL via TypeORM connection options, so the schema is portable across both engines.
04
Role-based access control
Three roles enforced consistently across the stack:
- Admin covers the full administrative surface including audit-log access
- Manager can mutate events and donors, no audit-log access
- Staff get read-only directory and analytics
NestJS guards run on every controller. The frontend also gates routes by role so users do not see UI for actions they cannot perform.
05
Audit trail
Every administrative mutation writes to audit_logs with:
- Actor user ID and role
- Entity type and entity ID
- Action verb
- Before and after state as JSONB diffs
- Sanitised request payload (PII redacted)
- IP address and timestamp
The log is queryable through the admin API with filters by date range, actor, action, or entity type. CSV and JSON exports drop straight into compliance reporting.
GET /admin/audit-logs?action=donor.update
&entityType=Donor
&startDate=2025-01-01
&endDate=2025-12-31
&page=1&limit=5006
Analytics
Dashboard endpoints surface donor segmentation, geographic distribution, gift velocity, and engagement channel mix. The same queries that power the dashboards are exposed at /analytics/* so the data is reusable in scheduled exports or downstream tools.
07
Stack
| Layer | Stack |
|---|---|
| Frontend | React 18, Vite, Tailwind, shadcn/ui |
| Backend | NestJS, TypeScript, TypeORM |
| Database | PostgreSQL (production), SQLite (local) |
| Auth | Session cookies, NestJS guards |
| API docs | Swagger at /api/docs |
Seeder populates 500 donors, sample events, and audit history on first boot so the app is browsable immediately after npm start. CI runs the backend and frontend test suites on every push via GitHub Actions.