Commerce Dashboard
A complete store admin built from the HextaUI commerce blocks. Handles refunds, fulfillment and stock edits that update every page at once.
Commerce Dashboard is a whole store admin for the Halden home goods shop, made from the other commerce blocks. Home and Analytics are Sales Overview, Orders is Orders Table, an order opens in Order Detail with the Refund sheet, Products opens Product Editor, Inventory is Inventory Table, and Discounts opens Discount Builder. The dashboard adds only the glue: a sidebar, a top bar with search and notifications, routing, one shared store, and small Products, Customers and Discounts lists.
Everything reads from one store, so a change in one place shows up everywhere. Refund an item and the order’s payment pill, its timeline, the customer’s total spent and the stock you chose to restock all change together. Fulfill an order and its pill in the orders table flips, the unfulfilled count in the sidebar drops and committed stock goes down. Rename a product and the list, the inventory table and the search palette pick it up.
Routing lives in the block. Pages are hash URLs like #/orders/order-1042, so Back and Forward work and links open in a new tab, and you can swap in Next.js routes through the router prop. Leaving a page with unsaved product, stock or discount edits is blocked with a message, including with the browser’s Back button. Pages cross-fade in 150 ms, and not at all with reduced motion.
The sidebar follows the new Shopify admin. The store switcher, search and a notifications bell sit at the top. Orders, Products, Customers and Analytics open their sub-pages with a separate chevron, the current section opens on its own, and your open sections are remembered. Sales channels and pinned apps sit below, with an amber dot when a channel fails to sync. The footer holds one card at a time (a setup checklist first, then an announcement you can dismiss), Help & support, Settings and your account menu with theme and language. ⌘B or the rail collapses it to icons, where sections open as flyouts and counts become dots. On a phone it's a sheet that closes when you pick a page, and only the page content scrolls.
Press ⌘K or Ctrl+K to jump to a page, an order by number or customer, a product, or a discount code, ⌘1 to ⌘9 to switch stores, and ? for the shortcuts list. Pages that don't have their own block yet, like Drafts or Purchase orders, show a short empty state with what they're for, and Collections and Segments list your real data.
Get HextaUI Pro
One payment unlocks every block, including future ones. See pricing.
Add the Pro registry to components.json
components.json Add your token
Create a token on your account page and put it in
.env.localasHEXTAUI_PRO_TOKEN.Add the block
pnpm dlx shadcn@latest add @hextaui-pro/commerce-dashboard
Your own data and API
Load orders, products and discounts once, put them in a store with createCommerceStore, and pass api functions that call your server. Each one runs before the store changes, and anything it throws shows where the action happened.
Next.js App Router routes
The dashboard routes with the URL hash by default. Pass a router built from usePathname and useRouter to give every page a real URL under /admin, with the same unsaved-changes guard.
Shopify Admin API
Map each api call to a Shopify Admin GraphQL mutation: fulfillmentCreate, refundCreate, orderCapture, orderUpdate, inventoryAdjustQuantities and productUpdate. userErrors become the message people see.
Anatomy
The parts you compose, from the outside in.
| Part | Description |
|---|---|
CommerceDashboard | The app shell: sidebar, top bar, command palette, notifications, routing and every page. |
AppSidebar | The sidebar: store switcher, search, notifications, nested nav with flyouts in icon mode, sales channels, pinned apps, feature card, help, settings and account menu. |
createCommerceStore, useCommerceStore | A tiny external store holding orders, products and discounts, read with useSyncExternalStore. |
simulatedApi | The default api: waits like a real server and checks discount codes against the store. |
useHashRouter, routeFromPath, routeToPath | The built-in hash router and the helpers to build your own. |
inventoryItems, customersFromOrders, productStock | Pure helpers that derive stock rows, customer totals and product stock from the store. |
CommandPalette | The ⌘K palette: pages, orders, products and discounts in one Raycast-simple list. |
CommerceDashboard
Also takes div props for the root.
| Prop | Type | Default |
|---|---|---|
storeThe shared data, from createCommerceStore({ orders, products, discounts }). | CommerceStore | – |
shop{ name, domain, currencyCode, timeZone?, logo?, locations, fulfillmentLocationId?, channels, collections, settingsHref? }. Locations drive inventory, fulfillment and restocking. | CommerceShop | – |
userThe staff member in the sidebar footer. Their name signs comments and timeline events. | { name; email?; avatar?; role? } | – |
salesLoadersLoaders for Home and Analytics, passed to Sales Overview. | SalesOverviewLoaders | – |
nowThe current instant as ISO, for greetings, relative dates and discount status. | string | – |
apiServer calls: fulfillOrder, refundOrder, capturePayment, cancelOrder, updateOrder, bulkOrders, saveProduct, saveInventory, saveDiscount, checkDiscountCode. Missing ones are simulated. Return the saved object to use it, or throw to show the message. | Partial<CommerceApi> | simulatedApi(store) |
router{ route, navigate, href }. Replaces the built-in hash router. | CommerceRouter | useHashRouter() |
liveLive view data for Home. Leave out to hide the card. | LiveViewProps | "loading" | null | – |
segmentsCustomer segments for the discount builder. | DiscountSegment[] | [] |
localeLocale for money, numbers and dates. | string | "en-US" |
stores{ id, name, domain, logo?, role?, testMode? }. More than five adds a search field to the switcher. A test-mode store shows a banner above the page. | SidebarStore[] | one store from shop |
storeId, onStoreChangeThe current store, controlled. Switching closes open popups and announces the change. | string, (id: string) => void | – |
onAddStoreShows Add store in the switcher. | () => void | – |
channels, pinnedChannelsSales channels and which are shown. { id, label, icon, route, status?, description? }. A status shows an amber dot and a Retry sync action on the channel page. | SidebarShortcut[], string[] | – |
apps, pinnedAppsApps and which are pinned. Pinned apps can be reordered or unpinned from their ⋯ menu. | SidebarShortcut[], string[] | – |
cardsFooter cards, shown one at a time: { kind: "setup", done, total, next } or { kind: "announcement", image, description }, each with a cta. Dismissed ids are remembered. | FeatureCard[] | – |
helpLinks in Help & support. | { center?, status?, email? } | – |
theme, onThemeChangeThe theme choice in the account menu. Leave out onThemeChange to hide it. | "light" | "dark" | "system", (theme) => void | – |
language, languages, onLanguageChangeThe language submenu. | string, { value, label }[], (value) => void | – |
onLogOutCalled by Log out. | () => void | – |
CommerceStore
Returned by createCommerceStore.
| Prop | Type | Default |
|---|---|---|
getSnapshot, subscribeFor useSyncExternalStore. | () => CommerceData, (listener) => () => void | – |
setOrders, updateOrderReplace all orders or change one by id. | (change) => void | – |
saveProductInsert or replace a product. | (product: ProductDraft) => void | – |
adjustStockMove stock at a location by a delta. itemId is a variant id, or the product id for products without options. | (locationId, { itemId, delta }[]) => void | – |
saveDiscountInsert or replace a discount. | (record: { discount, stats }) => void | – |
CommerceRoute
Where the dashboard is.
| Prop | Type | Default |
|---|---|---|
pageThe page. | "home" | "orders" | "products" | "inventory" | "discounts" | "customers" | "analytics" | "settings" | – |
idFor orders, products and discounts: the open item, or "new" to create one. | string | – |
| Key | Action |
|---|---|
| ⌘K | Opens search. Ctrl+K on Windows and Linux. |
| ⌘B | Collapses the sidebar to icons or expands it. |
| ⌘1 | Switches to the first store. ⌘2 to ⌘9 switch to the next ones. |
| ? | Shows the keyboard shortcuts. |
| EnterSpace | On a section's chevron, shows or hides its pages without leaving the page. |
| ↑↓ | In search and in menus, moves between items. Enter opens the highlighted one. |
| JK | On an order, opens the next or previous order, from Order Detail. |
| Esc | Closes search, menus, the refund sheet or the phone sidebar. |
- The page links sit in a nav named Main, and the current page's link has aria-current="page".
- Section chevrons are buttons with aria-expanded and aria-controls, and closed sections are inert so Tab skips them.
- In icon mode every item is named by its label and count, like "Orders, 9 unfulfilled", and counts become dots.
- Each page change and store switch is announced politely, and so is a finished refund.
- Table rows open with a real link in the first cell, so they work with the keyboard and open in a new tab.
- Status dots for unread notifications and failed channels have text for screen readers and stay visible in forced colors.
- Blocked navigation shows a warning toast instead of silently dropping the click.
Built with
The free HextaUI components Commerce Dashboard is made from. Each one installs on its own.
Code
10 files, added to components/blocks/commerce-dashboard.