Skip to main content

Vite React Back Office

apps/back-office replaces the Nuxt 2 / Vue 2 / Vuetify 2 UI in apps/back-office-old. The legacy source remains intact. The new workspace is @bsr/back-office, with Vite, React, TypeScript and custom responsive CSS inspired by the Vuexy semi-dark reference. It does not bundle the commercial template's source or assets.

Start and review

From the repository root, use Node 22.12+ and Yarn Classic:

yarn install --frozen-lockfile
yarn backoffice:dev

Open http://127.0.0.1:8090 and sign in with an operating account. All application data comes from the configured API environment; there is no demo mode. Stored sessions from the retired demo mode are discarded on load.

Real sessions allow the existing create/edit/delete/status actions by default, subject to API authorization. The instruction not to change live data applies to testing behavior. backoffice:safe remains an optional read-only command that blocks business POST/PATCH/DELETE while permitting authentication.

VITE_BACK_OFFICE_READ_ONLY=0 or an unset value enables normal operation. Set it to 1 in .env.local only to opt into read-only mode, then restart yarn backoffice:dev or rebuild. Vite variables are compiled into the build. Changing deployment environment variables after producing dist does not change that build.

Click a row or its eye action to open a modal. Filtering and pagination use the server; clicking a column sorts the currently loaded page. The table scrolls horizontally on small screens, and detail dialogs become full-screen on mobile.

RouteData and filtersDetail and supported actions
/, /dashboardCounts by branch, order status and recent ordersLive counts from response totals; missing totals show a dash. No invented revenue or trends.
/ordersOrders; branch, status, shipping type, date range, searchCustomer, line items, amounts; edit shipping type and coordinates. Status remains disabled as in legacy.
/orders-checkSame order request and filtersShows only rows whose refKey !== null within the fetched page. Not Mobile's waiting-payment workflow. Same detail actions as orders.
/invoicesInvoices; branch, canceled status, shipping type, Incoming, dates, searchCancellation notes, B1 errors, items; edit shipping and coordinates. Status remains disabled.
/cnCredit notes; branch, status, dates, searchReturned items, discount total, warehouse Q1/Q2/Q3 and photos. Read-only.
/transmission-lineDeliveries; branch, status, transportation flag, dates, searchInvoice B1 outcomes, grouped products; update delivery status.
/store-lineInvoices with fixed shippingType=2; branch, status, dates, searchStorefront picking; update NEW / CONFIRMED / CANCELED. No shipping/coordinate tabs.
/transportationTransports; branch, status, dates, searchRoute, driver, deliveries, customer payments, photos by customer and delivery coordinates. Status remains disabled.
/usersEmployees; searchEight permission flags, existing FCM token preserved; Excel export of all pages.
/vehiclesMaster vehicles; BFP/NFF/PCC company, capacity, searchType, chassis, engine, color, year and capacities. Legacy inactive fields retain their reset contract.
/pick-up-pointMaster branch addressesAdd/edit/delete pickup points, shared Thai address lookup, Google Maps or typed coordinates.
/signin, /signoutAuthentication/sessionLogin and local logout; direct signout clears only this application's session.

The sidebar makes the previously direct-access order review route discoverable. No new order creation, stock transitions or payment operations have been introduced.

Shared packages and boundaries

PackageUsage
@bsr/apiCentral environment/hosts, Master key, web Maps key and existing POST guard
@bsr/typesAuth user, employee and permission types
@bsr/utilsThai province/district/subdistrict/postcode dataset, lazy loaded

rn-ui remains native-only. The web application owns its DOM components, CSS theme, web session and heterogeneous legacy API adapters. No native UI, Mobile rule, central environment setting or old app file was changed for this migration.

Request contracts

The request layer uses Accept: application/json, POS Authorization: Bearer or Master x-api-key, JSON bodies, a 40-second timeout, cancellation and backend error text. List responses must be arrays. content-range supplies the total; absent totals do not become a false zero. A 401 clears this app's session and returns to login.

Common list fields are page, pageLimit, sort=DESC, sortBy=CreatedAt, search. POS document lists add branch/date filters. Orders use orderStatus; invoices use invoiceStatus, reqCancel='' and toB1; deliveries use deliveryStatus and toTransportation; transports/CN use status. Vehicle lists use companyCode (default BFPDB) and capacity, without POS branch/date fields. Pickup points come from a branch's addresses array.

ActionMethod and pathPayload
LoginPOST login, then GET branches?userCode=...email, password; returned token authorizes the branch request
ShippingPATCH orders/:id/shipping-type or invoices equivalentshippingType as a string
CoordinatesPATCH orders/:id/lat-lng or invoices equivalentU_BFP_Latitude, U_BFP_Longitude as strings
Storefront statusPATCH invoices/:id/statusinvoiceStatus
Route picking statusPATCH deliveries/:id/statusdeliveryStatus
Employee permissionsPATCH users/:idEight numeric 0/1 flags plus unchanged fcmToken
VehicleMaster PATCH vehicles/:idActive editable fields plus legacy inactive defaults below
Pickup create/editMaster POST branches/:id/addresses, PATCH branches/:id/addresses/:addressIdExact legacy address field whitelist
Pickup deleteMaster DELETE branches/:id/addresses/:addressIdNo body

