Skip to main content

OrderList

A self-contained, paginated order list that fetches orders from the Propeller GraphQL API. Supports text search, date/price range filters, sorting, status filtering, and company-aware queries for B2B users.

Usage

Orders page (basic)

import OrderList from '@/components/propeller/OrderList';
import { graphqlClient } from '@/lib/graphql';

<OrderList
graphqlClient={graphqlClient}
user={user}
onOrderClick={(orderId) => router.push(`/account/orders/${orderId}`)}
enableSearch={true}
searchFields={['term', 'createdAt']}
columns={['id', 'date', 'status', 'total', 'action']}
columnConfig={{ id: '#', date: 'Date', status: 'Status', total: 'Total', action: '' }}
/>

Quotes page (with status filter and validUntil column)

Filter by QUOTATION status and add the validUntil column to show quote expiry dates:

<OrderList
graphqlClient={graphqlClient}
user={user}
orderStatus={['QUOTATION']}
columns={['id', 'date', 'status', 'validUntil', 'total']}
columnConfig={{
id: '#',
date: 'Date',
status: 'Status',
validUntil: 'Valid until',
total: 'Total',
}}
rowsClickable={true}
enableSearch={true}
searchFields={['term', 'createdAt', 'price']}
onOrderClick={(orderId) => router.push(`/account/quotes/${orderId}`)}
/>

B2B with company filter (CompanyContext integration)

For Contact users who belong to multiple companies, pass the active company ID so orders are scoped to that company. The component re-fetches automatically when companyId changes.

import { useCompany } from '@/context/CompanyContext';

const { selectedCompany } = useCompany();
const isContact = (u: any) => u !== null && 'company' in u;
const getActiveCompany = () =>
!user || !isContact(user) ? null : selectedCompany ?? null;

<OrderList
graphqlClient={graphqlClient}
user={user}
companyId={getActiveCompany()?.companyId}
onOrderClick={(orderId) => router.push(`/account/orders/${orderId}`)}
enableSearch={true}
searchFields={['term', 'createdAt', 'price']}
columns={['id', 'date', 'status', 'total', 'action']}
/>

Configuration

Required

PropTypeDescription
userContact | Customer | nullThe authenticated user. Component does not fetch until a user is provided.
graphqlClientGraphQLClientInitialized SDK GraphQL client instance.
onOrderClick(orderId: number) => voidCalled when an order row or "View" button is clicked.

Display

PropTypeDefaultDescription
columnsstring[]['id', 'date', 'status', 'total']Column keys to render. Built-in renderers exist for id, date, status, total, validUntil, action. Any other key renders order[key] as plain text.
columnConfigRecord<string, string>Auto-capitalized keyMaps column keys to display labels for table headers.
classNamestringundefinedCSS class applied to the root wrapper <div>.
rowsClickablebooleanfalseWhen true, the entire row is clickable (calls onOrderClick) and the action column button is hidden.
PropTypeDefaultDescription
orderStatusstring[]['NEW', 'CONFIRMED', 'VALIDATED', 'ORDER']Status codes to filter by. Use ['QUOTATION'] for quotes.
companyIdnumberundefinedOverride company ID for order filtering. Falls back to user.company.companyId for Contact users. Omitted entirely for Customer users.
enableSearchbooleanfalseShow the search panel above the table.
searchFieldsstring[][]Which search inputs to render. Options: 'term', 'createdAt', 'lastModifiedAt', 'price', 'sortInput', 'type'. When enableSearch is true, 'term' is automatically prepended if not already present. See search field types for input types and API mapping.
termFieldsOrderSearchFields[][REFERENCE, ITEM_SKU, ID, ITEM_NAME, REMARKS]Backend fields searched when the user types a text query. Uses Enums.OrderSearchFields values.

Pagination

PropTypeDefaultDescription
initialItemsPerPagenumber10Number of orders per page.

Formatting and Localization

PropTypeDefaultDescription
formatPrice(price: number) => stringFormats as EUR X.XXCustom price formatter.
formatDate(dateString: string) => stringtoLocaleDateString()Custom date formatter.
getStatusColor(status: string) => stringBuilt-in color mapReturns Tailwind class string for the status badge.
labelsobjectEnglish defaultsUI text overrides (see Labels section).

Labels

All fields are optional. Defaults are English strings.

{
view?: string; // "View" button text (default: "View")
previous?: string; // Pagination previous button (default: "Previous")
next?: string; // Pagination next button (default: "Next")
showingPage?: string; // Page indicator prefix (default: "Showing page")
of?: string; // Page indicator separator (default: "of")
noOrders?: string; // Empty state message (default: "No orders found.")
loading?: string; // Loading spinner text (default: "Loading orders...")
order?: string;
date?: string;
status?: string;
total?: string;
action?: string;
}

Behavior

Pagination

  • Page-based pagination using page and offset (items per page) parameters.
  • Pagination controls appear only when totalPages > 1.
  • Mobile: simplified Previous/Next buttons. Desktop: page indicator ("Showing page X of Y") with Previous/Next buttons.
  • The initialItemsPerPage prop sets the page size (default 10). The API response offset can override this.
  • The search panel only renders when enableSearch={true} and at least one search field is configured.
  • The term field is automatically added when enableSearch is true, even if not listed in searchFields.
  • Pressing Enter in the term input triggers a search. A "Search" button and "Clear" button are also provided.
  • Searching always resets to page 1.

