Bundles get a kind: exact (as before), mix (any setSize of the qualifying tickets for a set price), free (cheapest freeCount in each set free) and group (setSize or more qualifying tickets at a per-ticket price or % off, applied once). The supervisor page has a type picker, buyers see a one-line description of each deal, and the Complete bundle cards handle every type. Demo: Family Fun Day adds a Lunch for four deal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
192 lines
9.5 KiB
Markdown
192 lines
9.5 KiB
Markdown
# Demo data
|
||
|
||
`npm run set-demo` replaces everything in the database with a realistic set of demo data,
|
||
for showing the platform to other churches. It stages six upcoming events with
|
||
registrations, payments, tickets and people, and ten completed events from the past year
|
||
with door sales, check-ins, feedback and cashups. That way every dashboard, report,
|
||
check-in and cashup screen has something to show.
|
||
|
||
> **This deletes real data.** Run it only against a database you're happy to wipe, such
|
||
> as a dedicated demo deployment. It is not something to run on a live church site.
|
||
|
||
---
|
||
|
||
## Running it
|
||
|
||
```bash
|
||
cd backend
|
||
npm run set-demo # shows the database it will wipe and asks you to type "demo"
|
||
npm run set-demo -- --yes # no prompt (for scripts)
|
||
```
|
||
|
||
The script uses `DATABASE_URL` from `backend/.env`. To point it at another database
|
||
for one run, set it in the environment first. A value already in the environment wins
|
||
over `.env`:
|
||
|
||
```bash
|
||
DATABASE_URL="postgresql://user:pass@host:5432/hope_demo" npm run set-demo
|
||
```
|
||
|
||
It takes about five seconds and is safe to re-run as often as you like. Each run wipes the
|
||
previous demo and creates it again.
|
||
|
||
### Before you run it
|
||
|
||
- The admin account must already exist. By default that's `joshua@crosscode.co.za`.
|
||
To keep a different account, set `DEMO_ADMIN_EMAIL`:
|
||
```bash
|
||
DEMO_ADMIN_EMAIL="admin@example.org" npm run set-demo
|
||
```
|
||
If the account isn't found, the script stops before changing anything.
|
||
- Migrations must be up to date (`npm run prisma:deploy`).
|
||
|
||
---
|
||
|
||
## What it keeps and what it clears
|
||
|
||
| Kept | Cleared |
|
||
|---|---|
|
||
| Site settings (`AppSetting`): org name, branding, SMTP, Yoco, WhatsApp, legal pages | Every event, with its options, variants, early-bird tiers, sections, forms, costs and cash-ups |
|
||
| The admin account, including its login history, trusted devices and 2FA | Every other user |
|
||
| Migration history | Registrations, payments, tickets, scans, coupons, waitlists, consents, cabins, feedback, analytics, audit log |
|
||
| Uploaded files on disk (`public/uploads`) | Queued scheduled emails/WhatsApps (`data/scheduled-emails.json`) |
|
||
|
||
The wipe and the new data are written in one database transaction. If that fails,
|
||
nothing changes. The past events' cashups run straight afterwards, through the same
|
||
code as the cashup screen. If one of those fails, re-run the script.
|
||
|
||
---
|
||
|
||
## What it creates
|
||
|
||
### Upcoming events
|
||
|
||
All dates are worked out from the day you run it, so these events are always upcoming.
|
||
Registrations are spread over the past month, as if sign-ups have been coming in.
|
||
The data is generated from a fixed seed, so every run looks the same.
|
||
|
||
| When (from today) | Event | Shows off |
|
||
|---|---|---|
|
||
| ~1½ weeks, Sat 07:30 | **Men's Breakfast: Built to Last** | **Sold out** (60 places), 5 people on the **waitlist**, card / cash / EFT payments recorded by staff, 2 cancelled registrations |
|
||
| ~2½ weeks, Sat 10:00 | **Family Fun Day** | Adult and child tickets with a **bundle deal** (Family deal: 2 adults + 3 kids for R220 instead of R280, used by about a third of registrations) and a **pick-any** deal (Lunch for four: any 4 lunch packs for R220), a non-ticket **add-on** (lunch pack), a fixed-amount **coupon** (`FUNDAY20`), entry and food-stall check-in sections |
|
||
| ~5 weeks, Fri–Sat | **Rooted Women's Conference** | Two **early-bird tiers** (one already expired, one still open) plus a Saturday-only ticket, T-shirt **variants** (S–XXL), a **required sign-up form** (dietary needs, workshop choice, prayer requests), a 20% **group coupon** (`ROOTED20`) used by 4 registrations |
|
||
| ~7 weeks, Mon–Thu | **Ignite Summer Camp** | **Named attendees** (campers aged 12–17), signed **parental consent**, gender collected, **deposits and part-payments**, campers placed in boys' and girls' **cabins** with staff leaders, **sponsor-a-spot** donations towards a 10-spot goal |
|
||
| ~8 weeks, Sun 19:00 | **Carols by Candlelight** | A **free event** with group tickets (1–6 people), plus general **donations** |
|
||
| ~14 weeks, Fri–Sun | **Marriage Enrichment Weekend** | Room-type **variants with limited stock**, early-bird prices per room type, a per-person spa add-on, R1 000 EFT deposits |
|
||
|
||
Each event also gets:
|
||
|
||
- A cover photo (from Unsplash, which `next.config` already allows) and a contact person.
|
||
- **Costs**, both once-off and per item, so the profit view and cash-up have figures.
|
||
- About 30 days of **funnel analytics** (views → started → submitted → paid) by source
|
||
(WhatsApp, Facebook, Instagram, email, direct) and device.
|
||
|
||
Paid registrations have tickets with QR codes, the same way the app issues them once
|
||
payment is complete. Part-paid and pending ones don't have tickets yet.
|
||
|
||
### Past events
|
||
|
||
Ten completed events spread over the past year, for showing cashups and the historical
|
||
reports. Each one has:
|
||
|
||
- Online sales in the five weeks before the event, with a few unpaid no-shows.
|
||
- **Door sales** on the day, recorded by the staff team (cash and card machine).
|
||
- **Check-ins**: about 90% of tickets scanned, with the odd group arriving short.
|
||
- **Staff cash counts** with note and coin denominations.
|
||
|
||
Some events also have refunds, donations and post-event feedback.
|
||
|
||
The cashups cover every path through the cashup screen:
|
||
|
||
| When | Event | Cashup to demo |
|
||
|---|---|---|
|
||
| ~11 months ago | **Church Family Picnic** | **Quick closed** (no settlement) |
|
||
| 24 Dec | **Christmas Eve Candlelight Service** | Free event with card and cash **donations**. Card settled net of **Yoco fees**, collection banked |
|
||
| Easter Sunday | **Easter Sunrise Service & Breakfast** | Cash banked less a **cash deposit fee** (bank charge), card fees, a **refund** |
|
||
| Mother's Day | **Mother's Day Brunch** | **Closed with R50 cash short → reopened → recounted → closed again.** The history shows all three steps |
|
||
| ~3½ months ago | **Youth Winter Camp** | Deposits and balances. **EFT pending** (POPs) at close, later **marked received** without reopening |
|
||
| ~2½ months ago | **Men's Golf Day** | Card-heavy, card fees written off, a refund |
|
||
| ~2 months ago | **Kids Holiday Club** | Cash-heavy door sales. **R20 cash shortfall written off**, **R500 kept on hand** as float |
|
||
| ~6 weeks ago | **Women's High Tea** | Closed with **R30 unaccounted**, shown in red |
|
||
| ~4 weeks ago | **Missions Fundraiser Dinner** | **R2 600 EFT still pending** (POP received, not cleared). Shows "Mark received" / "Write off" |
|
||
| ~10 days ago | **Worship Night** | **Still open, with the cash already counted.** Use it to do a cashup live in the demo |
|
||
|
||
Easter, the Youth Camp, the Holiday Club and the High Tea have **feedback forms** with
|
||
ratings and comments. Funnel stats older than 90 days are stored as daily totals, the
|
||
way the app rolls them up overnight, so the funnel report covers the whole year.
|
||
|
||
### Totals
|
||
|
||
A typical run gives roughly:
|
||
|
||
| | |
|
||
|---|---|
|
||
| Users | 130 (plus the admin) |
|
||
| Registrations | ~700 (paid, part-paid, pending, cancelled and refunded) |
|
||
| Payments | ~640, including donations, sponsorships and refunds |
|
||
| Tickets | ~780, with ~490 check-ins |
|
||
| Named campers | ~45 |
|
||
| Feedback responses | ~80 |
|
||
| Funnel rows | ~4 400 |
|
||
|
||
---
|
||
|
||
## Logging in
|
||
|
||
Every demo account uses the password **`Demo@1234`**.
|
||
|
||
| Role | Email | Good for showing |
|
||
|---|---|---|
|
||
| Supervisor | `lerato.khumalo@example.com` | Event setup, reports, coupons, viewing cashups |
|
||
| Staff | `sipho.dlamini@example.com` | At-the-door payments and check-in scanning |
|
||
| Staff | `anri.botha@example.com` | |
|
||
| Staff | `daniel.pillay@example.com` | |
|
||
|
||
Closing, reopening and settling pending money in a cashup are admin-only, so do the
|
||
live Worship Night cashup as your own admin account.
|
||
|
||
The other 126 people are ordinary members (role `user`), with emails like
|
||
`thandi.mokoena@example.com`. Log in as one of them to show the member view: their
|
||
registrations, tickets and payments. Any registrant's email from an event's attendee
|
||
list works.
|
||
|
||
Your own admin account keeps its existing password.
|
||
|
||
---
|
||
|
||
## Safety
|
||
|
||
- **Nothing is sent.** The script writes straight to the database and never calls the
|
||
email, WhatsApp or Yoco code.
|
||
- **Nothing can reach a real person later.** Demo people use `@example.com` addresses
|
||
(a reserved domain that can't receive mail) and have **no phone numbers**, so
|
||
reminders or broadcasts sent during a demo go nowhere. All parental consents are
|
||
already signed, so no consent reminders go out.
|
||
- Card payments have made-up Yoco ids (`demo_…`). They don't exist in Yoco, so leave
|
||
the demo site's Yoco settings in test mode or empty.
|
||
|
||
---
|
||
|
||
## Customising
|
||
|
||
Everything lives in [`scripts/set-demo.js`](scripts/set-demo.js). There is one
|
||
`build…()` function per event (`buildMensBreakfast`, `buildYouthCamp`, …), and
|
||
`main()` decides which ones run. To change an event's name, prices, ticket types or
|
||
number of registrations, edit its function. To add an event, write a new `build…()`
|
||
and add it to the list in `main()`. `addBundle(event, { name, price }, [[option, quantity], …])`
|
||
adds a bundle deal. Registrations added after it get the deal automatically, worked out
|
||
the same way as the app does it.
|
||
|
||
The past events are in `buildPastEvents()`, one spec each: dates, ticket types, costs,
|
||
how many people bought online and at the door, and a `cashup` that says how it was
|
||
closed. Supported closes:
|
||
- `'quick'`
|
||
- `'full'`, with `lines` saying where the money ended up
|
||
- `'reclose'`
|
||
- `'none'`, which leaves the event open
|
||
|
||
A full close can add `later` to settle pending money after closing. The helpers
|
||
`cardLine`, `eftLine` and `cashLine` build the usual settlements: card net of Yoco fees,
|
||
EFT with an optional pending POP, and cash banked with an optional float, deposit fee or
|
||
shortfall.
|