Bỏ qua để đến nội dung

Vendor panel overview

Nội dung này hiện chưa có sẵn bằng ngôn ngữ của bạn.

The vendor web app covers store operations: POS, orders, catalog, inventory, finance, marketing, and staff. This page summarizes owner vs employee access, how DashboardOutlet uses ROUTE_RULES (vendor-module-access.ts), how ProtectedRoute matches app-router.tsx, and how the sidebar / command palette mirrors vendorNavConfig (vendor-dashboard-nav.ts).


RoutePurpose
/Marketing landing (same public landing settings contract as admin-web).
/loginVendor sign-in (staff vs owner).
/forgot-password, /reset-password, /verify-otpPassword / OTP recovery flow.

There is no public vendor self-registration route in the app; account provisioning is out of band. Details and automated login tests: Vendor account & sign-in.

SurfaceRoutesChrome
Dashboard appMost URLs under /dashboard/*Sidebar + TopBar + DashboardOutletOutlet (apps/vendor-web/src/app/dashboard/layout.tsx)
Fullscreen checkout / POS workspace/pos, /point-of-sale/*, /dashboard/orders/createNo classic dashboard sidebar: /pos and /dashboard/orders/create are standalone; /point-of-sale/* uses PointOfSaleAppShell (own side nav + top bar). All remain behind ProtectedRoute / order module rules where applicable. User guide: Cashier help center.

Inside the dashboard shell, StoreSelector (toolbar) drives selectedStoreId (Zustand + APIs) so lists and home metrics are store-scoped. Owners pick a store; employees may be pinned to one store (Employees and stores).

The index route renders DashboardHomePage, which mounts VendorDashboardHome (apps/vendor-web/src/components/dashboard/home/VendorDashboardHome.tsx):

  • Period filters: Today, This week, This month, and custom Range (store timezone via dashboard_prefs.timezone).
  • Hero KPIs: Net sales, paid orders, average order size — compared to the prior period of equal length.
  • Summary cards: Open orders (successful sales — paid and on account), discounts, refunds.
  • Panels: Top items (analytics) and amount collected by tender (Card / Cash / Other), with links to catalog and Sales overview.

Data sources: GET /vendor/finance/sales-report (current + previous period) and GET /vendor/dashboard/analytics (top products, with timezone). Helpers live in dashboard-period-filter.util.ts and vendor-dashboard-home-display.ts.

  • Structure: vendorNavConfig in vendor-dashboard-nav.ts. The sale rail is Clover-style always-open sections (parent is a label, children stay visible): Home, Sales activity, Reports, Finances, Items, Inventory, Customers, Employees. organization lists Stores and Support; personal is employee self-service. The command palette flattens visible children (plus account/settings extras).
  • Active state: selected child uses a gray row fill and a 3px left bar (emerald, matching the Clover reference screenshots).
  • Visibility: navItemVisibleownerOnly, single module, or modulesAny (e.g. marketing banners: store or product read).
  • Visibility: navItemVisibleownerOnly, single module, or modulesAny (e.g. marketing banners: store or product read).
  • Command palette adds Account: Profile (/dashboard/profile), Settings (/dashboard/settings) when the user is owner or has settings read; Terminals (/dashboard/terminals, /dashboard/terminals/:deviceId) when settings access applies and the device list is non-empty.

DashboardOutlet calls canAccessVendorPath(profile, pathname). If access fails, the app Navigates to defaultLandingPath(profile) (tries a small ordered list of paths, then /dashboard/help — see vendor-module-access.ts). Bookmarked URLs are enforced here even when a nav item was visible.

Important: ROUTE_RULES uses first match. Any /dashboard/... path that does not match a rule is treated as owner-only (canAccessVendorPath returns true only for owners). That includes prefixes such as /dashboard/marketing/... and /dashboard/terminals/... until/unless rules are added for them. ProtectedRoute on those routes may still allow employees (e.g. banners: requiredModulesAny; terminals: settings); layout and nav visibility can therefore disagree — prefer aligning new routes with ROUTE_RULES. The Support screen (/dashboard/support) is documented in Tickets (vendor ↔ admin).

flowchart TB
  subgraph pub["Public"]
    L["/ — landing"]
    LI["/login · recovery routes"]
  end
  subgraph dash["Dashboard · /dashboard/*"]
    SB[Sidebar / search]
    TB[TopBar]
    ST[StoreSelector]
    OUT["Outlet: home, orders, catalog…"]
  end
  subgraph standalone["Fullscreen POS / orders"]
    POS["/pos"]
    POS2["/point-of-sale/…"]
    OC["/dashboard/orders/create"]
  end
  LI -->|session| dash
  L -->|optional sign-in| LI
  LI -->|session + order write| POS
  LI -->|session + order| POS2
  LI -->|session + order write| OC
  SB --> OUT
  1. Home/dashboard → operational KPIs for the selected store.
  2. Orders/dashboard/orders → list; row → /dashboard/orders/:id.
  3. Deliveries/dashboard/orders/deliveries (+ detail :id).
  4. Work shifts/dashboard/orders/shifts (+ /templates, /lanes, /:shiftId). Templates and checkout lanes require settings module on those nested routes. See Work shifts (Vendor) for the full open / operate / close lifecycle, RBAC matrix, and POS attribution rules.
  5. New order/dashboard/orders/create (standalone).
  6. POS/pos or /point-of-sale/sale (order with write); see POS. HTTP checkout uses /vendor/point-of-sale/* (mandatory store_id).
  7. Products/dashboard/products and sub-routes (categories, units, attributes, tags, suppliers, gallery).
  8. Marketing/dashboard/marketing/banners (ProtectedRoute: store or product).
  9. Inventory — warehouses, stocks, lots, adjustments (+ detail / create where routed).
  10. Finance — transactions, expenses, refunds, receivables, report (/dashboard/finance/...); legacy /dashboard/finances still mapped to finance in ROUTE_RULES.
  11. Customers, discounts, stores, employees, roles — parallel routes; roles nav is owner-only; employees use employee module.
  12. Managers/dashboard/managers (owner-only in ROUTE_RULES).
  13. Settings/dashboard/settings/* (general, notifications, team, devices, shift-templates, checkout-lanes, POS bridge / lab pages under settings where wrapped).
  14. Profile/dashboard/profile (allowAll in layout rules).
  15. Terminals/dashboard/terminals, /dashboard/terminals/:deviceId (settings on ProtectedRoute).
  16. Notifications/dashboard/notifications (allowAll for authenticated vendor in layout rules).

GoalsNon-goals
Map main screens to routes and modulesDuplicate every edge case of ProtectedRoute wrappers
Explain owner-only vs module-gated areasReplace backend permission checks

After sign-in, most features live under /dashboard. Checkout is reachable at /pos (classic), /point-of-sale/sale (POS workspace), and /dashboard/orders/create (manual). Owners bypass module membership checks; employees need slugs in profile.modules plus read/write via module_permissions. /pos, /point-of-sale/sale, and /dashboard/orders/create require order with write. vendorCanManageWorkShifts (shift management helpers) treats owner or order write as able to manage store-wide shift operations where the UI uses it.


  1. Vendor user signs in → /dashboard or first path in defaultLandingPath that passes canAccessVendorPath.
  2. StoreSelector persists selectedStoreId for scoped APIs.
  3. User opens POS, orders, catalog, etc. per assigned modules and layout rules.


The vendor documentation baseline is now:

  • Every user-facing screen must ship with both:
    • Unit tests (component/hook/schema logic in apps/vendor-web/src/**)
    • E2E tests (browser flow in apps/vendor-web/e2e/**)
  • Each documented screen should also have at least one docs screenshot under apps/docs/public/screenshots/vendor/ captured by Playwright docs screenshot specs.

Current implementation status (what already exists vs backlog) is tracked in Vendor testing and screenshots index. Treat that page as the source of truth before releasing screen changes.


  • Owners (vendor_type === "owner") pass module checks for every slug and ownerOnly rules.
  • Employees get profile.modules and optional module_permissions. The UI uses vendorHasModuleRead / vendorHasModuleWrite in vendor-module-access.ts.

Module and route matrix (layout: ROUTE_RULES)

Section titled “Module and route matrix (layout: ROUTE_RULES)”

Rules are evaluated in array order in vendor-module-access.ts. For a match, dashboardPathRequiresModuleWrite upgrades the check to write on create/edit/detail patterns (see source for regex details).

Path prefix (first match)Module / flagNotes
/dashboard/rolesrole + ownerOnly
/dashboard/employeesemployee
/dashboard/managersownerOnlyNo module slug
/posorderAlso ProtectedRoute write
/point-of-saleorderWorkspace shell; sale path requires write
/dashboard/inventoryinventory
/dashboard/financefinance
/dashboard/productsproduct
/dashboard/ordersorderIncludes deliveries, shifts, …
/dashboard/customerscustomer
/dashboard/discountscoupon
/dashboard/storesstore
/dashboard/notificationsallowAllAuthenticated vendor
/dashboard/messagesnotificationRule exists; no messages route in app-router.tsx at time of writing
/dashboard/financesfinance
/dashboard/settingssettings
/dashboard/helpallowAllRule exists for defaultLandingPath fallback; no help child route in app-router.tsx at time of writing
/dashboard/profileallowAll
/dashboard/systemownerOnlye.g. /dashboard/system/translations, …
/dashboard/analyticsdashboardRule present; no analytics segment in app-router.tsx at time of writing
/dashboard (exact)dashboardHome

Unlisted /dashboard/... paths (e.g. /dashboard/marketing/..., /dashboard/terminals/...): canAccessVendorPathowners only until a ROUTE_RULES entry exists.

Write vs read: Owners always pass. Employees need write on mutating URL patterns for the mapped module.

Source of truth for declared routes: apps/vendor-web/src/routes/app-router.tsx.

AreaTypical routes
POS/pos, /point-of-sale/sale, … (order write on sale)
Create order/dashboard/orders/create
Home/dashboard
Orders/dashboard/orders, /dashboard/orders/:id
Deliveries/dashboard/orders/deliveries, .../deliveries/:id
Work shifts/dashboard/orders/shifts, .../shifts/templates, .../shifts/lanes, .../shifts/:shiftId
Products/dashboard/products, create/edit/detail, categories, units, attributes, tags, suppliers, gallery
Marketing/dashboard/marketing/banners
Inventorywarehouses, stocks, lots, adjustments (+ detail / forms)
Financetransactions, expenses, refunds, receivables, report; /dashboard/finances
Customers/dashboard/customers, :id
Discounts/dashboard/discounts, :id
Stores/dashboard/stores, create, detail, edit
Employees/dashboard/employees, create, detail, edit
Roles/dashboard/roles, create, :id, :id/edit (owner-only nav)
Managers/dashboard/managers, create, :id/edit (owner-only layout rule)
Notifications/dashboard/notifications
Settings/dashboard/settings/* (nested tabs + device / POS bridge labs)
Profile/dashboard/profile
Terminals/dashboard/terminals, /dashboard/terminals/:deviceId
System (placeholders)/dashboard/system/translations, business-settings, currencies, withdrawals, delivery-men