The mirror of My Opportunities: that page is the work you do and the money that
comes in, this one is the work you buy and the money that goes out. It was a single undated
stack of six identical cards behind an All / Open / Filled toggle, which meant a listing with
three people waiting on a decision looked exactly like one nobody had applied to. It is now
grouped by who each job is waiting on, the applicants are on the row rather than behind a
button, and the page no longer offers to pay for work that has not happened yet. Same data
layer, same tokens, nothing renamed.
Scope The My Listings page and the row that lists a posted job on itStatus Committed as 72c3b9f on 2026-09-04; deployed
01
What was wrong with it
The page held everything an agent needed and sorted none of it. Every card was
the same size and the same weight whatever state it was in.
Before. Six cards, no dates, no order, and the applicant count set as the fourth item in a grey stat row.
Nothing said what needed doing. A listing with three people waiting on a decision rendered identically to one nobody had applied to. The applicant count was the fourth item in a muted stat row and the only way to act on it was a generic "View applications".
It offered to pay for work that had not happened. The sign placement at Farbrook Drive is booked for tomorrow, and the page was already showing "Mark complete and pay $35.50" against it. Marking a showing complete is what releases the provider's money.
No time anywhere. Every listing carries service_date and service_time and the page used neither. "Goes live Monday", "double-booked Thursday", "Sunday 12–3" were left inside the description prose.
The description ate the card. Two full lines of it on every row, given more space than anything an agent would act on.
The title repeated the badge. A "Deep clean" tag over a heading reading "Deep clean", on a listing whose street address appeared nowhere.
Photos went unused. Every listing has the agent's own pictures of the property, and the page showed none of them, though the day sheet on My Opportunities leads every job with one.
Chartreuse on hover, six at a time. The card carried hover:border-primary-ink, so the reserved accent lit up on any card the cursor crossed.
The tabs and the counter said the same thing. "All (6)" next to "Showing 6 listings".
No totals at all. Six listings with money against them and the page never said what was committed or what had been spent.
02
What it is now
A posted job is always waiting on somebody, and which somebody is the only
question this page has to answer. It is you, or it is the market, or it is a date that has
not arrived yet. So the sections are the answer, in the order they need the agent, and
"Needs you" is deliberately the only one that can hold an action worth taking.
After. Four sections, the property on every row, and the people waiting on you shown as people.
Four sections instead of three tabs. Needs you, Booked, Out for applications, Done. A section only renders when it has something in it, and inside each one the soonest date comes first.
Applicants stopped being a number. The strip under each row carries the faces and first names of the people waiting: "3 agents applied · Nia, Jordan, Erik". That comes from the applications themselves rather than a count query, so the agent can see who is waiting without opening anything.
A counter-offer is called a bid. Where somebody named their own price the row says "bid $110.00". Not "from": an applicant can name a number above the ask as readily as below it, and on the $95 posting in the sample data that is exactly what happened, so "from $110" next to "$95" would have been actively misleading.
A filled listing names who took it. "Booked with Erik Lindstrom", "Completed by Samira Haddad". The applicant list holds pending applications only, so a booked job has an empty one — an earlier draft of this page said "nobody has applied yet" underneath a job that was already booked, which is how that got caught.
Payment waits for the work. A filled listing only becomes due once its service date has passed. Until then it sits under Booked with "nothing to do until the day".
The property leads the row. The agent's own photo bleeding to the left edge with no frame, the same way the day sheet leads a job. A listing with no pictures gets a plain tile with a house mark, never a generated image standing in for one.
The date is a real range. "Sunday 11:00 am – 12:00 pm", built from the same clock formatter the day sheet uses, in place of "60 min" on its own.
The street stands in for a repeated title. Where the title is only the service type again, the heading becomes the property address, so "Deep clean / Deep clean" is now "Deep clean / 212 Briarwood Drive".
One control carries the accent, and it is the one that moves money.
03
The rail
Four figures, the paying side of the four on My Opportunities. What is waiting
on you, what is next, what is committed and not yet paid, and what has been spent. They are
all derived from the listings rather than from payment rows, because the payments query
returns nothing in demo mode and a rail of zeros would have been worse than no rail.
Light. Every surface and hairline comes from the same tokens, so nothing needed a second design.Phone. The rail goes two by two and moves above the work, and the row keeps its photo.
Waiting on you counts the applicants across the listings that have them, which is the number that means act.
Next job is the soonest date among everything booked or awaiting a decision.
Committed is what is booked and not yet paid. Spent is completed work.
It sticks beside the work from 1280px, runs four across below that, and goes two by two on a phone.
04
Files
File
What changed
src/components/ListingBoard.tsx
New. The posted-job row: the property photo bleeding to the left edge, the service type and time range, the cost on the far edge, and one strip underneath carrying the applicants, who it is booked with, and whatever decision is outstanding.
src/pages/MyListings.tsx
Rewritten. Grouping by who each job is waiting on, the summary rail, skeletons in the shape of the rows, a composed empty state, and the due-date guard on releasing payment.
Verified in both themes at 1440px and 390px on the demo account. Typecheck and
production build both clean, no console errors on the deployed page.
05
Two things left open
No map here, on purpose. Every listing carries coordinates and a map would render, but this is a page for triage rather than for discovery: the sections are what tell an agent what to do, and pins of their own six properties would not add to that. The Vendors map earns its place because it answers a question about reach that the list cannot.
The phone's list and map toggle on /find-opportunities is a one-way trip. Tapping Map hides the section containing the toggle, so there is no way back to the list without a reload. Confirmed at 390px on the deployed site. It is not fixed here — the same trap was avoided on Vendors by lifting the toggle out of the section that gets hidden.