@tailor-platform/app-shell 1.14.0 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,7 +11,7 @@ do:
11
11
  - The prose IS the interaction; any other controls only qualify how the text is submitted
12
12
  dont:
13
13
  - The text is one field among many on a record form — use form/modal, form/single-page or form/sectioned with a Textarea field
14
- - A chat input where Enter sends and there is no Discard — that is a different layout
14
+ - A chat input where Enter sends and there is no Discard — use the `AIChat` component instead
15
15
  ---
16
16
 
17
17
  # pattern/form/composer
@@ -124,7 +124,7 @@ export default function Composer({ onSubmit, submitting = false }: Props) {
124
124
  ## Constraints
125
125
 
126
126
  - The body control is `Textarea`, never `Input` — `Input` is locked to `h-9` and clips prose to one 36px line.
127
- - Size the body with `rows` (3–5 is the usual range). Don't reach for an `astw:h-*` / `astw:min-h-*` override: `astw:` utilities only resolve if that exact class was compiled into the AppShell package CSS, so an arbitrary one written in a consuming app silently does nothing.
127
+ - Size the body with `rows` (3–5 is the usual range). Do not override its height with classes: `astw:` is internal to AppShell, and `Textarea`'s supported sizing API is `rows`.
128
128
  - The `Textarea` needs an accessible name. There is usually no visible label in a composer, so pass `aria-label`; a placeholder is not a label.
129
129
  - One primary `Button` in the action row. Discard is `ghost`, and any left-side action is `ghost` too.
130
130
  - The action row is one row at every width — the left side shrinks, the buttons do not wrap under the body.
@@ -87,7 +87,7 @@ export default function MultiSelect({ orders, onArchive, onExport }: Props) {
87
87
  <Table.Root>
88
88
  <Table.Header>
89
89
  <Table.Row>
90
- <Table.Head className="astw:w-10">
90
+ <Table.Head className="w-10">
91
91
  <input
92
92
  type="checkbox"
93
93
  checked={selectedIds.size === orders.length && orders.length > 0}
@@ -1,201 +0,0 @@
1
- ---
2
- slug: pattern/detail/hero-with-actions
3
- name: Hero With Actions Detail
4
- category: pattern
5
- subcategory: detail
6
- description: Single-record detail view with workflow actions and activity timeline
7
- requiredImports:
8
- [
9
- Layout,
10
- Badge,
11
- Button,
12
- Menu,
13
- DescriptionCard,
14
- Card,
15
- Table,
16
- ActionPanel,
17
- ActivityCard,
18
- Grid,
19
- MetricCard,
20
- DocumentProgressCard,
21
- Alert,
22
- ]
23
- tags: [detail, actions, timeline, workflow, two-column]
24
- do:
25
- - Single-record detail view (Order #1234, Supplier ABC, Product SKU-42)
26
- - Record has a workflow with named actions (Approve, Cancel, Ship)
27
- - An activity/audit history is meaningful to surface
28
- dont:
29
- - The page primarily compares two grids or record sets (match/reconcile)
30
- - The record is purely informational with no actions and no history — use list/dense-scan row click into a sheet instead
31
- ---
32
-
33
- # pattern/detail/hero-with-actions
34
-
35
- ## When to Use
36
-
37
- - Single-record detail view (Order #1234, Supplier ABC, Product SKU-42)
38
- - Record has a workflow with named actions (Approve, Cancel, Ship)
39
- - An activity/audit history is meaningful to surface
40
-
41
- ## Layout
42
-
43
- ```
44
- +---------------------------------------------------------+
45
- | Layout.Header title [Badge] [Primary CTA] |
46
- +----------------------------------+----------------------+
47
- | Layout.Column (main) | Layout.Column right |
48
- | DescriptionCard Summary | ActionPanel |
49
- | - field : value | - Approve |
50
- | - field : value | - Cancel |
51
- | Card Attachments | |
52
- | - file rows | ActivityCard |
53
- | Card Line items | - history entry |
54
- | inline Table.Root | - history entry |
55
- | | (…N more side cards |
56
- | | per use case) |
57
- +----------------------------------+----------------------+
58
- ```
59
-
60
- Composition rules:
61
-
62
- - Both columns accept N cards based on the use case — add as many sections as the record requires.
63
- - Each main-column section sits in a `Card` container — except `DescriptionCard`, which already provides its own container and must not be double-wrapped in a `Card`.
64
- - Right-column cards (`ActionPanel`, `ActivityCard`, related summaries, etc.) are composed the same way. `ActionPanel` and `ActivityCard` are the most common; neither is mandatory.
65
-
66
- Responsive: <1024 collapses to single column with right column rendered below main; 1024–1280 two columns with narrow side; >1280 two columns at full width.
67
-
68
- ## Layout variants
69
-
70
- Choose the column structure from the **record** — the pattern is "hero + workflow + (optional) history," and the layout flexes. The skill never mandates a fixed column count.
71
-
72
- - **Main + right rail** (most common) — the record has workflow actions and/or activity/related summaries → right `Layout.Column area="right"` holds `ActionPanel`, `ActivityCard`, integration cards.
73
- - **Single main column** — the record is mostly descriptive with few/no workflow actions and no meaningful history → one `Layout.Column`. Don't invent a rail just to fill space.
74
- - **Main + left rail** (`area="left"`, wider 320px) — when a persistent section index / summary should lead the reading order.
75
- - Either column holds **N cards** as the record requires.
76
-
77
- ## Optional cards
78
-
79
- Add these only when the record calls for them — none is mandatory. Each is a card like any other in the columns above.
80
-
81
- - **Metric strip** — when the record has headline KPIs (totals, counts), lead the main column with `MetricCard`s wrapped in a `Grid` (never one per row). See the `Grid` and `MetricCard` entries in `components.md`.
82
-
83
- ```tsx
84
- <Grid columns={{ initial: 1, md: 2, xl: 4 }} gap={4}>
85
- <MetricCard title="Net total" value="$12,480" trend={{ direction: "up", value: "+4%" }} />
86
- <MetricCard title="Lines" value="18" />
87
- <MetricCard title="Received" value="11" />
88
- <MetricCard title="Outstanding" value="7" />
89
- </Grid>
90
- ```
91
-
92
- - **`DocumentProgressCard`** — when the record has a fulfilment / lifecycle breakdown (received vs returned vs pending, shipment status). Sits in the main column or the right rail; derive `percent` and `segments` in the consumer.
93
- - **`Alert`** — a record-level inline banner (blocking error, on-hold warning) above the columns; use the **error** variant for the pattern's required error state. Transient feedback uses `useToast`, not `Alert`.
94
-
95
- ## Page Implementation
96
-
97
- ```tsx
98
- /* pattern: detail/hero-with-actions */
99
- import {
100
- Layout,
101
- Badge,
102
- Button,
103
- Card,
104
- DescriptionCard,
105
- Table,
106
- ActionPanel,
107
- ActivityCard,
108
- } from "@tailor-platform/app-shell";
109
- import type { Order } from "./mock";
110
-
111
- type Props = {
112
- order: Order;
113
- onApprove: () => void;
114
- onCancel: () => void;
115
- };
116
-
117
- export default function HeroWithActionsDetail({ order, onApprove, onCancel }: Props) {
118
- return (
119
- <Layout>
120
- <Layout.Header
121
- title={`Order ${order.number}`}
122
- actions={[
123
- <Badge key="status" variant="success">
124
- {order.status}
125
- </Badge>,
126
- <Button key="edit">Edit</Button>,
127
- ]}
128
- />
129
- <Layout.Column>
130
- <DescriptionCard
131
- title="Summary"
132
- data={order}
133
- columns={3}
134
- fields={[
135
- { key: "number", label: "Order number" },
136
- {
137
- key: "status",
138
- label: "Status",
139
- type: "badge",
140
- meta: { badgeVariantMap: { Confirmed: "success", Draft: "neutral" } },
141
- },
142
- { key: "customer", label: "Customer" },
143
- { key: "total", label: "Total" },
144
- ]}
145
- />
146
- <Card.Root>
147
- <Card.Header title="Line items" />
148
- <Card.Content className="astw:px-0">
149
- <Table.Root containerClassName="astw:px-6">
150
- <Table.Header>
151
- <Table.Row>
152
- <Table.Head>SKU</Table.Head>
153
- <Table.Head align="right">Qty</Table.Head>
154
- <Table.Head align="right">Total</Table.Head>
155
- </Table.Row>
156
- </Table.Header>
157
- <Table.Body>
158
- {order.lineItems.map((item) => (
159
- <Table.Row key={item.id}>
160
- <Table.Cell>{item.sku}</Table.Cell>
161
- <Table.Cell align="right">{item.qty}</Table.Cell>
162
- <Table.Cell align="right">${item.total.toLocaleString()}</Table.Cell>
163
- </Table.Row>
164
- ))}
165
- </Table.Body>
166
- </Table.Root>
167
- </Card.Content>
168
- </Card.Root>
169
- </Layout.Column>
170
- <Layout.Column area="right">
171
- <ActionPanel
172
- title="Actions"
173
- actions={[
174
- { key: "approve", label: "Approve", icon: <span>✓</span>, onClick: onApprove },
175
- { key: "cancel", label: "Cancel", icon: <span>✕</span>, onClick: onCancel },
176
- ]}
177
- />
178
- <ActivityCard title="Activity" items={order.activities} />
179
- </Layout.Column>
180
- </Layout>
181
- );
182
- }
183
- ```
184
-
185
- ## Constraints
186
-
187
- - **Header carries the single primary CTA + the status `Badge`** — not workflow actions. Workflow actions (Approve, Reject, Archive, …) live in the `ActionPanel`. If the header needs more than ~2 actions, move the overflow into a `Menu`.
188
- - **Never duplicate an action** across `Layout.Header` and `ActionPanel` — each action has exactly one home.
189
- - Every content section MUST sit inside `Card.Root` (or `DescriptionCard`, which already self-contains). Raw divs are not allowed.
190
- - `ActionPanel` is workflow-only — never back-navigation (that lives in the breadcrumb).
191
- - `Table.Root` inside a Card requires `containerClassName="astw:px-6"`.
192
-
193
- ## Anti-patterns
194
-
195
- - No status `Badge` on stateful entities — users can't tell where the record is in its lifecycle.
196
- - `ActionPanel` mixed with metadata in the same card — keep workflow separate from descriptive fields.
197
- - Bare `<div>` sections in the main column — every content section MUST sit inside `Card.Root`.
198
- - `ActionPanel` containing back-navigation (e.g. "Back to Product List") — back navigation lives in `Layout.Header`'s breadcrumb.
199
- - A `Table.Root` inside a Card without `containerClassName="astw:px-6"` — the first column lands flush against the card edge.
200
- - The same action in both `Layout.Header` and the `ActionPanel` — duplicating it makes neither read as canonical. Pick one home.
201
- - Giving every status the same loud weight — the record's primary/lifecycle status is a **filled** semantic badge; secondary statuses (fulfilment, billing) use **`outline-*`** (see `design-system.md` → Composition & emphasis rules).