Front-office work exists in two shapes at once. A Service Request records what the customer asked for; a Job records what the business now owes them. Keeping those concepts separate is what stops a conversation status from pretending to be an operational workflow — but it also meant a dispatcher had to read two lists and join them by memory to answer a question as simple as "what is open right now?". The Operations workspace answers it in one place while leaving both state machines exactly where they were.
One row per request, and the job it became
A row is a request plus the job it became, when it became one. Three views sit at the top of the navigation: All shows every request whatever stage it reached, Requests narrows to work that has not been booked into a job yet, and Jobs narrows to requests that already produced a work order. Each carries a count from the same query that filled the list, so the number beside a view and the rows inside it can never disagree.
The columns are chosen for dispatch rather than for support. Work shows the stable request number and, once one exists, the job number. Customer, service, and address describe where a van would have to go. Both statuses are visible in the same row, coloured by what they mean operationally: ready for scheduling reads as progress, qualifying and handoff read as waiting on someone, cancelled reads as stopped. The visit column shows the confirmed appointment time when the job has one.
Columns built for dispatch, with an address flagged only where it blocks
A missing address is only flagged where it actually blocks something. A request still being qualified may legitimately have no address yet, so the row stays quiet. Once the request is ready for scheduling, or once a job exists and has not been completed or cancelled, the empty cell explains itself instead of looking like a rendering gap — because at that point somebody has committed to arriving somewhere nobody has written down.
Filtering follows the same logic. The service filter is built from facets the server returns with the page, so each option can show how many requests that service produced and how many became work — a conversion read the list gives away for free. The status filter switches its options with the stage, offering job statuses when the view is Jobs and request statuses otherwise. Search spans the request number, job number, summary, customer name, email and phone, service name, and the address line and city.
Bulk moves that respect each job's own lifecycle
Bulk moves are offered only when they are valid for every selected row. Selecting mixed rows where some have no job yet offers nothing, because a request that is not work cannot be dispatched. Where all selected rows do have jobs, the workspace offers exactly the statuses each of those jobs can legally transition to, so a bulk action can never push one job through a transition its own lifecycle forbids. Planning lives in the same sidebar: the schedule and the resource list are one click away from the list they feed.
A joined list, not a third state machine
Underneath, the workspace is deliberately thin. Its store owns the filters and the read projection and nothing else — qualification writes still belong to the service-request store, and lifecycle writes still belong to the jobs store. A joined list is a convenient way to look at two domains; it must not quietly become a third place where their rules are decided, because that is how two sources of truth start disagreeing about the same visit.
Run the morning check from one view instead of three
Open the day in All and sort your attention by what the statuses say rather than by what arrived most recently. Requests sitting in qualifying are waiting on an answer, requests in handoff are waiting on a person, and requests marked ready for scheduling are waiting on a slot. Anything with a job attached has already left the conversation and belongs to dispatch.
Use the service filter as a weekly read rather than only as a filter. Each option carries how many requests that service produced and how many became work, so a service with steady inbound volume and almost no conversion usually points at a template that asks for something customers cannot answer, or at a service nobody has capacity to schedule.
Treat a flagged missing address as an interrupt, not a backlog item. The flag only appears once a request is ready for scheduling or a live job exists, which means somebody is already committed to arriving. Fix it from the request panel while the conversation is still open, so the technician brief that gets snapshotted at booking time carries the address rather than a gap.
Frequently asked question
Do service requests and jobs stay separate if they share one list?
Yes. The Operations workspace is a joined read projection: it owns the filters and the list, while qualification writes stay with the service-request store and lifecycle writes stay with the jobs store. A request records what the customer asked for and a job records the work the business owes, and neither state machine changes because the two are displayed in the same row.