FLOW LGX · PART 4 · JANUARY 2026
In January 2026 the screens existed and the next risk was structure. A logistics platform grows sideways — new roles, new modules, new rules — and a codebase that is not organised for that growth slows down every month after.
Month by month, how a logistics platform for a Toronto client went from a simple brief to a live, multi-company SaaS product.
We organised the code so each type of user has their own area behind one shared, protected layout. That made later growth — from a few roles to nine — far easier.
Route-level pages are grouped by role: everything an admin sees lives under the admin folder, everything warehouse staff see under staff, and so on. That makes it obvious where a screen lives and who it is for.
The logic inside those pages is grouped by feature. Each feature folder holds its own components, types, validation schema and constants. Pages stay thin: they arrange features and pass in data. When two roles need the same feature with small differences, they share the feature and differ only at the page level.
Every protected page renders inside a single application layout: a top bar, a navigation menu and the page content. The menu is built from the signed-in user’s role, so a dispatcher and a warehouse worker see different menus inside the same frame.
Using one layout instead of one per role kept the product consistent and meant design fixes landed everywhere at once.
Each role’s routes sit behind a protected route that checks two things: is the user signed in, and is their role allowed here? A warehouse user who types an admin address is sent to an “unauthorised” page instead of seeing a broken screen. Public pages — sign in, register, forgotten password, one-time code, reset password — sit in a separate public layout.
We wrote the role list once, in one file, and every route, menu and check reads from it. That decision paid for itself many times over when the number of roles grew later in the project.
We chose Zustand for the small amount of truly global state — mainly who is signed in and their role — and TanStack Query for everything that comes from the server. There is no Redux. Server data is cached, refreshed and shared by query keys rather than copied into a global store and kept in sync by hand.
Keep the list of roles in exactly one place. Roles always multiply, and scattered role checks are where access bugs hide.