Search field types

FieldInput typeSent as
termText (full width)term: string
createdAtTwo date pickers (from/to)createdAt: { greaterThan, lessThan }
lastModifiedAtTwo date pickers (from/to)lastModifiedAt: { greaterThan, lessThan }
priceTwo number inputs (min/max)price: { greaterThan, lessThan }
sortInputTwo dropdowns (field + order)sortInput: { field, order }
typeDropdown (order type)type: OrderType

Column renderers

Six column keys have built-in rendering logic:

ColumnRendering
idBold text, order.id
dateFormatted date from order.date or order.createdAt
statusColored badge using getStatusColor()
totalRight-aligned, formatted price from order.total.net
validUntilFormatted date from order.validUntil (useful for quote expiry)
action"View" button that calls onOrderClick. Hidden when rowsClickable is true.

Any other column key falls through to render order[columnKey] as plain text, allowing access to any field on the Order object.

Status colors (defaults)

StatusStyle
COMPLETE, QUOTE_ACCEPTEDGreen-tinted badge (bg-secondary/10 text-secondary)
CANCELLED, QUOTE_REJECTEDRed badge (bg-red-100 text-red-800)
All othersYellow badge (bg-yellow-100 text-yellow-800)

Override with the getStatusColor prop to return your own Tailwind class string.

Company filtering

  • companyId prop provided: Filters orders by that company via companyIds in the search arguments.
  • companyId prop omitted, Contact user: Falls back to user.company.companyId.
  • Customer user (B2C): companyIds is not included in the query. Customers see only their own orders.
  • The component re-fetches when companyId changes, enabling live updates from a company switcher.

Status codes

Common status strings used with orderStatus:

StatusUsage
NEWNewly placed order
CONFIRMEDOrder confirmed
VALIDATEDOrder validated
ORDERActive order
COMPLETEFulfilled order
CANCELLEDCancelled order
QUOTATIONQuote / request for quotation
QUOTE_ACCEPTEDAccepted quote
QUOTE_REJECTEDRejected quote

Default filter (when orderStatus is not provided): ['NEW', 'CONFIRMED', 'VALIDATED', 'ORDER'].

Re-fetch triggers

The component fetches orders on mount and re-fetches when any of these change:

  • user (login/logout)
  • currentPage (pagination)
  • companyId (company switcher)

A fetching guard prevents concurrent requests.

GraphQL Query Examples

Under the hood, OrderService.getOrders() executes a GraphQL query. Here are equivalent raw queries for reference.

query Orders($input: OrderSearchArguments!) {
orders(input: $input) {
items {
id
date
createdAt
status
total {
net
gross
}
}
itemsFound
offset
page
}
}

Variables for a basic paginated fetch:

{
"input": {
"status": ["NEW", "CONFIRMED", "VALIDATED", "ORDER"],
"userId": [12345],
"page": 1,
"offset": 10,
"term": "",
"termFields": ["REFERENCE", "ITEM_SKU", "ID", "ITEM_NAME", "REMARKS"]
}
}

Quotes with company filter

{
"input": {
"status": ["QUOTATION"],
"userId": [12345],
"companyIds": [678],
"page": 1,
"offset": 10,
"term": ""
}
}

Orders with date range and price filter

{
"input": {
"status": ["NEW", "CONFIRMED", "ORDER"],
"userId": [12345],
"page": 1,
"offset": 20,
"term": "SKU-001",
"termFields": ["ITEM_SKU", "REFERENCE"],
"createdAt": {
"greaterThan": "2025-06-01T00:00:00Z",
"lessThan": "2025-12-31T23:59:59Z"
},
"price": {
"greaterThan": 100.0,
"lessThan": 1000.0
},
"sortInput": {
"field": "DATE",
"order": "DESC"
}
}
}

SDK Services

The component uses OrderService from propeller-sdk-v2 internally:

import { OrderService, OrderSearchArguments, Enums } from 'propeller-sdk-v2';

const orderService = new OrderService(graphqlClient);
const response = await orderService.getOrders(searchArgs);
// response.items — Order[]
// response.itemsFound — total count
// response.offset — items per page

The OrderSearchArguments object controls all filtering, pagination, and sorting:

const searchArgs: OrderSearchArguments = {
status: ['NEW', 'CONFIRMED', 'VALIDATED', 'ORDER'],
userId: [userId],
companyIds: [companyId], // optional, B2B only
page: 1,
offset: 10, // items per page
term: 'search text',
termFields: [
Enums.OrderSearchFields.REFERENCE,
Enums.OrderSearchFields.ITEM_SKU,
Enums.OrderSearchFields.ID,
Enums.OrderSearchFields.ITEM_NAME,
Enums.OrderSearchFields.REMARKS,
],
createdAt: { // optional date range
greaterThan: '2025-01-01T00:00:00Z',
lessThan: '2025-12-31T23:59:59Z',
},
price: { // optional price range
greaterThan: 50.0,
lessThan: 500.0,
},
sortInput: { // optional sorting
field: Enums.OrderSortField.DATE,
order: Enums.SortOrder.DESC,
},
type: Enums.OrderType.ORDER, // optional type filter
};