Vendor portal
The app outside firms use to report progress on their tasks and work from the current document revisions, and how the service keeps each firm to its own work.
The vendor portal is frontend/vendor-app, a second Flow MUI app on
port 3509. It is for the outside firms working on a project: architects, engineers,
consultants, contractors. Each person signs in with their work email and sees the tasks
assigned to their firm and the documents those tasks use, on the projects the firm is
assigned to. There is no second service. The portal reads construction-api's routes under
/v1/vendor, which read the same tables as the console. Why it is built this way is in
docs/decisions/0090-vendor-portal.md, as amended by
docs/decisions/0092-planning-and-document-control.md.
./localenv up vendor-app # http://localhost:3509Before the portal shows anything locally, the contact it signs in as has to exist. The steps are on Getting started.
Screens
| Screen | What a firm can do there |
|---|---|
| Waiting on you | See the firm's unfinished tasks and the documents with a revision published in the last 14 days, across its projects, late items first, grouped by due date |
| Tasks | Read what a task needs, report its status, percentage and forecast finish, add notes, and attach files and photos |
| Documents | Read the current published revision of a document, see the revisions the firm sent and what happened to each, and upload a new revision |
| Projects | The projects the firm is on, its role on each, its contact at Flow, and its tasks and documents on that project |
The readers are outside the company, so the copy avoids Flow's internal terms. Every status is shown with a plain label and the next step, for example "Superseded" with "Do not work from this revision. A newer one has been published."
Tasks
A task shows what to do, its planned and forecast finish, the firm's contact at Flow, and the documents to work from, each with its current revision number. "Report progress" sets the status (not started, in progress, blocked, or done), the percentage, the forecast finish, and what a blocked task is waiting on. A firm cannot cancel a task; that is the project team's call. Done is refused while a task this one depends on is unfinished, or while a document it requires has no published revision. The project team sees every change on the plan as soon as it is sent.
Documents
A document opens on its current published revision, because that is the one to work from. A firm never edits a published revision. To change a document, it uploads a new revision with a note on what changed, and then attaches the files to it. The new revision goes straight to internal review. The project team reviews and approves it, and only when they publish it does it become the current revision. Until then everyone keeps working from the current one.
A firm can add files to a revision it prepared only while that revision is a draft or in review. Files on a published revision are read only. While the firm has a revision with the project team, "Upload a new revision" is turned off and the page links to the one in progress.
Built for a phone
Most firms use the portal from a site. Every list is a card list, and below the small breakpoint the detail opens in a drawer over the list. Forms open full screen on a phone, with large buttons stacked at the bottom. The attachments control offers "Take a photo", which opens the camera directly, beside "Attach a file".
How a firm is kept to its own work
Signing in, the firm, and what it can see are three separate checks.
- The Access application. The portal is behind its own Cloudflare Access
application, and construction-api checks requests under
/v1/vendor/against that application's audience,CONSTRUCTION_VENDOR_ACCESS_AUD, and nothing else. A vendor's token does not open the internal routes, and an internal token does not open the vendor routes. With the audience unset, the vendor routes answer 401. - The contact. The email in the token must belong to an active contact of an active
firm, or the route answers 403
not_a_vendor_contact. An email can be a contact at only one firm, so the email decides the firm. The portal never sends a firm id. The contacts are kept by the vendors service, which construction asks on every portal request. - The scope. A firm sees the tasks assigned to it on projects it is still assigned to, and the documents linked to the firm or linked to or required by one of those tasks. Of a document, it sees the current published revision, the internal revision that was made from, and the revisions the firm prepared itself. Anything else answers 404, not 403, so a firm cannot tell whether another firm's record exists. Taking a firm off a project removes that project and everything on it from the portal at once.
Being a contact is the permission to write. The vendor routes do not read
CONSTRUCTION_EDITORS, and a caller on a vendor route is never an editor. Every write
records the contact's email as the actor, and a note or revision from a firm is marked
with the firm's id.
Routes
| Route | What it does |
|---|---|
GET /v1/vendor/me | The signed-in contact, their firm, and its projects |
GET /v1/vendor/inbox | Unfinished tasks, and documents with a revision published in the last 14 days. Overdue first, then soonest due |
GET /v1/vendor/projects/{projectId} | One of the firm's projects, with its tasks and documents there |
GET /v1/vendor/tasks, GET /v1/vendor/tasks/{id} | The firm's tasks; one task with its notes and required documents |
POST /v1/vendor/tasks/{id}/progress, POST /v1/vendor/tasks/{id}/notes | Report progress on a task, add a note |
GET /v1/vendor/documents, GET /v1/vendor/documents/{id} | The documents the firm sees; one document with the revisions it sees and their trail |
POST /v1/vendor/documents/{id}/revisions | Upload a new revision, which goes to internal review |
POST /v1/vendor/attachments/upload-url, POST /v1/vendor/attachments | Upload a file to a task or to a revision the firm prepared, in two steps |
GET /v1/vendor/attachments, GET /v1/vendor/attachments/{id}/url | List a record's files, get a download link |
The contract is services/construction/openapi.yaml; the vendor
routes are tagged vendor portal.
Not built yet
- Nothing tells a firm that something new is waiting for it. Email or push notifications would go through the communications service.
- The firm-and-assignment check is not the platform's final authorization model. It moves to identity's grants and Cedar along with the rest of the service; the steps are in docs/decisions/0090-vendor-portal.md.
- The vendor Access application is not deployed. The service config carries an empty
CONSTRUCTION_VENDOR_ACCESS_AUDuntil it is.
Verify it
From frontend/vendor-app:
pnpm lint
pnpm build