module5
This commit is contained in:
86
README.md
86
README.md
@@ -6,6 +6,7 @@ Module 3: recursive file listing, PDF streaming from the NAS and the ported
|
||||
pdf.js viewer.
|
||||
Module 4: the buyer side — buyers, contacts, NDA rounds, deals, the guided
|
||||
"New inquiry" flow with duplicate detection and the deal status transitions.
|
||||
Module 5: notes, todos and the "Today" view with the follow-up workflow.
|
||||
|
||||
The UI and all domain constants are English.
|
||||
|
||||
@@ -109,7 +110,7 @@ directory aborts the scan with an error naming the path.
|
||||
3. Copy the project folder to the AI machine, run `docker compose up -d --build`
|
||||
4. Restore the dump: `docker compose exec -T db psql -U bizmatch bizmatch < backup.sql`
|
||||
|
||||
## API (as of module 4)
|
||||
## API (as of module 5)
|
||||
|
||||
| Method | Path | Purpose | Session |
|
||||
| ------ | --------------------------- | ------------------------------------------------ | ------- |
|
||||
@@ -136,6 +137,18 @@ directory aborts the scan with an error naming the path.
|
||||
| POST | /api/ndas/:id/deals | add a business to an existing round | yes |
|
||||
| POST | /api/deals/:id/status | `{status, comment?}` — transition + note | yes |
|
||||
| GET | /api/deals/:id/notes | notes of one deal, newest first | yes |
|
||||
| POST | /api/deals/:id/follow-up-sent | `{comment?, rearm}` — note + re-arm or clear | yes |
|
||||
| POST | /api/notes | create a note on exactly one reference object | yes |
|
||||
| GET | /api/notes | notes of one object, `?…_id=` (+`include_related`) | yes |
|
||||
| PATCH | /api/notes/:id | edit `text` / `highlight` | yes |
|
||||
| DELETE | /api/notes/:id | delete a note | yes |
|
||||
| POST | /api/todos | create a todo | yes |
|
||||
| GET | /api/todos | `?assigned_to=&status=` + one ref id as scope | yes |
|
||||
| PATCH | /api/todos/:id | text, due_at, assigned_to, kind, document_id | yes |
|
||||
| POST | /api/todos/:id/done | close it (`done_by`/`done_at` = session, now) | yes |
|
||||
| POST | /api/todos/:id/reopen | reopen it and clear both | yes |
|
||||
| POST | /api/documents | pin a business file so a REVIEW todo can link it | yes |
|
||||
| GET | /api/today | `?staff_id=` — the day's work + nav counts | yes |
|
||||
|
||||
Everything except health, staff (GET+POST) and login requires the session
|
||||
cookie; without it the API answers `401`.
|
||||
@@ -196,17 +209,62 @@ alphabetical.
|
||||
* `.pdf` is served as `application/pdf` (inline), anything else as
|
||||
`application/octet-stream` with `Content-Disposition: attachment`.
|
||||
|
||||
### Notes, todos and Today (module 5)
|
||||
|
||||
A **note** hangs off exactly one object (buyer, deal, business or NDA round), a
|
||||
**todo** off at most one — the DB enforces both, and the API mirrors it so a
|
||||
wrong body gets a `400` instead of a constraint violation. Every note and todo
|
||||
row carries a `context` object `{type, id, label, buyer_id, business_id}`: the
|
||||
label is the business name, the buyer's company/contact name or
|
||||
`Round <date>`, and the two ids let the UI link straight to the right page.
|
||||
`GET /api/notes?buyer_id=…&include_related=true` additionally folds in the
|
||||
notes of that buyer's rounds and deals, which is what the buyer page shows.
|
||||
|
||||
`GET /api/today` is the daily workqueue, narrowed to one person with
|
||||
`?staff_id=` and covering the whole team without it:
|
||||
|
||||
* **todos** — OPEN, `due_at <= today`, each flagged `overdue` when
|
||||
`due_at < today`
|
||||
* **follow_ups** — deals with `follow_up_at <= today` that are not `ENDED`;
|
||||
"mine" means `created_by`
|
||||
* **pending_ndas** — rounds still `SENT` after `NDA_REMINDER_DAYS` (env,
|
||||
default 14); "mine" means `created_by`
|
||||
* **counts** — the numbers behind the nav badge
|
||||
|
||||
Follow-ups and pending NDAs are **virtual**: they are derived from the deal and
|
||||
nda rows on every request and never materialise as todo rows, so there is
|
||||
nothing to keep in sync or clean up. Answering one is
|
||||
`POST /api/deals/:id/follow-up-sent`, which always writes a note and then
|
||||
either re-arms the reminder for another 14 days (`rearm: true`) or clears
|
||||
`follow_up_at` — `409` on a deal that is already ENDED. Giving up on the deal
|
||||
instead is the normal `POST /api/deals/:id/status` with `ENDED`.
|
||||
|
||||
`POST /api/documents` is a stopgap for the REVIEW todo's file picker: it pins
|
||||
one business file (validated through the same resolver the file streaming uses)
|
||||
as a `document` row so `todo.document_id` can point at it. Real document
|
||||
management follows in module 6.
|
||||
|
||||
## Frontend
|
||||
|
||||
`web/` is a Vite + React + TypeScript app with Tailwind v4 (no router, no state
|
||||
library). The header carries the two nav entries **Businesses** and **Buyers**;
|
||||
routing is a hand-rolled `{view, id}` state in `App.tsx`. Views:
|
||||
library). The header carries the nav entries **Today**, **Businesses** and
|
||||
**Buyers**; routing is a hand-rolled `{view, id}` state in `App.tsx`. Today is
|
||||
the landing view and its nav entry shows a red badge with the signed-in user's
|
||||
overdue + due todos. There is no polling, so the badge is refreshed on mount,
|
||||
on every view change and after any action that can move an item off the list.
|
||||
Views:
|
||||
|
||||
* login ("Who is working?")
|
||||
* today — "My day" / "Team" tabs (Team groups todos by assignee and follow-ups
|
||||
by their creator) over three sections: todos (overdue in red, checkbox to
|
||||
complete, context chip opens the buyer or business), follow-ups due (with
|
||||
"Follow-up sent…" → comment + "wait another 14 days" / "stop waiting", and
|
||||
"End deal…" → the status dialog preset to ENDED) and NDA signatures pending.
|
||||
Empty state: "Nothing due. Enjoy your coffee."
|
||||
* business list (tabs with counts, search, "Scan NAS now")
|
||||
* business detail — a master-detail split filling the viewport: file table
|
||||
left, PDF viewer right, plus a collapsed "Buyer activity" panel linking to
|
||||
the buyers who were introduced to this business
|
||||
left, PDF viewer right, plus collapsed "Buyer activity", "Notes" and "Todos"
|
||||
panels above it
|
||||
* buyer list (status chips with counts, search over company/contact/e-mail,
|
||||
"New inquiry")
|
||||
* new inquiry — contact + business picker; while typing a known name, e-mail or
|
||||
@@ -216,11 +274,16 @@ routing is a hand-rolled `{view, id}` state in `App.tsx`. Views:
|
||||
historic deals in their real state.
|
||||
* buyer detail — status header with Deactivate/Reactivate (warns about the open
|
||||
deals it would end), inline-editable identity panel, contacts with a primary
|
||||
star, and the NDA rounds newest first: editable round fields, the deals of
|
||||
star, a notes panel covering the buyer *and* their rounds and deals, a todos
|
||||
panel, and the NDA rounds newest first: editable round fields, the deals of
|
||||
the round with an action menu (next step, "End deal", plus a "Correct to…"
|
||||
section) that opens a comment dialog, and a collapsed read-only notes list
|
||||
section) that opens a comment dialog, and a collapsed "Notes & todos" section
|
||||
per deal.
|
||||
|
||||
Notes are written in a composer at the top of every notes panel; the red flag
|
||||
button marks a note as important, and flagged notes get a red left border and a
|
||||
light red background wherever they appear.
|
||||
|
||||
In dev, Vite proxies `/api` to `http://localhost:8090`. In production the
|
||||
Fastify app serves `web/dist` via `@fastify/static` with an SPA fallback to
|
||||
`index.html` for all non-`/api` routes; the Dockerfile builds the frontend in
|
||||
@@ -263,21 +326,26 @@ migrations/ numbered SQL migrations
|
||||
001_init.sql full schema
|
||||
002_buyer_fields.sql buyer-side fields from the NDA form + intake sheet
|
||||
003_…_nullable.sql interested_in_updates becomes tri-state
|
||||
004_reset_….sql one-time reset of that column to NULL
|
||||
src/
|
||||
config.ts env configuration
|
||||
db.ts pg pool, query helpers, withTransaction
|
||||
session.ts the staff-id cookie
|
||||
http.ts input coercion + PATCH/reference helpers for the routes
|
||||
migrate.ts migration runner (transactional, advisory lock)
|
||||
business-scan.ts NAS scan, recursive listing, safe file path resolution
|
||||
server.ts Fastify app (health, staff, login, businesses, file, static)
|
||||
buyer-routes.ts buyers, contacts, NDA rounds, deals, the inquiry flow
|
||||
workflow-routes.ts notes, todos, documents, the Today view, follow-ups
|
||||
web/
|
||||
scripts/copy-pdfjs.mjs pdfjs-dist -> public/pdfjs/ (predev + prebuild)
|
||||
public/viewer/ standalone, unbundled pdf.js viewer page
|
||||
public/pdfjs/ generated, git-ignored pdf.js runtime
|
||||
src/api.ts typed API client
|
||||
src/App.tsx session gate + nav + view switch
|
||||
src/App.tsx session gate + nav (with the Today badge) + view switch
|
||||
src/components.tsx shared bits (badges, inline fields, business picker, dialog)
|
||||
src/views/ Login, Businesses, BusinessDetail, Buyers, BuyerDetail, NewInquiry
|
||||
src/workflow.tsx notes panel, todo list and the add-todo dialog
|
||||
src/views/ Login, Today, Businesses, BusinessDetail, Buyers,
|
||||
BuyerDetail, NewInquiry
|
||||
viewer-phase1/ reference copy of the phase-1 desktop viewer
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user