User UI Preferences (PWA)
FlowPOS PWA stores user-scoped UI preferences with a local-first adapter to keep behavior stable across Sale, Quote, and Restaurant forms.
Scope
Preferences are scoped by:
userIdbusinessIdlocationId
When any scope value is missing, anon-* fallbacks are used to avoid key collisions.
Stored Fields
panelLayout:"catalog-left" | "summary-left"— Order Taking split, Sale/Quote menu tabs, and the restaurant Add Item modal (TwoPanelSplitLayout)showLayoutControls:booleanautoCollapseSidebarOnWorkspaceRoutes:boolean— desktop sidebar auto-collapse on Restaurant workspace routes (defaultfalse)saleDefaultTab:"search" | "menu" | "customer" | "checkout"restaurantDefaultTab:"orders" | "orderTaking" | "searchProducts" | "detail" | "guests" | "billing"gatewayOrderByModuleId:Record<string, string[]>(ordered gateway item keys per module)schemaVersion: currently1
Sidebar auto-collapse (desktop only)
When autoCollapseSidebarOnWorkspaceRoutes is true, these routes start with the collapsed icon rail (~96px) and use session-only expand/collapse (re-collapses on navigation within workspace routes):
/forms/restaurantOrders/forms/restaurantKds(staff mode)/forms/restaurantExpo/forms/restaurantPackingScanner
When the preference is false, those routes use the persisted sidebar collapse state (flowpos-sidebar-collapsed in localStorage).
KDS kiosk (/forms/restaurantKds?kiosk=true) always uses transient collapsed sidebar regardless of this preference.
Manual sidebar width (flowpos-sidebar-width) remains device-scoped and is not part of user UI preferences.
Policy resolution lives in resolveSidebarCollapseMode() (apps/frontend-pwa/src/routes/routeLayout.ts); layout state is applied via useSidebarCollapsePolicy().
Storage Keys
- New key:
flowpos:ui:user-preferences:{userId}:{businessId}:{locationId} - Legacy compatibility key:
flowpos:ui:two-panel-layout:{segment}:{userId}:{businessId}:{locationId}
The repository reads legacy layout when the new key is absent and writes both keys to preserve backward compatibility.
After each write, the repository dispatches flowpos:ui-preferences-changed on the same window so all useUserUiPreferences hook instances (for example layout shell + settings page) stay in sync without a full reload. Cross-tab updates still use the browser storage event.
Precedence Rules
Default tab behavior follows this precedence:
- Explicit URL/query/deep-link intent
- Existing explicit page state
- Saved user preference
- Hardcoded fallback
This prevents user preferences from overriding intentional navigation flows.
Gateway ordering behavior follows this precedence:
- Saved
restaurantGatewayOrderkeys that still exist - Current gateway items not present in saved keys (appended in remote default order)
This keeps ordering resilient when menu entries are added, removed, or renamed.
Sidebar auto-collapse follows this precedence:
- KDS kiosk mode → always transient collapsed
- User preference
autoCollapseSidebarOnWorkspaceRoutes(default off) - Persisted device sidebar state (
flowpos-sidebar-collapsed)