astryx-dracula 0.5.0 → 0.6.2
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.
- package/BRAND.md +3 -3
- package/README.md +7 -4
- package/USAGE.md +49 -49
- package/astryx-dracula.d.ts +2 -2
- package/astryx-dracula.js +25 -9
- package/astryx-dracula.variants.d.ts +2 -2
- package/astryx.integration.mjs +1 -1
- package/icons.mjs +21 -17
- package/icons.tsx +69 -62
- package/package.json +125 -120
- package/shared/auth-chrome-config.ts +6 -6
- package/shared/auth-chrome.tsx +143 -144
- package/shared/auth-copy.ts +14 -14
- package/shared/chaptered-doc-config.ts +48 -51
- package/shared/chaptered-doc.tsx +267 -289
- package/shared/chart-hues.ts +25 -10
- package/shared/chart-labels.tsx +31 -30
- package/shared/chart-legend.tsx +55 -56
- package/shared/chart-panel-style.ts +3 -3
- package/shared/data-bar.tsx +97 -102
- package/shared/format-money.ts +1 -1
- package/shared/gallery-image.ts +6 -6
- package/shared/login-demo.ts +1 -1
- package/shared/metric-card.tsx +101 -105
- package/shared/metric-delta.tsx +28 -27
- package/shared/order-desk.tsx +205 -216
- package/shared/revenue-chart.tsx +145 -165
- package/shared/scene-castle.tsx +206 -291
- package/shared/scene-frame.tsx +33 -32
- package/shared/scene-hues.ts +17 -17
- package/shared/scene-tile.tsx +88 -92
- package/shared/settings-data.ts +119 -125
- package/shared/settings-rows.tsx +122 -159
- package/shared/sparkline.tsx +75 -88
- package/shared/sso-icons.tsx +10 -22
- package/shared/stars.tsx +16 -25
- package/templates/ai-chat-landing.template.mjs +5 -4
- package/templates/ai-chat-landing.tsx +473 -460
- package/templates/ai-chat.template.mjs +5 -4
- package/templates/ai-chat.tsx +543 -612
- package/templates/blank.template.mjs +5 -4
- package/templates/blank.tsx +18 -14
- package/templates/centered-hero.template.mjs +5 -4
- package/templates/centered-hero.tsx +55 -68
- package/templates/classic-gallery.template.mjs +5 -4
- package/templates/classic-gallery.tsx +97 -104
- package/templates/contact-form.template.mjs +5 -4
- package/templates/contact-form.tsx +247 -282
- package/templates/dashboard-portfolio.template.mjs +5 -4
- package/templates/dashboard-portfolio.tsx +717 -548
- package/templates/dashboard.template.mjs +5 -4
- package/templates/dashboard.tsx +596 -567
- package/templates/detail-page.template.mjs +5 -4
- package/templates/detail-page.tsx +546 -603
- package/templates/documentation-design.template.mjs +5 -4
- package/templates/documentation-design.tsx +231 -243
- package/templates/documentation-technical.template.mjs +5 -4
- package/templates/documentation-technical.tsx +236 -239
- package/templates/documentation.template.mjs +5 -4
- package/templates/documentation.tsx +267 -279
- package/templates/editor.template.mjs +5 -4
- package/templates/editor.tsx +844 -877
- package/templates/file-explorer.template.mjs +5 -4
- package/templates/file-explorer.tsx +566 -590
- package/templates/form-two-column.template.mjs +5 -4
- package/templates/form-two-column.tsx +195 -210
- package/templates/gallery-hero.template.mjs +5 -4
- package/templates/gallery-hero.tsx +93 -114
- package/templates/ide.template.mjs +5 -4
- package/templates/ide.tsx +407 -453
- package/templates/incident-console.template.mjs +5 -4
- package/templates/incident-console.tsx +593 -575
- package/templates/kanban-board.template.mjs +5 -4
- package/templates/kanban-board.tsx +665 -683
- package/templates/library.template.mjs +5 -4
- package/templates/library.tsx +409 -430
- package/templates/login-card.template.mjs +5 -4
- package/templates/login-card.tsx +63 -72
- package/templates/login-split.template.mjs +5 -4
- package/templates/login-split.tsx +192 -193
- package/templates/login-sso.template.mjs +5 -4
- package/templates/login-sso.tsx +257 -258
- package/templates/login.template.mjs +5 -4
- package/templates/login.tsx +53 -49
- package/templates/messaging-shell.template.mjs +5 -4
- package/templates/messaging-shell.tsx +556 -574
- package/templates/mixed-gallery.template.mjs +5 -4
- package/templates/mixed-gallery.tsx +60 -58
- package/templates/payment-form.template.mjs +5 -4
- package/templates/payment-form.tsx +882 -991
- package/templates/product-detail.template.mjs +5 -4
- package/templates/product-detail.tsx +261 -272
- package/templates/product-gallery.template.mjs +5 -4
- package/templates/product-gallery.tsx +126 -137
- package/templates/product-tour.template.mjs +5 -4
- package/templates/product-tour.tsx +741 -773
- package/templates/settings-dialog.template.mjs +5 -4
- package/templates/settings-dialog.tsx +688 -717
- package/templates/settings-sidebar.template.mjs +5 -4
- package/templates/settings-sidebar.tsx +762 -807
- package/templates/settings.template.mjs +5 -4
- package/templates/settings.tsx +289 -304
- package/templates/shell-nav.template.mjs +5 -4
- package/templates/shell-nav.tsx +212 -219
- package/templates/shell-side-nav.template.mjs +5 -4
- package/templates/shell-side-nav.tsx +135 -157
- package/templates/shell-top-nav.template.mjs +5 -4
- package/templates/shell-top-nav.tsx +182 -203
- package/templates/side-gallery.template.mjs +5 -4
- package/templates/side-gallery.tsx +85 -90
- package/templates/table-grouped.template.mjs +5 -4
- package/templates/table-grouped.tsx +1000 -1053
- package/templates/table-page-chart.template.mjs +5 -4
- package/templates/table-page-chart.tsx +400 -398
- package/templates/table-page-heatmap-status.template.mjs +5 -4
- package/templates/table-page-heatmap-status.tsx +483 -512
- package/templates/table-page-shoe-store-heatmap.template.mjs +5 -4
- package/templates/table-page-shoe-store-heatmap.tsx +748 -746
- package/templates/table-page.template.mjs +5 -4
- package/templates/table-page.tsx +396 -405
- package/templates/table.template.mjs +5 -4
- package/templates/table.tsx +92 -93
- package/templates/tech-report.template.mjs +5 -4
- package/templates/tech-report.tsx +1316 -1304
- package/templates/theme-showcase.template.mjs +5 -4
- package/templates/theme-showcase.tsx +871 -951
- package/theme.css +35 -77
- package/theme.css.d.ts +9 -0
- package/tokens.css +373 -380
|
@@ -1,48 +1,49 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
-
// XLE (canonical structure
|
|
3
|
-
//
|
|
2
|
+
// XLE (canonical structure of the tree below; the CLI had no XLE validator
|
|
3
|
+
// after `astryx layout` was removed in 0.7.0, so this is a reading aid and a
|
|
4
|
+
// human drift check — update it with the file, never alone):
|
|
5
|
+
// L[h=fill mw=1000] > (LH[divider p=6] > V[g=3] > ((H[g=4 a=start] > (SI[fill] > V[g=2] > (Lk"All orders" + (V[g=0] > Hd"#1001"[level=1] + (H[g=1 a=center wrap] > Tx"5 ordered items"[t=supporting] + (H[g=1 a=center] > Bu + Av"Vlad Dracul" + Tx"Vlad Dracul"[t=supporting]) + (H[g=1 a=center] > Bu + SD"Unfulfilled" + Tx"Unfulfilled"[t=supporting]) + (H[g=1 a=center] > Bu + Ic + Tx"02/23/2026"[t=supporting]) + (H[g=1 a=center] > Bu + Ic + Tx"Needs attention"[t=supporting]) + (H[g=1 a=center] > Bu + Lk"See all")))) + (H[g=2] > B.secondary"Restock" + B.secondary"Edit")) + (H[g=2] > (SI[fill] > V[a=stretch] > B.secondary"Restock") + (SI[fill] > V[a=stretch] > B.secondary"Edit")) + (H[a=center] > (SI[fill] > TL[size=lg] > (Tab"Details"! + Tab"Invoices" + Tab"Timeline" + TabMenu"More")) + B.ghost))) + (LC > V[g=4] > ((S > V[g=4] > ((H[g=2 a=center wrap] > (SI[fill] > (H[g=2 a=center] > Hd"Items"[level=2] + SD"Unfulfilled" + Tx"Unfulfilled"[t=supporting])) + (H[g=2] > B.ghost"Fulfill item" + B.secondary"Create label")) + (UL[density=spacious edge=outset] > LI*5)) + (S > V[g=4] > ((H[g=2 a=center wrap] > (SI[fill] > (H[g=2 a=center] > Hd"Invoice"[level=2] + SD"Paid" + Tx"Paid"[t=supporting])) + (H[g=2] > B.ghost"Refund" + B.secondary"Send Invoice")) + (ML > MLI*5) + D + (ML > MLI*1))) + (S > V[g=4] > ((H[g=2 a=center] > (SI[fill] > Hd"Timeline"[level=2]) + B.ghost"Filters") + (UL[density=spacious edge=outset] > LI*4)))) + (LP[w=320 p=4] > V[g=4] > Col"Notes" + Col"Customer" + Col"Fraud analysis")) ;; Dlg[variant=fullscreen] > L[h=auto] > (DH"Order details" + (LC[p=4] > V[g=4] > Col"Notes" + Col"Customer" + Col"Fraud analysis"))
|
|
4
6
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
+
import { Avatar } from "@astryxdesign/core/Avatar";
|
|
8
|
+
import { Button } from "@astryxdesign/core/Button";
|
|
9
|
+
import { Collapsible } from "@astryxdesign/core/Collapsible";
|
|
10
|
+
import { Dialog, DialogHeader } from "@astryxdesign/core/Dialog";
|
|
11
|
+
import { Divider } from "@astryxdesign/core/Divider";
|
|
12
|
+
import { useMediaQuery } from "@astryxdesign/core/hooks";
|
|
13
|
+
import { Icon } from "@astryxdesign/core/Icon";
|
|
7
14
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
} from
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {Collapsible} from '@astryxdesign/core/Collapsible';
|
|
28
|
-
import {Icon} from '@astryxdesign/core/Icon';
|
|
29
|
-
import {Dialog, DialogHeader} from '@astryxdesign/core/Dialog';
|
|
30
|
-
import type {SceneHue} from 'astryx-dracula/shared/scene-hues';
|
|
31
|
-
import {SceneFrame} from 'astryx-dracula/shared/scene-frame';
|
|
32
|
-
import {formatMoney} from 'astryx-dracula/shared/format-money';
|
|
15
|
+
HStack,
|
|
16
|
+
Layout,
|
|
17
|
+
LayoutContent,
|
|
18
|
+
LayoutHeader,
|
|
19
|
+
LayoutPanel,
|
|
20
|
+
Section,
|
|
21
|
+
StackItem,
|
|
22
|
+
VStack,
|
|
23
|
+
} from "@astryxdesign/core/Layout";
|
|
24
|
+
import { Link } from "@astryxdesign/core/Link";
|
|
25
|
+
import { List, ListItem } from "@astryxdesign/core/List";
|
|
26
|
+
import { MetadataList, MetadataListItem } from "@astryxdesign/core/MetadataList";
|
|
27
|
+
import { ProgressBar } from "@astryxdesign/core/ProgressBar";
|
|
28
|
+
import { StatusDot } from "@astryxdesign/core/StatusDot";
|
|
29
|
+
import { Tab, TabList, TabMenu } from "@astryxdesign/core/TabList";
|
|
30
|
+
import { Heading, Text } from "@astryxdesign/core/Text";
|
|
31
|
+
import { formatMoney } from "astryx-dracula/shared/format-money";
|
|
32
|
+
import { SceneFrame } from "astryx-dracula/shared/scene-frame";
|
|
33
|
+
import type { SceneHue } from "astryx-dracula/shared/scene-hues";
|
|
33
34
|
import {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
} from
|
|
43
|
-
|
|
35
|
+
ArrowLeft,
|
|
36
|
+
Calendar,
|
|
37
|
+
Columns2,
|
|
38
|
+
Filter,
|
|
39
|
+
Flag,
|
|
40
|
+
Heart,
|
|
41
|
+
SquarePen,
|
|
42
|
+
ThumbsUp,
|
|
43
|
+
} from "lucide-react";
|
|
44
44
|
// ─── Styles ─────────────────────────────────────────────────────────────────
|
|
45
|
-
import type {CSSProperties} from
|
|
45
|
+
import type { CSSProperties } from "react";
|
|
46
|
+
import { useState } from "react";
|
|
46
47
|
|
|
47
48
|
// The only custom CSS in this template is small optical-alignment negative
|
|
48
49
|
// margins: LayoutHeader/TabList have no edge-dock prop (#2622) and List
|
|
@@ -51,50 +52,48 @@ import type {CSSProperties} from 'react';
|
|
|
51
52
|
|
|
52
53
|
// Bleed the tab bar to the header's content edges so the active-tab underline
|
|
53
54
|
// meets the header divider. No edge-dock prop on TabList (#2622).
|
|
55
|
+
//
|
|
56
|
+
// These are written as spacing-token arithmetic, not raw pixels: core's
|
|
57
|
+
// --spacing-* scale is `rem` since 0.7.0, so a literal -12px stops matching
|
|
58
|
+
// the padding it cancels at any root font size other than 16px.
|
|
54
59
|
const tabsRow: CSSProperties = {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
};
|
|
59
|
-
// Pull the list items' inner padding back so their content optically aligns
|
|
60
|
-
// with the section heading above (ListItem insets content by ~8px). No
|
|
61
|
-
// edge/inset prop on List (#2626).
|
|
62
|
-
const itemsList: CSSProperties = {
|
|
63
|
-
marginInline: -8,
|
|
60
|
+
marginInline: "calc(var(--spacing-3) * -1)",
|
|
61
|
+
marginBottom: "calc(var(--spacing-4) * -1)",
|
|
62
|
+
marginTop: "var(--spacing-3)",
|
|
64
63
|
};
|
|
65
64
|
|
|
66
65
|
// ─── Product data ───────────────────────────────────────────────────────────
|
|
67
66
|
const PRODUCTS = [
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
67
|
+
{
|
|
68
|
+
name: "Midnight Mug",
|
|
69
|
+
details: "Glaze: Moonlight\nFinish: Matte",
|
|
70
|
+
price: 89.0,
|
|
71
|
+
qty: 1,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: "Cinder Bowl",
|
|
75
|
+
details: "Glaze: Sage\nSize: 6 in",
|
|
76
|
+
price: 42.0,
|
|
77
|
+
qty: 2,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: "Crypt Cup",
|
|
81
|
+
details: "Glaze: Batwing\nSize: 14 in",
|
|
82
|
+
price: 65.0,
|
|
83
|
+
qty: 1,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: "Dusk Plate Set",
|
|
87
|
+
details: "Glaze: Charcoal\nCapacity: 3 oz",
|
|
88
|
+
price: 34.0,
|
|
89
|
+
qty: 3,
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: "Coven Salad Bowl",
|
|
93
|
+
details: "Glaze: Moonlight\nHeight: 8 in",
|
|
94
|
+
price: 78.0,
|
|
95
|
+
qty: 1,
|
|
96
|
+
},
|
|
98
97
|
];
|
|
99
98
|
|
|
100
99
|
// Per-product art tint, read positionally out of a never-reordered array so
|
|
@@ -105,41 +104,36 @@ const PRODUCTS = [
|
|
|
105
104
|
// The art-treatment remit bans purple, so the leading entry changes to cyan on
|
|
106
105
|
// the same pass.
|
|
107
106
|
const PRODUCT_HUES: readonly SceneHue[] = [
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
107
|
+
"var(--dracula-cyan)",
|
|
108
|
+
"var(--dracula-cyan)",
|
|
109
|
+
"var(--dracula-pink)",
|
|
110
|
+
"var(--dracula-yellow)",
|
|
111
|
+
"var(--dracula-green)",
|
|
113
112
|
];
|
|
114
113
|
|
|
115
114
|
const sigilClip: CSSProperties = {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
115
|
+
width: 40,
|
|
116
|
+
height: 40,
|
|
117
|
+
overflow: "clip",
|
|
118
|
+
flexShrink: 0,
|
|
120
119
|
};
|
|
121
120
|
|
|
122
|
-
function ProductSigil({index}: {name: string; index: number}) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
fill="var(--dracula-current-line)"
|
|
139
|
-
/>
|
|
140
|
-
</SceneFrame>
|
|
141
|
-
</div>
|
|
142
|
-
);
|
|
121
|
+
function ProductSigil({ index }: { name: string; index: number }) {
|
|
122
|
+
const hue = PRODUCT_HUES[index % PRODUCT_HUES.length];
|
|
123
|
+
return (
|
|
124
|
+
<div style={sigilClip}>
|
|
125
|
+
<SceneFrame
|
|
126
|
+
label={`${PRODUCTS[index % PRODUCTS.length].name} sigil`}
|
|
127
|
+
viewBox="0 0 40 40"
|
|
128
|
+
hasBackdrop={false}
|
|
129
|
+
>
|
|
130
|
+
<rect width="40" height="40" fill="var(--dracula-bg-light)" />
|
|
131
|
+
<circle cx={28} cy={11} r={7} fill={hue} />
|
|
132
|
+
<circle cx={25} cy={9} r={5.5} fill="var(--dracula-bg-light)" opacity={0.5} />
|
|
133
|
+
<path d="M0 28 Q10 22 20 26 T40 24 V40 H0 Z" fill="var(--dracula-current-line)" />
|
|
134
|
+
</SceneFrame>
|
|
135
|
+
</div>
|
|
136
|
+
);
|
|
143
137
|
}
|
|
144
138
|
|
|
145
139
|
const SUBTOTAL = PRODUCTS.reduce((sum, p) => sum + p.price * p.qty, 0);
|
|
@@ -151,554 +145,503 @@ const TOTAL = SUBTOTAL - DISCOUNT + SHIPPING + TAX;
|
|
|
151
145
|
|
|
152
146
|
// ─── Activity data ──────────────────────────────────────────────────────────
|
|
153
147
|
const ACTIVITY = [
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
reactions: 1,
|
|
183
|
-
time: 'Feb 23 at 2:15 PM',
|
|
184
|
-
},
|
|
148
|
+
{
|
|
149
|
+
type: "event" as const,
|
|
150
|
+
user: "Vlad Dracul",
|
|
151
|
+
text: "placed order #1001",
|
|
152
|
+
reactions: 2,
|
|
153
|
+
time: "Feb 23 at 9:12 AM",
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
type: "comment" as const,
|
|
157
|
+
user: "Alex Rivera",
|
|
158
|
+
text: "Customer requested gift wrapping for the mug & plate set. I've added a note to the packing slip. Warehouse team should wrap in recycled kraft paper.",
|
|
159
|
+
reactions: 3,
|
|
160
|
+
time: "Feb 23 at 10:45 AM",
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
type: "update" as const,
|
|
164
|
+
user: "System",
|
|
165
|
+
text: "has several information changes",
|
|
166
|
+
time: "Feb 23 at 11:30 AM",
|
|
167
|
+
changes: ["Payment verified via Visa …7482", "Fraud check passed, low risk"],
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
type: "event" as const,
|
|
171
|
+
user: "Alex Rivera",
|
|
172
|
+
text: "marked order as ready for fulfillment",
|
|
173
|
+
reactions: 1,
|
|
174
|
+
time: "Feb 23 at 2:15 PM",
|
|
175
|
+
},
|
|
185
176
|
];
|
|
186
177
|
|
|
187
178
|
// ─── Bullet separator ───────────────────────────────────────────────────────
|
|
188
179
|
function Bullet() {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
180
|
+
return (
|
|
181
|
+
<Text type="supporting" color="secondary" aria-hidden="true">
|
|
182
|
+
{"・"}
|
|
183
|
+
</Text>
|
|
184
|
+
);
|
|
194
185
|
}
|
|
195
186
|
|
|
196
187
|
// ─── Page Header ────────────────────────────────────────────────────────────
|
|
197
188
|
function PageHeader({
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
189
|
+
activeTab,
|
|
190
|
+
onTabChange,
|
|
191
|
+
isPanelOpen,
|
|
192
|
+
onTogglePanel,
|
|
193
|
+
isNarrow,
|
|
203
194
|
}: {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
195
|
+
activeTab: string;
|
|
196
|
+
onTabChange: (v: string) => void;
|
|
197
|
+
isPanelOpen: boolean;
|
|
198
|
+
onTogglePanel: () => void;
|
|
199
|
+
isNarrow: boolean;
|
|
209
200
|
}) {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
201
|
+
return (
|
|
202
|
+
<LayoutHeader hasDivider padding={6}>
|
|
203
|
+
<VStack gap={3}>
|
|
204
|
+
<HStack gap={4} vAlign="start">
|
|
205
|
+
<StackItem size="fill">
|
|
206
|
+
{/* gap={2}: the breadcrumb is a control, not a text-baseline
|
|
216
207
|
pairing, so it needs separation from the H1 rather than 0. */}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
208
|
+
<VStack gap={2}>
|
|
209
|
+
<Link href="#/templates/detail-page">
|
|
210
|
+
<HStack gap={1} vAlign="center">
|
|
211
|
+
<Icon icon={ArrowLeft} size="sm" color="inherit" />
|
|
212
|
+
All orders
|
|
213
|
+
</HStack>
|
|
214
|
+
</Link>
|
|
215
|
+
<VStack gap={0}>
|
|
216
|
+
<Heading level={1} maxLines={1}>
|
|
217
|
+
#1001
|
|
218
|
+
</Heading>
|
|
219
|
+
{/* Metadata wraps to multiple lines on narrow screens (rather
|
|
229
220
|
than collapsing items behind a "+N more" overflow). */}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
<HStack gap={2}>
|
|
278
|
-
<Button label="Restock" variant="secondary" />
|
|
279
|
-
<Button label="Edit" variant="secondary" />
|
|
280
|
-
</HStack>
|
|
281
|
-
)}
|
|
282
|
-
</HStack>
|
|
221
|
+
<HStack gap={1} vAlign="center" wrap="wrap">
|
|
222
|
+
<Text type="supporting" color="secondary" hasTabularNumbers>
|
|
223
|
+
{PRODUCTS.length} ordered items
|
|
224
|
+
</Text>
|
|
225
|
+
<HStack gap={1} vAlign="center">
|
|
226
|
+
<Bullet />
|
|
227
|
+
<Avatar name="Vlad Dracul" size="sm" />
|
|
228
|
+
<Text type="supporting" color="secondary" maxLines={1}>
|
|
229
|
+
Vlad Dracul
|
|
230
|
+
</Text>
|
|
231
|
+
</HStack>
|
|
232
|
+
<HStack gap={1} vAlign="center">
|
|
233
|
+
<Bullet />
|
|
234
|
+
<StatusDot variant="warning" label="Unfulfilled" />
|
|
235
|
+
<Text type="supporting" color="secondary">
|
|
236
|
+
Unfulfilled
|
|
237
|
+
</Text>
|
|
238
|
+
</HStack>
|
|
239
|
+
<HStack gap={1} vAlign="center">
|
|
240
|
+
<Bullet />
|
|
241
|
+
<Icon icon={Calendar} size="sm" color="secondary" />
|
|
242
|
+
<Text type="supporting" color="secondary" maxLines={1} hasTabularNumbers>
|
|
243
|
+
02/23/2026
|
|
244
|
+
</Text>
|
|
245
|
+
</HStack>
|
|
246
|
+
<HStack gap={1} vAlign="center">
|
|
247
|
+
<Bullet />
|
|
248
|
+
<Icon icon={Flag} size="sm" color="secondary" />
|
|
249
|
+
<Text type="supporting" color="secondary" maxLines={1}>
|
|
250
|
+
Needs attention
|
|
251
|
+
</Text>
|
|
252
|
+
</HStack>
|
|
253
|
+
<HStack gap={1} vAlign="center">
|
|
254
|
+
<Bullet />
|
|
255
|
+
<Link href="#/templates/detail-page">See all</Link>
|
|
256
|
+
</HStack>
|
|
257
|
+
</HStack>
|
|
258
|
+
</VStack>
|
|
259
|
+
</VStack>
|
|
260
|
+
</StackItem>
|
|
261
|
+
{!isNarrow && (
|
|
262
|
+
<HStack gap={2}>
|
|
263
|
+
<Button label="Restock" variant="secondary" />
|
|
264
|
+
<Button label="Edit" variant="secondary" />
|
|
265
|
+
</HStack>
|
|
266
|
+
)}
|
|
267
|
+
</HStack>
|
|
283
268
|
|
|
284
|
-
|
|
269
|
+
{/* Mobile: actions drop below the metadata as a full-width row. The
|
|
285
270
|
VStack hAlign="stretch" wrapper is the full-width-button
|
|
286
271
|
workaround — Button has no full-width prop (#2600). */}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
272
|
+
{isNarrow && (
|
|
273
|
+
<HStack gap={2}>
|
|
274
|
+
<StackItem size="fill">
|
|
275
|
+
<VStack hAlign="stretch">
|
|
276
|
+
<Button label="Restock" variant="secondary" />
|
|
277
|
+
</VStack>
|
|
278
|
+
</StackItem>
|
|
279
|
+
<StackItem size="fill">
|
|
280
|
+
<VStack hAlign="stretch">
|
|
281
|
+
<Button label="Edit" variant="secondary" />
|
|
282
|
+
</VStack>
|
|
283
|
+
</StackItem>
|
|
284
|
+
</HStack>
|
|
285
|
+
)}
|
|
301
286
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
287
|
+
<HStack vAlign="center" style={tabsRow}>
|
|
288
|
+
<StackItem size="fill">
|
|
289
|
+
<TabList value={activeTab} onChange={onTabChange} size="lg">
|
|
290
|
+
<Tab value="details" label="Details" />
|
|
291
|
+
<Tab value="invoices" label="Invoices" />
|
|
292
|
+
<Tab value="timeline" label="Timeline" />
|
|
293
|
+
<TabMenu
|
|
294
|
+
label="More"
|
|
295
|
+
options={[
|
|
296
|
+
{ value: "customer", label: "Customer" },
|
|
297
|
+
{ value: "analysis", label: "Analysis" },
|
|
298
|
+
]}
|
|
299
|
+
/>
|
|
300
|
+
</TabList>
|
|
301
|
+
</StackItem>
|
|
302
|
+
<Button
|
|
303
|
+
label={isPanelOpen ? "Hide panel" : "Show panel"}
|
|
304
|
+
variant="ghost"
|
|
305
|
+
size="md"
|
|
306
|
+
icon={<Icon icon={Columns2} size="sm" />}
|
|
307
|
+
isIconOnly
|
|
308
|
+
onClick={onTogglePanel}
|
|
309
|
+
/>
|
|
310
|
+
</HStack>
|
|
311
|
+
</VStack>
|
|
312
|
+
</LayoutHeader>
|
|
313
|
+
);
|
|
329
314
|
}
|
|
330
315
|
|
|
331
316
|
// ─── Items Card ─────────────────────────────────────────────────────────────
|
|
332
317
|
function ItemsCard() {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
318
|
+
return (
|
|
319
|
+
<Section>
|
|
320
|
+
<VStack gap={4}>
|
|
321
|
+
<HStack vAlign="center" gap={2} wrap="wrap">
|
|
322
|
+
<StackItem size="fill">
|
|
323
|
+
<HStack gap={2} vAlign="center">
|
|
324
|
+
<Heading level={2}>Items</Heading>
|
|
325
|
+
<StatusDot variant="warning" label="Unfulfilled" />
|
|
326
|
+
<Text type="supporting" color="secondary">
|
|
327
|
+
Unfulfilled
|
|
328
|
+
</Text>
|
|
329
|
+
</HStack>
|
|
330
|
+
</StackItem>
|
|
331
|
+
<HStack gap={2}>
|
|
332
|
+
<Button label="Fulfill item" variant="ghost" />
|
|
333
|
+
<Button label="Create label" variant="secondary" />
|
|
334
|
+
</HStack>
|
|
335
|
+
</HStack>
|
|
351
336
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
type="supporting"
|
|
384
|
-
color="secondary"
|
|
385
|
-
maxLines={1}
|
|
386
|
-
hasTabularNumbers>
|
|
387
|
-
{formatMoney(product.price)} {'×'} {product.qty}
|
|
388
|
-
</Text>
|
|
389
|
-
</VStack>
|
|
390
|
-
}
|
|
391
|
-
/>
|
|
392
|
-
))}
|
|
393
|
-
</List>
|
|
394
|
-
</VStack>
|
|
395
|
-
</Section>
|
|
396
|
-
);
|
|
337
|
+
<List density="spacious" edge="outset">
|
|
338
|
+
{PRODUCTS.map((product, i) => (
|
|
339
|
+
<ListItem
|
|
340
|
+
key={product.name}
|
|
341
|
+
label={product.name}
|
|
342
|
+
description={
|
|
343
|
+
<VStack gap={0}>
|
|
344
|
+
{product.details.split("\n").map((line) => (
|
|
345
|
+
<Text key={line} type="supporting" color="secondary" hasTabularNumbers>
|
|
346
|
+
{line}
|
|
347
|
+
</Text>
|
|
348
|
+
))}
|
|
349
|
+
</VStack>
|
|
350
|
+
}
|
|
351
|
+
startContent={<ProductSigil name={product.name} index={i} />}
|
|
352
|
+
endContent={
|
|
353
|
+
<VStack gap={0} hAlign="end">
|
|
354
|
+
<Text type="body" weight="semibold" maxLines={1} hasTabularNumbers>
|
|
355
|
+
{formatMoney(product.price * product.qty)}
|
|
356
|
+
</Text>
|
|
357
|
+
<Text type="supporting" color="secondary" maxLines={1} hasTabularNumbers>
|
|
358
|
+
{formatMoney(product.price)} {"×"} {product.qty}
|
|
359
|
+
</Text>
|
|
360
|
+
</VStack>
|
|
361
|
+
}
|
|
362
|
+
/>
|
|
363
|
+
))}
|
|
364
|
+
</List>
|
|
365
|
+
</VStack>
|
|
366
|
+
</Section>
|
|
367
|
+
);
|
|
397
368
|
}
|
|
398
369
|
|
|
399
370
|
// ─── Invoice Card ───────────────────────────────────────────────────────────
|
|
400
371
|
function InvoiceCard() {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
372
|
+
return (
|
|
373
|
+
<Section>
|
|
374
|
+
<VStack gap={4}>
|
|
375
|
+
<HStack vAlign="center" gap={2} wrap="wrap">
|
|
376
|
+
<StackItem size="fill">
|
|
377
|
+
<HStack gap={2} vAlign="center">
|
|
378
|
+
<Heading level={2}>Invoice</Heading>
|
|
379
|
+
<StatusDot variant="success" label="Paid" />
|
|
380
|
+
<Text type="supporting" color="secondary">
|
|
381
|
+
Paid
|
|
382
|
+
</Text>
|
|
383
|
+
</HStack>
|
|
384
|
+
</StackItem>
|
|
385
|
+
<HStack gap={2}>
|
|
386
|
+
<Button label="Refund" variant="ghost" />
|
|
387
|
+
<Button label="Send Invoice" variant="secondary" />
|
|
388
|
+
</HStack>
|
|
389
|
+
</HStack>
|
|
419
390
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
391
|
+
<MetadataList>
|
|
392
|
+
<MetadataListItem label="Subtotal">
|
|
393
|
+
<HStack>
|
|
394
|
+
<StackItem size="fill">
|
|
395
|
+
<Text type="body" hasTabularNumbers>
|
|
396
|
+
{PRODUCTS.length} items
|
|
397
|
+
</Text>
|
|
398
|
+
</StackItem>
|
|
399
|
+
<Text type="body" hasTabularNumbers>
|
|
400
|
+
{formatMoney(SUBTOTAL)}
|
|
401
|
+
</Text>
|
|
402
|
+
</HStack>
|
|
403
|
+
</MetadataListItem>
|
|
404
|
+
<MetadataListItem label="Discount">
|
|
405
|
+
<HStack>
|
|
406
|
+
<StackItem size="fill">
|
|
407
|
+
<Text type="body">New customer code: NEW15</Text>
|
|
408
|
+
</StackItem>
|
|
409
|
+
<Text type="body" hasTabularNumbers>
|
|
410
|
+
– {formatMoney(DISCOUNT)}
|
|
411
|
+
</Text>
|
|
412
|
+
</HStack>
|
|
413
|
+
</MetadataListItem>
|
|
414
|
+
<MetadataListItem label="Shipping">
|
|
415
|
+
<HStack>
|
|
416
|
+
<StackItem size="fill">
|
|
417
|
+
<Text type="body" hasTabularNumbers>
|
|
418
|
+
Free shipping (0.0lbs) USPS
|
|
419
|
+
</Text>
|
|
420
|
+
</StackItem>
|
|
421
|
+
<Text type="body" hasTabularNumbers>
|
|
422
|
+
{formatMoney(SHIPPING)}
|
|
423
|
+
</Text>
|
|
424
|
+
</HStack>
|
|
425
|
+
</MetadataListItem>
|
|
426
|
+
<MetadataListItem label="Tax">
|
|
427
|
+
<HStack>
|
|
428
|
+
<StackItem size="fill">
|
|
429
|
+
<Text type="body" hasTabularNumbers>
|
|
430
|
+
Sales tax (8.25%)
|
|
431
|
+
</Text>
|
|
432
|
+
</StackItem>
|
|
433
|
+
<Text type="body" hasTabularNumbers>
|
|
434
|
+
{formatMoney(TAX)}
|
|
435
|
+
</Text>
|
|
436
|
+
</HStack>
|
|
437
|
+
</MetadataListItem>
|
|
438
|
+
<MetadataListItem label="Total">
|
|
439
|
+
<HStack>
|
|
440
|
+
<StackItem size="fill" />
|
|
441
|
+
<Text type="body" weight="semibold" hasTabularNumbers>
|
|
442
|
+
{formatMoney(TOTAL)}
|
|
443
|
+
</Text>
|
|
444
|
+
</HStack>
|
|
445
|
+
</MetadataListItem>
|
|
446
|
+
</MetadataList>
|
|
476
447
|
|
|
477
|
-
|
|
448
|
+
<Divider />
|
|
478
449
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
450
|
+
<MetadataList>
|
|
451
|
+
<MetadataListItem label="Paid by customer">
|
|
452
|
+
<HStack>
|
|
453
|
+
<StackItem size="fill">
|
|
454
|
+
<Text type="body" hasTabularNumbers>
|
|
455
|
+
Visa …7482
|
|
456
|
+
</Text>
|
|
457
|
+
</StackItem>
|
|
458
|
+
<Text type="body" hasTabularNumbers>
|
|
459
|
+
{formatMoney(TOTAL)}
|
|
460
|
+
</Text>
|
|
461
|
+
</HStack>
|
|
462
|
+
</MetadataListItem>
|
|
463
|
+
</MetadataList>
|
|
464
|
+
</VStack>
|
|
465
|
+
</Section>
|
|
466
|
+
);
|
|
496
467
|
}
|
|
497
468
|
|
|
498
469
|
// ─── Timeline ───────────────────────────────────────────────────────────────
|
|
499
470
|
function TimelineSection() {
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
471
|
+
return (
|
|
472
|
+
<Section>
|
|
473
|
+
<VStack gap={4}>
|
|
474
|
+
{/* gap={2}, matching the byte-identical header rows at :340 and
|
|
504
475
|
:408. Core's Stack has no default gap (Stack.tsx:211, pass-through
|
|
505
476
|
at :255/:285), so this rendered 0px -- the heading and the Filters
|
|
506
477
|
button touched -- while both siblings render 8px. */}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
variant="ghost"
|
|
514
|
-
icon={<Icon icon={Filter} />}
|
|
515
|
-
isIconOnly
|
|
516
|
-
/>
|
|
517
|
-
</HStack>
|
|
478
|
+
<HStack gap={2} vAlign="center">
|
|
479
|
+
<StackItem size="fill">
|
|
480
|
+
<Heading level={2}>Timeline</Heading>
|
|
481
|
+
</StackItem>
|
|
482
|
+
<Button label="Filters" variant="ghost" icon={<Icon icon={Filter} />} isIconOnly />
|
|
483
|
+
</HStack>
|
|
518
484
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
/>
|
|
569
|
-
))}
|
|
570
|
-
</List>
|
|
571
|
-
</VStack>
|
|
572
|
-
</Section>
|
|
573
|
-
);
|
|
485
|
+
<List density="spacious" edge="outset">
|
|
486
|
+
{ACTIVITY.map((item) => (
|
|
487
|
+
<ListItem
|
|
488
|
+
key={`${item.user}-${item.time}`}
|
|
489
|
+
label={item.user}
|
|
490
|
+
description={
|
|
491
|
+
<VStack gap={2}>
|
|
492
|
+
<Text type="body">{item.text}</Text>
|
|
493
|
+
{item.changes && (
|
|
494
|
+
<VStack gap={1}>
|
|
495
|
+
{item.changes.map((change) => (
|
|
496
|
+
<HStack key={change} gap={2} vAlign="center">
|
|
497
|
+
<Icon icon={SquarePen} size="sm" color="secondary" />
|
|
498
|
+
<Text type="supporting" color="secondary">
|
|
499
|
+
{change}
|
|
500
|
+
</Text>
|
|
501
|
+
</HStack>
|
|
502
|
+
))}
|
|
503
|
+
</VStack>
|
|
504
|
+
)}
|
|
505
|
+
<HStack gap={3} vAlign="center">
|
|
506
|
+
<HStack gap={1} vAlign="center">
|
|
507
|
+
<Icon icon={ThumbsUp} size="xsm" color="secondary" />
|
|
508
|
+
<Icon icon={Heart} size="xsm" color="secondary" />
|
|
509
|
+
<Text type="supporting" color="secondary" hasTabularNumbers>
|
|
510
|
+
{item.reactions}
|
|
511
|
+
</Text>
|
|
512
|
+
</HStack>
|
|
513
|
+
<Text type="supporting" color="secondary">
|
|
514
|
+
Like
|
|
515
|
+
</Text>
|
|
516
|
+
<Bullet />
|
|
517
|
+
<Text type="supporting" color="secondary">
|
|
518
|
+
Reply
|
|
519
|
+
</Text>
|
|
520
|
+
<Bullet />
|
|
521
|
+
<Text type="supporting" color="secondary" hasTabularNumbers>
|
|
522
|
+
{item.time}
|
|
523
|
+
</Text>
|
|
524
|
+
</HStack>
|
|
525
|
+
</VStack>
|
|
526
|
+
}
|
|
527
|
+
startContent={<Avatar name={item.user} size="md" />}
|
|
528
|
+
/>
|
|
529
|
+
))}
|
|
530
|
+
</List>
|
|
531
|
+
</VStack>
|
|
532
|
+
</Section>
|
|
533
|
+
);
|
|
574
534
|
}
|
|
575
535
|
|
|
576
536
|
// ─── Right Panel ────────────────────────────────────────────────────────────
|
|
577
537
|
// Renders as a fixed-width side panel on desktop, and as a plain stacked section
|
|
578
538
|
// on mobile (so it flows below the content instead of squishing beside it).
|
|
579
539
|
function PanelContent() {
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
540
|
+
return (
|
|
541
|
+
<VStack gap={4}>
|
|
542
|
+
<Collapsible trigger={<Heading level={3}>Notes</Heading>}>
|
|
543
|
+
{/* "Show more" closes the sentence inside the Text below, so it is
|
|
584
544
|
prose and asks for `hasUnderline` — colour alone is not a
|
|
585
545
|
sufficient cue inline (1.4.1 / F73). `type="inherit"` adopts the
|
|
586
546
|
paragraph's size and leading. The two links in the header are
|
|
587
547
|
navigation, so they pass neither. */}
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
isLabelHidden
|
|
617
|
-
/>
|
|
618
|
-
<Text type="body">Recommendation: Fulfill order</Text>
|
|
619
|
-
<Text type="body">
|
|
620
|
-
There is a low chance that you will receive a chargeback on this
|
|
621
|
-
order.
|
|
622
|
-
</Text>
|
|
623
|
-
</VStack>
|
|
624
|
-
</Collapsible>
|
|
625
|
-
</VStack>
|
|
626
|
-
);
|
|
548
|
+
<Text type="body">
|
|
549
|
+
Customer is a repeat buyer, 3rd order this quarter. Prefers moonlight and sage glazes.
|
|
550
|
+
Requested gift wrapping for the mug set. Ships to a residential address in CA.{" "}
|
|
551
|
+
<Link href="#/templates/detail-page" hasUnderline type="inherit">
|
|
552
|
+
Show more
|
|
553
|
+
</Link>
|
|
554
|
+
</Text>
|
|
555
|
+
</Collapsible>
|
|
556
|
+
<Collapsible trigger={<Heading level={3}>Customer</Heading>}>
|
|
557
|
+
<MetadataList>
|
|
558
|
+
<MetadataListItem label="Name">Vlad Dracul</MetadataListItem>
|
|
559
|
+
<MetadataListItem label="Address">321 Smith Road, CA 38238</MetadataListItem>
|
|
560
|
+
<MetadataListItem label="Phone">234-555-0134</MetadataListItem>
|
|
561
|
+
<MetadataListItem label="Email">vlad@castle-dracula.transylvania</MetadataListItem>
|
|
562
|
+
<MetadataListItem label="Billing Address">Same as shipping address</MetadataListItem>
|
|
563
|
+
</MetadataList>
|
|
564
|
+
</Collapsible>
|
|
565
|
+
<Collapsible trigger={<Heading level={3}>Fraud analysis</Heading>}>
|
|
566
|
+
<VStack gap={1}>
|
|
567
|
+
<ProgressBar label="Risk level" value={15} variant="success" isLabelHidden />
|
|
568
|
+
<Text type="body">Recommendation: Fulfill order</Text>
|
|
569
|
+
<Text type="body">
|
|
570
|
+
There is a low chance that you will receive a chargeback on this order.
|
|
571
|
+
</Text>
|
|
572
|
+
</VStack>
|
|
573
|
+
</Collapsible>
|
|
574
|
+
</VStack>
|
|
575
|
+
);
|
|
627
576
|
}
|
|
628
577
|
|
|
629
578
|
// Desktop: fixed-width side panel in the layout's `end` slot.
|
|
630
579
|
function RightPanel() {
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
580
|
+
return (
|
|
581
|
+
<LayoutPanel width={320} padding={4} role="complementary">
|
|
582
|
+
<PanelContent />
|
|
583
|
+
</LayoutPanel>
|
|
584
|
+
);
|
|
636
585
|
}
|
|
637
586
|
|
|
638
587
|
// ─── Main Page ──────────────────────────────────────────────────────────────
|
|
639
588
|
export default function DetailPage() {
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
589
|
+
const [activeTab, setActiveTab] = useState("details");
|
|
590
|
+
const isNarrow = useMediaQuery("(max-width: 1024px)");
|
|
591
|
+
// Desktop: a fixed-width `end`-slot side panel that can be hidden.
|
|
592
|
+
const [showSidePanel, setShowSidePanel] = useState(true);
|
|
593
|
+
// Mobile: the panel opens as a full-screen dialog (the side slot would squish
|
|
594
|
+
// the main content), driven by the same toolbar button.
|
|
595
|
+
const [isPanelDialogOpen, setPanelDialogOpen] = useState(false);
|
|
647
596
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
? setPanelDialogOpen(prev => !prev)
|
|
652
|
-
: setShowSidePanel(prev => !prev);
|
|
597
|
+
const isPanelShown = isNarrow ? isPanelDialogOpen : showSidePanel;
|
|
598
|
+
const togglePanel = () =>
|
|
599
|
+
isNarrow ? setPanelDialogOpen((prev) => !prev) : setShowSidePanel((prev) => !prev);
|
|
653
600
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
/>
|
|
701
|
-
</Dialog>
|
|
702
|
-
</>
|
|
703
|
-
);
|
|
601
|
+
return (
|
|
602
|
+
<>
|
|
603
|
+
<Layout
|
|
604
|
+
height="fill"
|
|
605
|
+
contentWidth={1000}
|
|
606
|
+
defaultHasDividers
|
|
607
|
+
header={
|
|
608
|
+
<PageHeader
|
|
609
|
+
activeTab={activeTab}
|
|
610
|
+
onTabChange={setActiveTab}
|
|
611
|
+
isPanelOpen={isPanelShown}
|
|
612
|
+
onTogglePanel={togglePanel}
|
|
613
|
+
isNarrow={isNarrow}
|
|
614
|
+
/>
|
|
615
|
+
}
|
|
616
|
+
content={
|
|
617
|
+
<LayoutContent role="main">
|
|
618
|
+
<VStack gap={4}>
|
|
619
|
+
<ItemsCard />
|
|
620
|
+
<InvoiceCard />
|
|
621
|
+
<TimelineSection />
|
|
622
|
+
</VStack>
|
|
623
|
+
</LayoutContent>
|
|
624
|
+
}
|
|
625
|
+
end={!isNarrow && showSidePanel ? <RightPanel /> : undefined}
|
|
626
|
+
/>
|
|
627
|
+
{/* Mobile: the side-panel content uses the full viewport so order details
|
|
628
|
+
are not squeezed beside narrow page chrome. Drawer remains available
|
|
629
|
+
for side-overlay flows that intentionally preserve page context. */}
|
|
630
|
+
<Dialog
|
|
631
|
+
variant="fullscreen"
|
|
632
|
+
isOpen={isNarrow && isPanelDialogOpen}
|
|
633
|
+
onOpenChange={setPanelDialogOpen}
|
|
634
|
+
>
|
|
635
|
+
<Layout
|
|
636
|
+
height="auto"
|
|
637
|
+
header={<DialogHeader title="Order details" onOpenChange={setPanelDialogOpen} />}
|
|
638
|
+
content={
|
|
639
|
+
<LayoutContent padding={4}>
|
|
640
|
+
<PanelContent />
|
|
641
|
+
</LayoutContent>
|
|
642
|
+
}
|
|
643
|
+
/>
|
|
644
|
+
</Dialog>
|
|
645
|
+
</>
|
|
646
|
+
);
|
|
704
647
|
}
|