Every.org processes millions of dollars in donations for thousands of nonprofits. Once in a blue moon, a donation has to be processed manually. Using any tools and languages at your disposal, your task is to build a simple internal tool for processing and managing donations.
- Node.js 22 or later
- npm 10 or later (bundled with Node 22)
npm installnpm run devThis starts both servers concurrently:
| Service | URL |
|---|---|
| API | http://localhost:3001 |
| Dashboard | http://localhost:5173 |
The SQLite database (api/donations.db) is not committed to the repo. On first run it is created automatically and seeded with the eight sample donations from sample.json — no setup required.
To reset and re-seed (wipes all data and restores the sample donations on next start):
npm run db:reset
npm run devTo start with an empty database (useful for testing POST /donations from scratch):
SEED=false npm run devA full stack application with:
- A backend API that ingests and processes donations
- An internal dashboard UI for viewing and updating donation statuses
As a reminder, you have 2 hours to complete this, with a grace period of 1 additional hour. AI use is allowed and encouraged. Best of luck!
Use the following TypeScript interfaces as your data model:
type PaymentMethod = "cc" | "ach" | "crypto" | "venmo";
type DonationStatus = "new" | "pending" | "success" | "failure";
interface Donation {
uuid: string; // unique identifier
amount: number; // in cents, e.g. 5000 = $50.00
currency: "USD";
paymentMethod: PaymentMethod;
nonprofitId: string;
donorId: string;
status: DonationStatus;
createdAt: string; // ISO datetime string
updatedAt: string; // ISO datetime string
}Request body:
{
uuid: string;
amount: number;
currency: "USD";
paymentMethod: PaymentMethod;
nonprofitId: string;
donorId: string;
status: DonationStatus;
createdAt: string;
}Responses:
- 201 : donation successfully created
- 409 : conflict (see idempotency note in Backend Requirements)
- 400 : invalid payload
Response:
{
donations: Donation[];
}Responses:
- 200 : donation found
- 404 : donation not found
Request body:
{
status: DonationStatus;
}Responses:
- 200 : status successfully updated
- 409 : conflict (see idempotency note in Backend Requirements)
- 422 : invalid status transition
- 404 : donation not found
Valid transitions are:
new → pending
pending → success
pending → failure
Any other transition must be rejected with a 422 and a clear error message. For example:
- new → success ❌
- failure → pending ❌
- success → failure ❌
Seed your database or in-memory store with sample donations
Original brief: At minimum, a PATCH request where the target status is identical to the current status should return a 409. Beyond that, the interpretation of what constitutes a duplicate status update request is intentionally underspecified. Choose an approach that makes sense to you and document your reasoning in the README.
For POST: 409 if a donation with the same UUID already exists.
For PATCH: 409 if the requested status equals the current status (the required minimum). A retry with a valid forward transition (e.g. retrying new → pending after network failure) succeeds if the transition is still valid, or 409 if the transition already applied.
Build a dashboard that allows internal users to:
- View all donations with their current status, amount, payment method, and nonprofit
- Update the status of a donation using the valid transitions above The UI requirements are intentionally minimal. Use your own judgment to decide what an operator would actually need and document any product decisions you made in your README.
- Invalid transitions are not offered to the user (e.g. a success donation has no further actions)
- Errors from the API are surfaced clearly
- Amounts are displayed in dollars, not cents
The UI does not need to be polished; clarity and correctness matter more than aesthetics.
If you finish early, consider adding one of the following or something of your own choosing:
- Filtering donations by status or payment method
- A summary view (e.g. total donated by payment method, success/failure rate)
- Webhook simulation: emit an event when a donation reaches success or failure
- Anything else you think makes this more production-ready