Skip to content

OrderHistory

Pure presentation of the order history. It never fetches; the caller passes the resolved orders page (via useOrders) and owns pagination. Compose children for a bespoke layout, or omit children (plus orders + renderItem) for the standard card list.

import { OrderHistory } from "@evolve-storefront/feature-order/order-history";

Renders inside OrderProvider.

Preview

DefaultOpen in Storybook ↗

Usage

The filtered history, which is the whole composition a consumer writes.

// A Server Component. `page`, `search`, `status` and the dates all come
// from searchParams, so the filtered list is a shareable URL.
const { createdFrom, createdUntil } = orderDateRange({
from: query.from,
until: query.until,
timeZone: shopperTimeZone,
});
const orders = await gqlClient(GetBusinessOrders, {
businessUnitId,
page,
pageSize: PAGE_SIZE,
searchTerm: query.search,
status: query.status ? [query.status] : undefined,
createdFrom,
createdUntil,
}).then((response) => response.data?.businessUnit?.orders);
<>
<OrderHistoryFilters
searchTerm={query.search}
createdFrom={query.from}
createdUntil={query.until}
/>
<OrderHistoryStatusFilter
status={query.status}
renderLink={({ status, children }) => (
<Link href={ordersHref({ ...query, status, page: undefined })}>
{children}
</Link>
)}
/>
{/* Only the list waits — the filters render immediately. */}
<Suspense key={`${page}:${query.search ?? ""}`} fallback={<OrderHistorySkeleton />}>
<OrderHistory
orders={orders?.items ?? []}
pagination={orders?.pagination}
renderLink={pageLink(query)}
noResults={Boolean(query.search)}
renderItem={renderItem}
/>
</Suspense>
</>;

Props

No props of its own.

Change the layout

Pass children to OrderHistory to arrange its parts yourself. Every part is exported from @evolve-storefront/feature-order/order-history.

// A Server Component. `page`, `search`, `status` and the dates all come
// from searchParams, so the filtered list is a shareable URL.
const { createdFrom, createdUntil } = orderDateRange({
from: query.from,
until: query.until,
timeZone: shopperTimeZone,
});
const orders = await gqlClient(GetBusinessOrders, {
businessUnitId,
page,
pageSize: PAGE_SIZE,
searchTerm: query.search,
status: query.status ? [query.status] : undefined,
createdFrom,
createdUntil,
}).then((response) => response.data?.businessUnit?.orders);
<>
<OrderHistoryFilters
searchTerm={query.search}
createdFrom={query.from}
createdUntil={query.until}
/>
<OrderHistoryStatusFilter
status={query.status}
renderLink={({ status, children }) => (
<Link href={ordersHref({ ...query, status, page: undefined })}>
{children}
</Link>
)}
/>
{/* Only the list waits — the filters render immediately. */}
<Suspense key={`${page}:${query.search ?? ""}`} fallback={<OrderHistorySkeleton />}>
<OrderHistory
orders={orders?.items ?? []}
pagination={orders?.pagination}
renderLink={pageLink(query)}
noResults={Boolean(query.search)}
renderItem={renderItem}
/>
</Suspense>
</>;
OrderCardSummary card for one order: number, date, status, total and a few product thumbnails.
PropTypeDescription
orderOrderSummary
actions?ReactNode

Per-card action (e.g. a "View order" link) — composed by the caller.

Any other props go to the root element, like on a plain HTML element.

OrderHistoryFiltersSearch and date-range filters for the order history: a GET form of named fields, with no handlers, so a Server Component can render it.

Render it outside the boundary that suspends on the orders — a field inside it is blanked by its own submission. The dates are yyyy-mm-dd days; pass them through orderDateRange before useOrders, which takes instants. What the search matches, and why the placeholder ships here: README, "Filtering the history".

PropTypeDescription
searchTerm?string

The term currently applied, echoed back into the field.

createdFrom?string

The applied lower bound as yyyy-mm-dd — what the date input reads and writes.

createdUntil?string

The applied upper bound as yyyy-mm-dd.

names?OrderHistoryFilterNames

Override the field names to match the page's search params.

dates?boolean

Offer the date range. Defaults to true; a consumer store with few orders turns it off.

purchaseOrders?boolean

Name purchase order numbers in the search placeholder. Defaults to true; consumers have none.

children?ReactNode

Extra controls, rendered after the built-in ones and before the submit.

Any other props go to the root element, like on a plain HTML element.

OrderHistoryItemsThe order list plus its pager.

Pass renderLink to page by navigation and keep the whole subtree server-rendered, or onPageChange to page from client state. Neither renders no pager; both is a type error.

No props of its own.

OrderHistoryResultCount"12 results": how many orders match the current search and filters.
PropTypeDescription
pagination?Pagination

The list query's pagination; its totalCount is the number shown.

Any other props go to the root element, like on a plain HTML element.

OrderHistoryStatusFilterState filter for the order history.

The "all orders" tab omits the filter, which leaves cancelled orders out — so CANCELLED is its own tab rather than a row inside the first one. One tab is one state and the query argument is a list, so a caller wraps: status: status ? [status] : undefined.

No props of its own.

OrderHistoryTableThe order history as a table, for buyers who compare and search: date, number, purchase order reference, who ordered, status and the total excluding VAT.

No props of its own.

Loading and empty states

Render these while the data loads or when there is nothing to show, so the page does not jump when it arrives.

LoadingOrderCardSkeletonLoading state for OrderCard.
EmptyOrderHistoryEmptyEmpty state — a friendly message with a shopping call to action.
LoadingOrderHistorySkeletonLoading state for OrderHistory — the same flex flex-col gap-4 list of cards.