Skip to content

Commit 6368c18

Browse files
Update App Shell pages
1 parent 80569df commit 6368c18

3 files changed

Lines changed: 232 additions & 6 deletions

File tree

docs/app-shell/components/data-table.md

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -589,12 +589,13 @@ const { variables, control } = useCollectionVariables({
589589

590590
### Options
591591

592-
| Option | Type | Description |
593-
| ----------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------- |
594-
| `params.pageSize` | `number` | Initial page size. Default: `20`. |
595-
| `params.initialFilters` | `Filter[]` | Filters applied on first render. |
596-
| `params.initialSort` | `SortState[]` | Sort applied on first render. |
597-
| `tableMetadata` | `TableMetadata` | Generated table metadata. Required for typed GraphQL documents (see [Typed query variables](#typed-query-variables)). |
592+
| Option | Type | Description |
593+
| ----------------------- | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------- |
594+
| `params.pageSize` | `number` | Initial page size. Default: `20`. |
595+
| `params.initialFilters` | `Filter[]` | Filters applied on first render. |
596+
| `params.initialSort` | `SortState[]` | Sort applied on first render. |
597+
| `tableMetadata` | `TableMetadata` | Generated table metadata. Required for typed GraphQL documents (see [Typed query variables](#typed-query-variables)). |
598+
| `onParamsChange` | `(params: CollectionParams) => void` | Called after each filter, sort, or page-size change with the current params. |
598599

599600
### Return Value
600601

@@ -629,6 +630,54 @@ const [result] = useQuery({
629630
});
630631
```
631632

633+
## `useURLCollectionVariables`
634+
635+
Wraps `useCollectionVariables` with automatic URL persistence. It seeds filter, sort, and page-size state from the current URL search params on mount and writes changes back to the URL as the user interacts with the table — using `replace` navigation so individual interactions don't push new history entries.
636+
637+
Use this instead of `useCollectionVariables` when you want filters, sort, and pagination to survive page refreshes and be shareable via URL.
638+
639+
```tsx
640+
import { useURLCollectionVariables } from "@tailor-platform/app-shell";
641+
642+
const { variables, control } = useURLCollectionVariables({
643+
tableMetadata,
644+
params: { pageSize: 20 },
645+
});
646+
```
647+
648+
The return value is identical to `useCollectionVariables``variables` and `control`.
649+
650+
### Options
651+
652+
All options accepted by `useCollectionVariables` are accepted here too. `tableMetadata` is optional but recommended for typed variables and correct URL round-tripping of typed field values (numbers and booleans are preserved correctly).
653+
654+
### URL format
655+
656+
| State | URL key | Example |
657+
| ---------- | ---------------------- | --------------------- |
658+
| `pageSize` | `p` | `?p=20` |
659+
| Sort | `s` | `?s=createdAt:desc` |
660+
| Filter | `f.<field>:<operator>` | `?f.status:eq=ACTIVE` |
661+
662+
### Custom search-params binding: `withURLCollectionState`
663+
664+
If you need URL persistence but cannot use react-router's `useSearchParams` (e.g. a different router or test environment), use the pure `withURLCollectionState` decorator to compose URL state with `useCollectionVariables` directly:
665+
666+
```tsx
667+
import { withURLCollectionState, useCollectionVariables } from "@tailor-platform/app-shell";
668+
669+
const [searchParams, setSearchParams] = useSearchParams();
670+
671+
const { variables, control } = useCollectionVariables(
672+
withURLCollectionState({ tableMetadata, params: { pageSize: 20 } }, [
673+
searchParams,
674+
setSearchParams,
675+
]),
676+
);
677+
```
678+
679+
`withURLCollectionState` returns augmented `useCollectionVariables` options — it does not call the hook itself. The `[searchParams, setSearchParams]` tuple must match the `URLSearchParams` + setter shape that `useSearchParams()` returns.
680+
632681
## `useDataTableContext`
633682

634683
Accesses the full DataTable state from any component rendered inside `DataTable.Root`. Use this to build custom sub-components when the built-in ones don't fit.
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
---
2+
title: DocumentProgressCard
3+
description: Generic card visualising a document's lifecycle state — optional percentage, stacked progress bar, and status legend
4+
---
5+
6+
# DocumentProgressCard
7+
8+
`DocumentProgressCard` is a generic, presentational card for communicating the lifecycle/fulfilment state of a document in a record detail right rail. It shows an optional completion percentage, a stacked progress bar, and a legend — driven by an arbitrary set of status `segments`.
9+
10+
It is view-only and domain-agnostic: pass the segments and an explicit `percent`. Any domain-specific math — deriving the percentage, or decomposing overlapping buckets into bar segments — lives in the consumer. See [Example: purchase-order fulfilment](#example-purchase-order-fulfilment) for a complete, copyable pattern.
11+
12+
## Import
13+
14+
```tsx
15+
import { DocumentProgressCard } from "@tailor-platform/app-shell";
16+
```
17+
18+
## Basic Usage
19+
20+
```tsx
21+
<DocumentProgressCard
22+
title="Shipment status"
23+
percent={60}
24+
segments={[
25+
{ label: "Shipped", value: 30, color: "green" },
26+
{ label: "Returned", value: 3, color: "red" },
27+
{ label: "Pending", value: 17, color: "neutral" },
28+
]}
29+
/>
30+
```
31+
32+
## Props
33+
34+
| Prop | Type | Default | Description |
35+
| ----------- | --------------------------- | ------------- | --------------------------------------------------------------------------------------------------------- |
36+
| `segments` | `DocumentProgressSegment[]` | **Required** | Status segments rendered as a stacked bar (and, by default, the legend). |
37+
| `title` | `React.ReactNode` | - | Optional card title shown top-left. |
38+
| `percent` | `number` | - | Optional headline percentage (0–100), shown top-right. Explicit — the generic card derives no progress. |
39+
| `legend` | `DocumentProgressSegment[]` | `segments` | Optional legend rows; override only when the legend should differ from the bar (see [Legend](#legend)). |
40+
| `total` | `number` | sum of values | Denominator used to size the bar. A value larger than the segment sum leaves an unfilled track remainder. |
41+
| `className` | `string` | - | Additional CSS classes for the card root. |
42+
43+
### `DocumentProgressSegment`
44+
45+
| Field | Type | Description |
46+
| ------- | ----------------------- | ------------------------------------------------------ |
47+
| `label` | `string` | Legend label. |
48+
| `value` | `number` | Amount — shown in the legend and used to size the bar. |
49+
| `color` | `DocumentProgressColor` | Bar / marker color (required). |
50+
51+
`DocumentProgressColor` is one of: `"indigo"`, `"pink"`, `"green"`, `"amber"`, `"red"`, `"blue"`, `"neutral"`.
52+
53+
## Progress Bar
54+
55+
The bar tiles `segments` left-to-right, each sized as `value / (total ?? sum of values)`. When `total` exceeds the segment sum, the shortfall renders as an empty `bg-muted` track — useful for a "remaining" portion you don't want as a colored segment. A `"neutral"` segment reads as a muted/track-like fill.
56+
57+
## Legend
58+
59+
By default the legend mirrors `segments`. Pass `legend` to render different rows — for example when buckets overlap and the bar shows a decomposition while the legend shows the raw figures:
60+
61+
```tsx
62+
<DocumentProgressCard
63+
percent={30}
64+
total={40}
65+
segments={[
66+
{ label: "Net received", value: 10, color: "indigo" },
67+
{ label: "Returned", value: 2, color: "pink" },
68+
]}
69+
legend={[
70+
{ label: "Received items", value: 12, color: "indigo" },
71+
{ label: "Returned items", value: 2, color: "pink" },
72+
{ label: "Yet to receive", value: 28, color: "neutral" },
73+
]}
74+
/>
75+
```
76+
77+
## Example: purchase-order fulfilment
78+
79+
A common use is communicating a purchase order's receiving state — **received**, **returned**, and **yet to receive**. `DocumentProgressCard` stays generic, so derive the percentage and the bar breakdown in your component and pass them in. This recipe reproduces the recommended look (labels, indigo/pink/neutral colors, and a bar that decomposes received into "kept" + "returned" against the ordered total):
80+
81+
```tsx
82+
function PurchaseOrderFulfilment({
83+
received,
84+
returned,
85+
yetToReceive,
86+
// Whether returned items still count as fulfilled. Set false to subtract them.
87+
returnedCountsAsComplete = true,
88+
}: {
89+
received: number;
90+
returned: number;
91+
yetToReceive: number;
92+
returnedCountsAsComplete?: boolean;
93+
}) {
94+
// Returned is a subset of received; clamp so the breakdown can't exceed it.
95+
const effectiveReturned = Math.min(Math.max(returned, 0), Math.max(received, 0));
96+
const total = Math.max(received, 0) + Math.max(yetToReceive, 0); // the ordered quantity
97+
const complete = returnedCountsAsComplete ? received : received - effectiveReturned;
98+
const percent = total > 0 ? Math.round((complete / total) * 100) : 0;
99+
100+
return (
101+
<DocumentProgressCard
102+
title="Fulfilment rate"
103+
percent={percent}
104+
total={total} // ordered quantity — the shortfall renders as the "yet to receive" track
105+
// Bar: net received (kept) + returned. Yet-to-receive is the unfilled remainder.
106+
segments={[
107+
{ label: "Received items", value: received - effectiveReturned, color: "indigo" },
108+
{ label: "Returned items", value: effectiveReturned, color: "pink" },
109+
]}
110+
// Legend: the three buckets as-is, so "Received items" shows the full amount.
111+
legend={[
112+
{ label: "Received items", value: received, color: "indigo" },
113+
{ label: "Returned items", value: returned, color: "pink" },
114+
{ label: "Yet to receive", value: yetToReceive, color: "neutral" },
115+
]}
116+
/>
117+
);
118+
}
119+
120+
// <PurchaseOrderFulfilment received={12} returned={2} yetToReceive={28} /> → 30%
121+
```
122+
123+
Notes for adapting it:
124+
125+
- **Colors:** `indigo` (received), `pink` (returned), `neutral` (yet to receive) is the recommended set; swap any of the seven palette colors to fit your domain.
126+
- **Bar vs. legend:** the bar uses `received − returned` so the two colored segments don't double-count the returned items, while the `legend` override keeps "Received items" showing the full received figure.
127+
- **`total`:** pass the ordered quantity so the unfilled portion of the bar represents "yet to receive". Omit it (or use the segment sum) if you want a fully-tiled bar instead.
128+
- **Percentage policy:** flip `returnedCountsAsComplete` to decide whether returned items count toward completion.
129+
130+
## Input handling
131+
132+
Segment values are expected to be non-negative numbers. Non-finite or negative values are coerced to `0`, and `percent` is rounded and clamped to `[0, 100]`.
133+
134+
## Related
135+
136+
- [MetricCard](metric-card) — Compact KPI summary card.
137+
- [Card](card) — The underlying card primitive.

docs/app-shell/components/table.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ All other standard HTML `<table>` props are accepted.
6060

6161
All other sub-components (`Table.Header`, `Table.Body`, `Table.Footer`, `Table.Row`, `Table.Head`, `Table.Cell`, `Table.Caption`) accept `className` and their corresponding standard HTML element props.
6262

63+
`Table.Head` and `Table.Cell` also accept `align?: "left" | "center" | "right"` for semantic text alignment without relying on utility-class overrides.
64+
6365
## Examples
6466

6567
### With Footer
@@ -114,6 +116,44 @@ All other sub-components (`Table.Header`, `Table.Body`, `Table.Footer`, `Table.R
114116
</Table.Root>
115117
```
116118

119+
### Right-aligned numeric columns
120+
121+
```tsx
122+
<Table.Root>
123+
<Table.Header>
124+
<Table.Row>
125+
<Table.Head>Item</Table.Head>
126+
<Table.Head align="right">Amount</Table.Head>
127+
</Table.Row>
128+
</Table.Header>
129+
<Table.Body>
130+
<Table.Row>
131+
<Table.Cell>Widget A</Table.Cell>
132+
<Table.Cell align="right">123</Table.Cell>
133+
</Table.Row>
134+
</Table.Body>
135+
</Table.Root>
136+
```
137+
138+
### Center-aligned status column
139+
140+
```tsx
141+
<Table.Root>
142+
<Table.Header>
143+
<Table.Row>
144+
<Table.Head>Item</Table.Head>
145+
<Table.Head align="center">Status</Table.Head>
146+
</Table.Row>
147+
</Table.Header>
148+
<Table.Body>
149+
<Table.Row>
150+
<Table.Cell>Widget A</Table.Cell>
151+
<Table.Cell align="center">Active</Table.Cell>
152+
</Table.Row>
153+
</Table.Body>
154+
</Table.Root>
155+
```
156+
117157
### Constrained Height with Scroll
118158

119159
```tsx

0 commit comments

Comments
 (0)