There is no active mutation for ordinary order/invoice status, transport status or credit-note status, because the old UI disabled them.

Calculations and fields that must not drift

  • Order cancellation mismatch compares floored amount and invoiceAmount for CONFIRMED/WAITING_PAYMENT. Invoice badge priority is canceled, missing SAP ref/retry, Incoming sent, then cancellation request or unsent.
  • Delivery header totals include every invoice. Grouped products exclude CANCELED invoices, combine by productCode and sort descending by quantity.
  • A transport customer without paymentType displays amount; after payment it displays cash + transfer. Photo requests include customer code and both coordinates so separate stops are not conflated.
  • CN returned quantity/weight includes only isReturn=1; the discount total includes every priceCN. Warehouse Q1/Q2/Q3 is read-only.
  • Employee flags are fillWeight, editPrice, delDelivery, newCustomer, approveCustomer, resetBooking, editDeliveryDate, isAdmin. Excel keeps the original 15 explicit columns, excludes tokens and fetches all pages instead of truncating at 999. Values are literal cells, never formulas.
  • Vehicle payload sends vehicleTypeId, chassisNumber, engineNumber, vehicleColor, yearOfManuf, numeric capacityOfPayload and maxCapacityOfPayload. As in legacy, it resets capacityOfFuel=0, status=1, and inactive fuel/date/insurance/license/remark fields to null. This consequential legacy behavior is intentionally retained; changing it needs a separate business decision.
  • Pickup payload allows only addressType, checkAddressName, addressName, street, block, city, country, zipCode, visOrder, U_BFP_Amphur, U_BFP_Tambon, U_ISS_RouteCode, U_ISS_RouteName, U_BFP_Latitude, U_BFP_Longitude. Type is bo_ShipTo. Latitude/longitude must be finite and within geographic bounds.

Theme, dialogs and sessions

The semi-dark sidebar works with both light and dark content. Theme preference is stored under bsr.back-office.theme, initially following the OS. A drawer serves smaller screens; desktop navigation can collapse. Ctrl/Cmd K opens menu search. Native dialogs trap focus, use labelled headings and support nested confirmation/gallery dialogs. Unsaved edits require explicit discard before closing; saving prevents closure and refreshes both table and detail after success.

Session storage is namespaced bsr.back-office.<environment>.session, separate from the old application. Closing the browser tab ends that session according to browser session-storage behavior. Logout removes only this key. The interface does not invent client-only permission gates absent in legacy; API authorization remains the authority.

PRD and UAT on one domain

Run yarn backoffice:build:all to produce apps/back-office/dist-prd for / and apps/back-office/dist-uat for /uat/. The explicit builds select their own API environment and Master key without editing Mobile/Terminal configuration. They call the matching POS and Master APIs directly from the browser. Development still uses Vite proxies.

Upload PRD at the web root and UAT inside uat/, including each generated .htaccess. The root rules preserve UAT routing; its fallback is /uat/index.html. API servers must allow CORS from the website origin (the same for both paths). UAT preflight checks currently require an API-side CORS fix; see apps/back-office/deploy/README.md for the deployment guide and recorded results. Legacy gateway build commands below remain available.

Build and deployment

yarn backoffice:test
yarn backoffice:typecheck
yarn backoffice:build
yarn workspace @bsr/back-office preview

The preview server is at 127.0.0.1:8091. Vite's development/preview gateway proxies /pos-api and /mst-api to hosts selected by shared configuration. Static hosting needs its own reverse proxy and SPA fallback; apps/back-office/deploy/nginx.uat.example.conf is a ready-to-adapt example for an existing HTTPS ingress. It is not an installed server configuration. Keep the API host environment consistent with the compiled shared environment.

Deploy apps/back-office/dist, preserve index.html fallback for every client route, avoid caching API responses, and serve hashed assets with long caching. The Maps web key requires the deployment origin to be allowed by its existing Google configuration. Browser-visible values are not a place for server secrets.

Root workspaces explicitly list the three maintained apps and packages/*, excluding ignored Nuxt legacy dependency installation. ExcelJS and the Thai address database are separate lazy chunks, so their large-chunk build warnings do not mean they load on the dashboard. Vite currently bundles the shared TypeScript config; its notice about a future native config loader does not prevent the current build.

Validation and remaining operating checks

Automated tests exercise read-only request blocking, authentication headers, response errors, retired-session cleanup, mutation payloads, query filters, export above 999 records, amount rules and product aggregation. Tests mock the network. See apps/back-office/QA.md for the historical migration review and current removal checks.

No real/UAT business writes, actual login credentials or live Maps authorization were used during migration testing. Before an operational rollout, verify read-only login/data visibility with the intended user and deployed gateway/Maps origin, then perform business acceptance of enabled writes under the team's normal change process. These operating checks are distinct from the completed local implementation and simulated verification.