The admin page counted rows. It could tell you there were sixteen open listings and twenty-one accounts, and it could not tell you what Tour Dash had earned, because the one number the business runs on lives in a table the page never read. It is now the place the business is run from: what we earned and what we are holding, what needs a hand today, how the marketplace moved, who is on it, and every row behind those figures, in that order.
Every fee Tour Dash keeps is frozen on a row in the payments table at the moment the agent pays. The old page summed the wallet ledger instead, which carries what agents paid and what contractors were owed, and never our cut. So "gross volume" was on the page and revenue was not.
payments.fee_cents, the same column the checkout function
writes.supabase/admin-command-center-migration.sql. Until it is run, the live page says so
rather than showing a zero as if it were true.
Four words carry the whole money section, and they are used the same way on every figure, in the ledger, and in this report.
| Figure | What it sums | Why that and not something else |
|---|---|---|
| Fees earned | fee_cents on payments with status released, by release date, inside the period. | A fee is ours when the agent marks the job complete and the transfer goes out. Before that it is refundable, so counting it would be counting money we might give back. |
| Fees held | fee_cents on payments with status paid. A balance: the period picker does not touch it. | Captured, sitting with the platform, waiting on a job that has not happened. It becomes earned or it becomes a refund. |
| Net after Stripe | Fees earned minus 2.9% + 30¢ of each released payment's full amount. | Stripe charges us on what the agent paid, not on our slice, so a $10 fee on a $50 job costs $1.75 to collect. The rate is the constant in fees.ts; the true figure is on Stripe's balance report and will differ by pennies. |
| Take rate | Fees on paid and released payments in the period, over the amount agents paid. | The number to watch as the mix shifts toward dearer jobs. The curve is $5 + 10%, so the rate falls as tickets rise, on purpose. |
| Owed to contractors | payout_cents on held payments. | Our liability. It is the reason the held fee cannot be spent: the two are the same charge, split. |
| Fees all time | Every released fee, ever. | The one figure that ignores the picker, so the headline never disappears when you narrow the period. |
Leads and referrals appear in the marketplace card with their fee rules ($2 + 12%, and 0%) and no figure, because there is nothing to sum: neither has tables yet. The card says "not live yet" rather than showing $0, which would read as a slow month instead of an unbuilt rail.
"Needs a look" is built from the same rows as everything else, on every refresh. An item only appears when its count is above zero, so on a quiet day the section is one sentence.
| Item | Rule | Why it is on the list |
|---|---|---|
| Payments held past the showing date | Status paid, the appointment's date is before today. | The work happened and the contractor is unpaid because the agent never pressed "mark complete". Every one of these is a support message waiting to arrive. |
| Contractors who cannot be booked | Contractor accounts with stripe_payouts_enabled false. | They are on the map and the app refuses to take a card for them. Supply that is not for hire. |
| Applicants waiting more than 48 hours | Pending applications older than two days on a job that is still open. | Applicants stop applying once silence becomes normal. |
| Open jobs whose date has passed | Status open, service date before today. | Covered off-platform or went unserved. Either is worth a call. |
| Open jobs with nobody applying after 48 hours | Open, future-dated, no applications, posted more than two days ago. | Demand with no supply under it. Where these cluster is where the next contractors need recruiting. |
| Checkouts opened and never paid | Status pending for more than a day. | An agent reached Stripe and stopped. The booking is still theirs to finish. |
| Refunds in the last 30 days | Any refunded_cents in the window. | One is noise. A pattern on one agent or one contractor is not. |
| Stripe has gone quiet | A payment landed this week and no webhook event has been recorded for seven days. | The webhook is the only thing that turns a card charge into a booking. Silence there is an outage, not calm. |
The page is deployed. On the live site the money section will read as empty until one file is run in the Supabase SQL editor, and the page says so in its own words when that is the case.
supabase/admin-command-center-migration.sql
payments, on
waitlist and on stripe_events. Nothing gains a write; the money tables stay
writable by the Edge Functions alone.is_admin() function from the earlier admin migration,
and the two admin accounts already flagged.lead_purchases and referral_agreements land, the marketplace card gets a
figure each and the fee chart gets a second and third series.tsc --noEmit clean on both tsconfigs; npm run build clean./demo/admin at 1440, 1024
and 390 wide, light and dark. scrollWidth equals the viewport at every width. The only
console output is the recharts ResponsiveContainer notice the wallet page already emits.| File | What changed |
|---|---|
| src/pages/Admin.tsx | Rewritten. Period picker, the queue, the money section with chart and fee ladder, pulse, funnel, service mix, markets, supply, and the records section. |
| src/lib/admin-metrics.ts | New. Every figure on the page, computed from raw rows in one place: revenue, marketplaces, pulse, funnel, the queue, mix, markets, trades. |
| src/components/admin/AdminRecords.tsx | New. Seven tabbed tables with one search box: payments (with our fee and a Stripe link), accounts (with payout state), jobs (with the fee at that price), applications, waiting list, ledger, Stripe events. |
| src/hooks/useAdmin.ts | Reads payments, waitlist and the last hundred Stripe events alongside the six tables it already read. Refetches every minute. |
| src/lib/database.types.ts | WaitlistRow and StripeEventRow. |
| src/demo/demo-data.ts, demo-store.ts | Payments, waiting list and Stripe events in the demo seed and the demo admin overview. |
| supabase/admin-command-center-migration.sql | New. Admin read policies on payments, waitlist and stripe_events, with verification queries. |