Feat/api contract alignment - #31
Conversation
`core.autocrlf=true` writes CRLF into a Windows working copy while Prettier defaults to LF, so `prettier --check` failed on ~200 files nobody had touched and `npm run precheck` could never go green. `.gitattributes` pins the checkout to LF on every platform; `endOfLine: "auto"` tolerates a working copy that predates it. No file contents are reformatted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…esponse
The web client was built against `docs/04-api-design.md`, which the backend's
own guide says predates the code. Where they disagreed the doc won here, so
requests were sent that the server always refused and refusals arrived as
English prose or as nothing at all. Every rule below was read off the
FluentValidation validator or handler that actually runs.
Response handling
- `resolveApiError()` replaces the string-only path: it returns the localized
message plus the code, status and whether the failure was recognized.
`describeApiError()` stays a thin wrapper, so no call site changed.
- An unrecognized failure now keeps its code in the sentence. Previously an
unmapped code fell through to the HTTP category, so two different 403s read
identically and the one identifying detail was dropped.
- Field errors: an unmapped rule is framed as a Vietnamese sentence with the
server's words as the detail, instead of being shown as raw English.
- FluentValidation's *default* messages (which interpolate the property name
and the length typed) are matched by pattern — they can never match a
literal table, and they are most of what the API sends.
- Error codes swept out of every `Error.*` call site: 122 -> 201 mapped,
covering the Hub / Logistics / Procurement half that had none. Fixed two
codes that are never sent (`REFRESH_TOKEN_INVALID`, `RATE_LIMITED`) against
the ones that are (`TOKEN_INVALID`, `TOO_MANY_REQUESTS`).
- Admin CRUD dialogs pin `details[]` onto the offending control, so "check the
highlighted fields" finally highlights one.
Client-side validation, per validator
- Tax profile: three fields are `NotEmpty` and the tax code has a format
(`^\d{10}(-\d{3})?$`) that was not checked at all; three limits were wrong.
- Delivery address, business profile, account, products, markets, categories,
units, packing codes, hubs: limits corrected or added where the form was
unbounded. Several differ per entity (unit name 100, others 200).
- Order issue: quantity must exceed 0 and may not exceed the line's ordered
quantity; description is capped at 1000, not 500.
- Cart: minimum order quantity and stock are decided from what is on screen,
named per product, before a draft is created.
Draft orders
The cart is now the server's `Draft` order rather than an in-memory list, so a
reload no longer loses it. Writes are optimistic and serialized; a failed write
re-reads the draft and says why. Signed out the cart stays local and is merged
into the open draft on sign-in.
Claims
`POST /orders/{id}/claims` is restaurant-only and had no screen: filing now
lives on the order it concerns, and the outcomes are listed under the account
area. The status vocabulary moved beside the module that owns claims so the
admin queue and the filer cannot disagree about it.
Fixes found on the way
- Admin create dialog opened without building its form, rendering an empty
header with no fields and no save button — on every CRUD screen.
- The fields template is projected through `ngTemplateOutlet`, so the
surrounding `formGroup` never reached the controls (`NG01050`): inputs
rendered unbound and later fields were left half-built.
- Packing code capacity is a required, bounded decimal; the form treated it as
optional and sent `undefined`, which binds to 0 and is always refused.
- Route stop limit counts the hub, so a route holds 19 restaurants, not 20.
- Route stop reordering allowed an order the domain refuses (pickups must
precede dropoffs).
- `/routes/suggestions` was read from a `markets` key the response has never
carried, so the hubs it suggests — the ones with goods waiting that day —
were dropped and the origin came from the plain hub list instead.
- The map disappeared silently when no Maptiles key was configured, which is
what a deployment built without one looks like from the outside.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Code Review Could Not Complete
|
| Options | Enabled |
|---|---|
| Bug | ✅ |
| Performance | ✅ |
| Security | ✅ |
| Business Logic | ✅ |
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (67)
📝 WalkthroughWalkthroughThe pull request aligns frontend validation and API error handling with backend rules, adds persistent draft carts and checkout checks, improves logistics route workflows, and introduces restaurant claim filing and listing. It also adds localized messages and account layout updates. ChangesAPI errors and validation
Persistent draft cart
Logistics
Restaurant claims
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary by CodeRabbit
New Features
Bug Fixes