Employees can now write and save internal notes on an order from the order detail page. The notes are stored on the order itself, so any teammate who opens the order later sees them in the new "Internal notes" card below Audit history. Each save is recorded in Audit history as "Updated internal notes." The API allows only employees to save notes and rejects notes that are empty or longer than 1000 characters.
Shared contract: OrderDetail now includes notes (string | null). The 1000-character limit (maxOrderNotesLength) is shared by the API and the web app.
Database: new notes column on orders. Existing local databases get the column added automatically on startup, with no data loss or reseed needed.
API: new POST /api/orders/:orderId/notes, restricted to employees. The business rules (trimming, empty and length checks, 404 for unknown orders, audit event) live in update-order-notes.ts. The route handler only passes the request through.
GET /api/orders/:orderId now returns the saved notes.
UI: new "Internal notes" card on the order detail page, with a textarea and a "Save notes" button. Saving shows the "Notes saved." toast and refreshes the order and its audit history.
How to test
List exact commands and manual checks.
bun run test
bun run build:web
bun run check:web
Manual checks:
Log in as an employee, open an order, enter a note and click "Save notes". The "Notes saved." toast appears and Audit history shows "Updated internal notes."
Reload the page. The saved note is still in the textarea.
With an empty or spaces-only note, "Save notes" is disabled.
Screenshots or recordings
Risks / follow-ups
bun run check fails on an error that already exists on master in the Assignment 1 test file apps/api/src/orders/order-search-assignment-1.test.ts(28,3): TS2322 Type 'string | undefined' is not assignable to type 'string'. It is unrelated to this PR, and the test file was left untouched because it belongs to the owner. check:web passes.
Notes cannot be cleared once saved, because the API rejects empty notes as the Assignment 2 tests require.
Each order has a single note; saving replaces the previous text. Audit history records who updated the note and when, but not the old text.
PR checklist
change is scoped to one ticket or one small reviewable concern
## Summary
Employees can now write and save internal notes on an order from the order detail page. The notes are stored on the order itself, so any teammate who opens the order later sees them in the new "Internal notes" card below Audit history. Each save is recorded in Audit history as "Updated internal notes." The API allows only employees to save notes and rejects notes that are empty or longer than 1000 characters.
## Ticket / Assignment
Ticket: Assignment 2 (#9)
## What changed
- Shared contract: `OrderDetail` now includes `notes` (`string | null`). The 1000-character limit (`maxOrderNotesLength`) is shared by the API and the web app.
- Database: new `notes` column on `orders`. Existing local databases get the column added automatically on startup, with no data loss or reseed needed.
- API: new `POST /api/orders/:orderId/notes`, restricted to employees. The business rules (trimming, empty and length checks, 404 for unknown orders, audit event) live in `update-order-notes.ts`. The route handler only passes the request through.
- `GET /api/orders/:orderId` now returns the saved `notes`.
- UI: new "Internal notes" card on the order detail page, with a textarea and a "Save notes" button. Saving shows the "Notes saved." toast and refreshes the order and its audit history.
## How to test
List exact commands and manual checks.
```bash
bun run test
bun run build:web
bun run check:web
```
Manual checks:
- Log in as an employee, open an order, enter a note and click "Save notes". The "Notes saved." toast appears and Audit history shows "Updated internal notes."
- Reload the page. The saved note is still in the textarea.
- With an empty or spaces-only note, "Save notes" is disabled.
## Screenshots or recordings

## Risks / follow-ups
- `bun run check` fails on an error that already exists on `master` in the Assignment 1 test file `apps/api/src/orders/order-search-assignment-1.test.ts(28,3): TS2322 Type 'string | undefined' is not assignable to type 'string'`. It is unrelated to this PR, and the test file was left untouched because it belongs to the owner. `check:web` passes.
- Notes cannot be cleared once saved, because the API rejects empty notes as the Assignment 2 tests require.
- Each order has a single note; saving replaces the previous text. Audit history records who updated the note and when, but not the old text.
## PR checklist
- [x] change is scoped to one ticket or one small reviewable concern
- [x] acceptance criteria were checked
- [x] code follows `CONTRIBUTING.md`
- [x] no unrelated refactor is mixed in
- [x] debug code and temporary logs are removed
- [x] relevant automated checks were run locally
- [x] changed behavior was manually tested when needed
- [x] known risks or follow-ups are called out
🤖 Generated with [Claude Code](https://claude.com/claude-code)
This is fine for sharing a simple value across frontend and backend. I think a better way to make it more explicit is to encapsulate it by relation, so make it so that you can use it like: orderConstraints.notes.maxLength or similar, this way we can also easily add any other constraints when needed.
This is fine for sharing a simple value across frontend and backend. I think a better way to make it more explicit is to encapsulate it by relation, so make it so that you can use it like: `orderConstraints.notes.maxLength` or similar, this way we can also easily add any other constraints when needed.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Employees can now write and save internal notes on an order from the order detail page. The notes are stored on the order itself, so any teammate who opens the order later sees them in the new "Internal notes" card below Audit history. Each save is recorded in Audit history as "Updated internal notes." The API allows only employees to save notes and rejects notes that are empty or longer than 1000 characters.
Ticket / Assignment
Ticket: Assignment 2 (#9)
What changed
OrderDetailnow includesnotes(string | null). The 1000-character limit (maxOrderNotesLength) is shared by the API and the web app.notescolumn onorders. Existing local databases get the column added automatically on startup, with no data loss or reseed needed.POST /api/orders/:orderId/notes, restricted to employees. The business rules (trimming, empty and length checks, 404 for unknown orders, audit event) live inupdate-order-notes.ts. The route handler only passes the request through.GET /api/orders/:orderIdnow returns the savednotes.How to test
List exact commands and manual checks.
Manual checks:
Screenshots or recordings
Risks / follow-ups
bun run checkfails on an error that already exists onmasterin the Assignment 1 test fileapps/api/src/orders/order-search-assignment-1.test.ts(28,3): TS2322 Type 'string | undefined' is not assignable to type 'string'. It is unrelated to this PR, and the test file was left untouched because it belongs to the owner.check:webpasses.PR checklist
CONTRIBUTING.md🤖 Generated with Claude Code
@@ -4,2 +4,4 @@import type { OrderStatus } from "./order-status";// Internal order notes are capped so they stay a short follow-up context, not a document.export const maxOrderNotesLength = 1000;This is fine for sharing a simple value across frontend and backend. I think a better way to make it more explicit is to encapsulate it by relation, so make it so that you can use it like:
orderConstraints.notes.maxLengthor similar, this way we can also easily add any other constraints when needed.I see what you refer to, Sir.
Thanks, I will keep this pattern of extensibility in my mind next PRs
Code is good, this would easily pass a simple feature addition. PR is a little wordy, but otherwise good job.
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.