Skip to content

Simpler first work order, Home, GitHub repo import, clearer list and detail pages - #86

Merged
jlwaugh merged 2 commits into
stagingfrom
feature/from-staging
Oct 30, 2026
Merged

jlwaugh merged 2 commits into
stagingfrom
feature/from-staging

Conversation

@saadiqbal-dev

Copy link
Copy Markdown
Collaborator

What changes

Creating a work order

  • The builder's GitHub login and email fill in from their profile or the Agency's last work order (new workOrders.builderDefaults, scoped to the Agency).
  • "+ New project" and "+ New builder" work inside the form. Project slugs avoid collisions (-2, -3).
  • An unsent work order is kept as a local draft per user, network and Agency. Representative details are never stored, and drafts are cleared on sign-out.
  • Import from GitHub: pick repositories from NEARBuilders or MultiAgency (or any organization). The picker lists public repositories only, cached for 10 minutes, and every chosen repo shows as a GitHub link (new workOrders.githubRepos).
  • "Send for signature" says why it's disabled.

Home and lists

  • New Home page showing the work orders waiting on you, plus what's in progress. Home is added to the menu; nothing is removed.
  • Work orders list: tabs and search on one row, full titles, builder account, and the budget as "$X of $Y".
  • list() loads requests, documents, signatures and builders in batches. Output is unchanged.

Work order page

  • Overview, Payouts and Documents tabs. Old ?tab=evidence / ?tab=activity links still work.
  • Key facts strip, an Edit and More menu, and Terminate for offered orders.
  • "Withdraw and delete" checks Documenso under the signature lock first. It refuses if the builder already signed, if Documenso can't be checked, or if the envelope can't be cancelled.
  • The payout account shows "Confirmed when they sign" only when the order is signed through Documenso. Accepted orders no longer show the payout-account warning twice.
  • The page says how the order was signed: Documenso, Google Docs or MultiAgency.

Fixes

  • created_at / updated_at are written explicitly, and both PGlite and pg sessions use UTC. This fixes "since" times that were hours off.
  • Renewals keep the project title and slug. "Unknown project" now shows instead of raw ids, and documents refuse to generate without a project title.
  • Editing an order no longer auto-fills terms from builder defaults, so it can't void signatures by accident.
  • Setup and status copy is shorter across the admin pages.

Review

A deep review covered coding standards, the agreed plan, and security and logic. It found no critical or high issues. The review's fixes are in this PR:

  • per-user drafts
  • the Withdraw and delete safeguards
  • correct payout-account copy for the Google e-sign path
  • no prefill while editing
  • no duplicate payout warning
  • refusing to generate documents without a project title
  • UTC on pg

Testing

  • bun run typecheck: clean
  • API: 754 passed, 8 skipped. New tests cover builder defaults and Agency isolation, timestamps, renewal titles, the Withdraw and delete safeguards, the GitHub repo import (404 and outage), and the payout warning.
  • UI: 268 passed, 1 skipped. New tests cover draft storage per user and network, clearing on sign-out, and Home lists.
  • Biome is clean on the changed files. oxlint shows no new errors; the remaining no-restyle errors are already on staging.
  • Verified by hand on a local server: the full flow from a new work order through Documenso signing by both parties to a submitted payout request. listOrgRepos was checked against live GitHub (NEARBuilders: 124 repos, MultiAgency: 32, unknown org → 404).

Not in this PR

  • bos.config.json, .env.example and the generated plugins/builders/types/contract.d.ts (local dev changes).
  • NEAR wallet signing of work orders (separate work).

…etail pages

Work order creation
- Prefill the builder's GitHub login and email from their profile or the
  Agency's last work order (new builderDefaults route, Agency-scoped)
- Create a project or builder inline from the form; project slugs avoid
  collisions
- Keep an unsent work order as a local draft per user, network and Agency;
  representative details are never stored and drafts clear on sign-out
- Import repositories from GitHub (NEARBuilders and MultiAgency by default,
  or any organization); public repositories only, cached for 10 minutes,
  each chosen repo shown as a link
- Explain why "Send for signature" is disabled

Home and lists
- New Home page with the work orders waiting on you; Home added to the menu
- Work orders list: one row of tabs, full titles, builder account,
  "$X of $Y" budget
- list() loads requests, documents, signatures and builders in batches

Work order page
- Overview, payouts and documents tabs (old tab links still work), key
  facts strip, Edit and More menu, Terminate for offered orders
- "Withdraw and delete" checks Documenso first: it refuses when the builder
  already signed, when Documenso can't be checked, or when the envelope
  can't be cancelled
- Payout account shows "Confirmed when they sign" only for Documenso
  signing; no duplicate payout-account warning on accepted orders
- Shows how the order was signed (Documenso, Google Docs or MultiAgency)

Fixes
- Store created/updated times in UTC (PGlite and pg sessions)
- Renewals keep the project title and slug; "Unknown project" instead of
  raw ids, and documents refuse to generate without a project title
