Tour Dash
Report · 5 September 2026

Admin command center

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.

Scope /admin, the admin data hook, the demo seed, one migration Verified 1440, 1024 and 390 wide, light and dark Live at tour-dash.pages.dev/admin
01

The short version

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.

02

Before and after

The previous admin page: a heading reading Admin with a shield icon, eight tiles counting agents, open listings, pending applications, confirmed appointments, gross volume, pending payouts, reviews and total accounts, then four tabs and two pipeline cards with amber and red status chips.
Before. Eight counts, four tabs of raw tables, and a gross volume figure summed from the wallet ledger. Nothing on it said what Tour Dash kept.
The new command center: a heading reading Command center with a period picker showing 7 days, 30 days, 90 days and All time, then a section headed Needs a look with four items, each a card with a title, a one-line reason, a count and the rows behind it: an applicant waiting two days, two open jobs nobody has applied to, a checkout opened and never paid, and a refund. Below it the section What Tour Dash earned begins with fees earned, fees held, net after Stripe and take rate.
After. The queue leads, because it is what somebody opening the page has to do; the money follows, because it is what they came to see.
The money section in dark theme: a bar chart of fees by week over twelve weeks with solid bars for earned and a hollow bar for held; beside it a By marketplace card listing Jobs as live with $262.12 earned and its rule of $5 plus 10%, Leads as not live yet at $2 plus 12%, and Referrals as not live yet at 0%; a What we keep on a job table showing $50, $100, $200 and $400 jobs with the fee, the contractor's share and the rate; and a row of figures for gross volume, owed to contractors, refunded and fees all time.
The money section, dark. Earned is solid and held is hollow, the wallet's own convention for money that is real versus money that is not yet spendable. The fee ladder is computed from the same constants the checkout uses, so it cannot drift from what is charged.
The full command center on a 390-pixel-wide phone: the period picker stacks under the heading, the Needs a look cards run full width, figures sit two across, the chart and every card stack in one column, and the records tables scroll sideways inside their own frame.
On a phone every section folds into one column and the tables scroll inside their own frame. The page never scrolls sideways at 390, 1024 or 1440.
03

How the fees are counted

Four words carry the whole money section, and they are used the same way on every figure, in the ledger, and in this report.

FigureWhat it sumsWhy that and not something else
Fees earnedfee_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 heldfee_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 StripeFees 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 rateFees 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 contractorspayout_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 timeEvery 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.

04

The queue

"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.

ItemRuleWhy it is on the list
Payments held past the showing dateStatus 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 bookedContractor 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 hoursPending applications older than two days on a job that is still open.Applicants stop applying once silence becomes normal.
Open jobs whose date has passedStatus open, service date before today.Covered off-platform or went unserved. Either is worth a call.
Open jobs with nobody applying after 48 hoursOpen, 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 paidStatus pending for more than a day.An agent reached Stripe and stopped. The booking is still theirs to finish.
Refunds in the last 30 daysAny refunded_cents in the window.One is noise. A pattern on one agent or one contractor is not.
Stripe has gone quietA 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.
05

What has to happen on the live database

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
06

What it does not show yet

07

Verification

08

Files

FileWhat changed
src/pages/Admin.tsxRewritten. 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.tsNew. 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.tsxNew. 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.tsReads payments, waitlist and the last hundred Stripe events alongside the six tables it already read. Refetches every minute.
src/lib/database.types.tsWaitlistRow and StripeEventRow.
src/demo/demo-data.ts, demo-store.tsPayments, waiting list and Stripe events in the demo seed and the demo admin overview.
supabase/admin-command-center-migration.sqlNew. Admin read policies on payments, waitlist and stripe_events, with verification queries.