- Editing an order never auto-fills terms from builder defaults
- Shorter setup and status copy across admin pages
@multi-review

multi-review Bot commented Oct 30, 2026

Copy link
Copy Markdown

Multi Review · eba17f0

The PR adds a Home page, changes the work order form (local drafts, inline creation of projects and builders, GitHub repo import, builder defaults), batches the queries behind list(), makes "Withdraw and delete" check Documenso first, and writes timestamps explicitly with sessions in UTC. The batched list() gives the same results as the old per-row path, the new endpoints are limited to the caller's Agency and to managers, and the delete safeguards behave as the PR describes. I found one low-severity side effect in the new delete path.

1 issue(s)

What was checked (12 areas)
  • remove(): syncEnvelope (rejected, contractor-signed, status-check failure and representative-only branches), the strict cancelEnvelope and its other callers (terminate, non-strict)
  • list()/needsYouCount → viewCacheOf + view(): batched requests, offered docs and signatures compared with requestsOf/latestDocument/signaturesOf; paidBeforeOnce cache key vs paidBefore inputs
  • documentProjectTitle callers: fillGoogleDocument (draftDocument strict/non-strict, offered PDF export) and termsRows
  • progressOf payee-attention change vs activationBlockers and EDITABLE payee computation
  • cachedOrgRepos/listOrgRepos: pages() MAX_PAGES bound, deleting the cache entry on failure, 404 mapping in githubRepos
  • builderDefaults: queries scoped to the Agency, empty nearAccount, userEmail failure
  • DB driver: PGlite exec and pg pool connect hook versus drizzle's timestamp-without-tz parsing
  • work-order-form: restoring the draft (lists loaded first), write/clear effect and finished flag, builder-defaults prefill effect, sendBlocker/reason gating
  • work-order-draft storage helpers and their sign-out callers (user-nav, profile, NotYourWorkOrder)
  • Home route: manager gating of setupStatus (agencyManager) vs list (agencyMember), canAccessAdmin = isManager
  • navigation viewHome/exact match, sidebar badge, setupSummary blocking change vs API fixHref values
  • admin work order page: legacy tab mapping, terminable for offered, Duplicate moved into the More menu, payeeConfirmedBySigning vs syncEnvelope setting payeeConfirmedAt

Findings

  • 🟡 Low api/src/services/work-orders/service.ts:3861 Withdraw-and-delete runs the full syncEnvelope, which can notify the builder about an order it is about to delete
    remove now calls syncEnvelope(row) before deleting. syncEnvelope has side effects as well as a status check. Suppose the representative signed in Documenso and that signature hasn't been synced yet: for example, a different representative signs after the manager opened the page, and the manager then clicks "Withdraw and delete". In that case syncEnvelope records the signature and, because the contractor hasn't signed, calls notifyBuilder(current, "work_order_offered", …) and announce(current, "work_order.offered"). Lines 3874–3875 then cancel the envelope and delete the row. The builder gets a "sign this work order" notification whose link points to a deleted order, and the outbox emits a work_order.offered event for an order that no longer exists. In the same way, a Documenso rejection found here runs notifyManagers(..., "work_order_declined") just before the deletion.
    Suggested fix: For the delete check, call esign.status directly (or give syncEnvelope a quiet mode). Refuse if the contractor signed or the status can't be read, and leave out the offer and decline notifications and announcements when the result only gates a delete.

Comment @multi-review to re-review new commits, or @multi-review full for a full review.

@jlwaugh

jlwaugh commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

I pushed fixes (da7e840) from a review of this PR. Tests and typecheck pass locally.

  • Terminate always goes through, so a Documenso outage or a deleted envelope can't block it. If Documenso can't cancel an envelope that can still be signed, the order shows a warning that the builder may still be able to sign there.
  • Delete refuses if Documenso can't be checked, the builder already signed, or the cancel fails, and no longer gets stuck on an old Documenso error. It no longer emails the builder just before deleting, and a declined order is deleted without notifying anyone.
  • Missing project: preparing the document of a work order whose project is gone is refused with its own error. Google is no longer marked as broken, and the message says to choose another project.
  • Restored drafts: a period that has already ended is replaced with the current one.
  • Form: if the builder lookup fails, Send for signature stays disabled and shows "Try again".
  • "Not available" screen: the message now covers withdrawn orders too.
  • Removed: the payee warning, which could never show.
  • Sign-out: one shared helper that also clears drafts. Signing out from the profile page now also disconnects the wallet and reports errors, like the other sign-out buttons.
  • GitHub caches: the two are merged into one.
  • Work-order list: signatures are grouped once instead of filtered for every row.
  • Database: UTC is set at connection start instead of by a query sent after connecting.
  • Repo check: the form uses the shared GITHUB_REPO pattern.

Terminate, Delete, the missing project, restored drafts and the cache have new tests. The UI changes (form retry, "not available" screen, sign-out, restored drafts) were checked by hand in a local run.

@jlwaugh
jlwaugh merged commit ac759cc into staging Oct 30, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants