@spree/dashboard-ui 6.0.2 → 6.0.4
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/package.json +20 -19
- package/src/index.ts +5 -0
- package/src/lib/field-trigger.ts +10 -0
- package/src/lib/same-rich-text.ts +12 -3
- package/src/lib/utils.ts +14 -1
- package/src/spree/address-block.tsx +3 -3
- package/src/spree/address-book-row.tsx +88 -0
- package/src/spree/bulk-price-table.tsx +3 -3
- package/src/spree/claim-resolve-dialog.tsx +1 -1
- package/src/spree/collapsible-card.tsx +2 -2
- package/src/spree/color-picker.tsx +1 -1
- package/src/spree/currency-label.tsx +71 -0
- package/src/spree/data-grid/cells.tsx +2 -2
- package/src/spree/data-grid/data-grid.tsx +2 -2
- package/src/spree/delivery-form-dialog.tsx +1 -1
- package/src/spree/drag-handle.tsx +1 -1
- package/src/spree/editable-note-card.tsx +2 -2
- package/src/spree/form-actions.tsx +32 -10
- package/src/spree/fulfill-items-form.tsx +20 -22
- package/src/spree/fulfillment-edit-dialog.tsx +1 -1
- package/src/spree/fulfillment-item-list.tsx +87 -28
- package/src/spree/fulfillment-panel.tsx +2 -2
- package/src/spree/icons.ts +8 -0
- package/src/spree/json-preview-drawer.tsx +32 -7
- package/src/spree/json-value-view.tsx +14 -4
- package/src/spree/label-upload-dialog.tsx +1 -1
- package/src/spree/language-menu-items.tsx +1 -1
- package/src/spree/metadata/metadata-card.tsx +2 -2
- package/src/spree/order-cancel-dialog.tsx +3 -3
- package/src/spree/post-sale-create-dialogs.tsx +36 -33
- package/src/spree/post-sale-fields.tsx +30 -1
- package/src/spree/product-identity.tsx +74 -0
- package/src/spree/product-membership-list.tsx +79 -41
- package/src/spree/products/status-card.tsx +1 -1
- package/src/spree/quantity-tier-editor.tsx +1 -1
- package/src/spree/return-dialogs.tsx +2 -2
- package/src/spree/search-input.tsx +3 -1
- package/src/spree/secret-input.tsx +1 -1
- package/src/spree/section.tsx +55 -0
- package/src/spree/stat-item.tsx +27 -0
- package/src/spree/tax-identifiers-card.tsx +117 -76
- package/src/spree/theme-toggle.tsx +1 -1
- package/src/spree/wizard.tsx +2 -2
- package/src/styles.css +226 -120
- package/src/ui/alert.tsx +20 -17
- package/src/ui/attachment.tsx +1 -1
- package/src/ui/avatar.tsx +2 -2
- package/src/ui/badge.tsx +1 -1
- package/src/ui/breadcrumb.tsx +1 -1
- package/src/ui/button.tsx +14 -11
- package/src/ui/calendar.tsx +2 -2
- package/src/ui/card.tsx +14 -37
- package/src/ui/chart.tsx +1 -1
- package/src/ui/checkbox.tsx +2 -2
- package/src/ui/code-editor.tsx +6 -2
- package/src/ui/collapsible.tsx +21 -2
- package/src/ui/combobox.tsx +12 -16
- package/src/ui/command.tsx +8 -6
- package/src/ui/context-menu.tsx +1 -1
- package/src/ui/data-table.tsx +58 -15
- package/src/ui/date-picker.tsx +3 -3
- package/src/ui/date-range-panel.tsx +102 -0
- package/src/ui/date-range-picker.tsx +33 -72
- package/src/ui/dialog.tsx +3 -3
- package/src/ui/dropdown-menu.tsx +6 -6
- package/src/ui/empty.tsx +3 -3
- package/src/ui/field.tsx +33 -12
- package/src/ui/input-group.tsx +17 -6
- package/src/ui/input.tsx +148 -4
- package/src/ui/item.tsx +7 -4
- package/src/ui/label.tsx +1 -1
- package/src/ui/media-preview.tsx +40 -5
- package/src/ui/pagination.tsx +16 -4
- package/src/ui/progress.tsx +2 -2
- package/src/ui/radio-group.tsx +2 -2
- package/src/ui/rich-text-editor.tsx +3 -3
- package/src/ui/select.tsx +7 -16
- package/src/ui/sheet.tsx +13 -5
- package/src/ui/sidebar.tsx +5 -5
- package/src/ui/switch.tsx +8 -2
- package/src/ui/tabs.tsx +45 -9
- package/src/ui/textarea.tsx +1 -1
- package/src/ui/toast.tsx +2 -2
- package/src/ui/tooltip.tsx +1 -1
|
@@ -8,8 +8,8 @@ import { Checkbox } from '../ui/checkbox'
|
|
|
8
8
|
import { Field, FieldLabel } from '../ui/field'
|
|
9
9
|
import { Input } from '../ui/input'
|
|
10
10
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select'
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
11
|
+
import { TriangleAlertIcon } from './icons'
|
|
12
|
+
import { ProductIdentity } from './product-identity'
|
|
13
13
|
|
|
14
14
|
/** A pickable row: what it looks like and how many units are on offer. */
|
|
15
15
|
export type FulfillableRowData = {
|
|
@@ -48,10 +48,7 @@ function ItemQuantityRow({
|
|
|
48
48
|
|
|
49
49
|
return (
|
|
50
50
|
<div
|
|
51
|
-
className={cn(
|
|
52
|
-
'my-2 flex items-center gap-3 rounded-lg py-2 pl-2',
|
|
53
|
-
selected && 'bg-accent-strong',
|
|
54
|
-
)}
|
|
51
|
+
className={cn('my-2 flex items-center gap-3 rounded-lg p-2', selected && 'bg-accent-strong')}
|
|
55
52
|
>
|
|
56
53
|
<Controller
|
|
57
54
|
control={form.control}
|
|
@@ -74,14 +71,12 @@ function ItemQuantityRow({
|
|
|
74
71
|
)}
|
|
75
72
|
/>
|
|
76
73
|
|
|
77
|
-
<
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
)}
|
|
84
|
-
</div>
|
|
74
|
+
<ProductIdentity
|
|
75
|
+
name={row.name}
|
|
76
|
+
subtitle={row.optionsText}
|
|
77
|
+
thumbnailUrl={row.thumbnailUrl}
|
|
78
|
+
className="flex-1"
|
|
79
|
+
/>
|
|
85
80
|
|
|
86
81
|
<Controller
|
|
87
82
|
control={form.control}
|
|
@@ -108,7 +103,7 @@ function ItemQuantityRow({
|
|
|
108
103
|
}}
|
|
109
104
|
onBlur={field.onBlur}
|
|
110
105
|
/>
|
|
111
|
-
<span className="w-16 whitespace-nowrap text-muted-foreground text-
|
|
106
|
+
<span className="w-16 whitespace-nowrap text-muted-foreground text-base">
|
|
112
107
|
{t('admin.orders.fulfill.of_units', { count: row.quantity })}
|
|
113
108
|
</span>
|
|
114
109
|
</div>
|
|
@@ -172,7 +167,7 @@ export function FulfillItemsForm({
|
|
|
172
167
|
return (
|
|
173
168
|
<form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col">
|
|
174
169
|
{errors.root?.message && (
|
|
175
|
-
<p className="text-destructive text-
|
|
170
|
+
<p className="px-4 pt-3 text-destructive text-base" role="alert">
|
|
176
171
|
{errors.root.message}
|
|
177
172
|
</p>
|
|
178
173
|
)}
|
|
@@ -180,7 +175,10 @@ export function FulfillItemsForm({
|
|
|
180
175
|
{rows.length === 0 ? (
|
|
181
176
|
<p className="py-6 text-center text-muted-foreground">{t('admin.orders.fulfill.empty')}</p>
|
|
182
177
|
) : (
|
|
183
|
-
|
|
178
|
+
// Rows are inset 8px so a selected row's fill sits inside the card,
|
|
179
|
+
// and their own 8px padding lines the content up with the 16px card
|
|
180
|
+
// content below.
|
|
181
|
+
<div className="divide-y px-2">
|
|
184
182
|
{rows.map((row, index) => (
|
|
185
183
|
<ItemQuantityRow
|
|
186
184
|
key={row.key}
|
|
@@ -194,7 +192,7 @@ export function FulfillItemsForm({
|
|
|
194
192
|
</div>
|
|
195
193
|
)}
|
|
196
194
|
|
|
197
|
-
<div className="grid grid-cols-1 gap-4 border-border-subtle border-t py-3 sm:grid-cols-2">
|
|
195
|
+
<div className="mx-4 grid grid-cols-1 gap-4 border-border-subtle border-t py-3 sm:grid-cols-2">
|
|
198
196
|
<Field>
|
|
199
197
|
<FieldLabel htmlFor={`${idPrefix}-tracking`}>
|
|
200
198
|
{t('admin.orders.fulfill.tracking_label')}
|
|
@@ -241,7 +239,7 @@ export function FulfillItemsForm({
|
|
|
241
239
|
render={({ field }) => (
|
|
242
240
|
<label
|
|
243
241
|
htmlFor={`${idPrefix}-notify`}
|
|
244
|
-
className="flex cursor-pointer items-center gap-2 py-3 text-
|
|
242
|
+
className="flex cursor-pointer items-center gap-2 px-4 py-3 text-base"
|
|
245
243
|
>
|
|
246
244
|
<Checkbox
|
|
247
245
|
id={`${idPrefix}-notify`}
|
|
@@ -254,7 +252,7 @@ export function FulfillItemsForm({
|
|
|
254
252
|
/>
|
|
255
253
|
|
|
256
254
|
{!shipsEverything && totalSelected > 0 && (
|
|
257
|
-
<div className="px-
|
|
255
|
+
<div className="px-4 pb-3">
|
|
258
256
|
<Alert variant="warning">
|
|
259
257
|
<TriangleAlertIcon />
|
|
260
258
|
<AlertDescription>{t('admin.orders.fulfill.partial_hint')}</AlertDescription>
|
|
@@ -262,8 +260,8 @@ export function FulfillItemsForm({
|
|
|
262
260
|
</div>
|
|
263
261
|
)}
|
|
264
262
|
|
|
265
|
-
<div className="flex items-center justify-end gap-2 border-border-subtle border-t py-3">
|
|
266
|
-
<span className="mr-auto text-muted-foreground text-
|
|
263
|
+
<div className="mx-4 flex items-center justify-end gap-2 border-border-subtle border-t py-3">
|
|
264
|
+
<span className="mr-auto text-muted-foreground text-base">
|
|
267
265
|
{t('admin.orders.fulfill.units_selected')}: {totalSelected} / {totalAvailable}
|
|
268
266
|
</span>
|
|
269
267
|
<Button type="button" size="sm" variant="outline" onClick={onCancel}>
|
|
@@ -125,7 +125,7 @@ export function FulfillmentEditDialog({
|
|
|
125
125
|
|
|
126
126
|
<DialogBody className="flex flex-col gap-4">
|
|
127
127
|
{errorMessage && (
|
|
128
|
-
<p className="text-
|
|
128
|
+
<p className="text-base text-destructive" role="alert">
|
|
129
129
|
{errorMessage}
|
|
130
130
|
</p>
|
|
131
131
|
)}
|
|
@@ -1,7 +1,18 @@
|
|
|
1
|
-
import type { ReactNode } from 'react'
|
|
2
|
-
import { Item, ItemActions
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import type { ReactElement, ReactNode } from 'react'
|
|
2
|
+
import { Item, ItemActions } from '../ui/item'
|
|
3
|
+
import { CornerDownRightIcon } from './icons'
|
|
4
|
+
import { ProductIdentity } from './product-identity'
|
|
5
|
+
|
|
6
|
+
/** What an exchanged unit was swapped for. */
|
|
7
|
+
export type FulfillmentItemReplacement = {
|
|
8
|
+
name: string
|
|
9
|
+
optionsText?: string | null
|
|
10
|
+
thumbnailUrl?: string | null
|
|
11
|
+
productId?: string | null
|
|
12
|
+
displayPrice?: string | null
|
|
13
|
+
/** Read out before the replacement, since the arrow says it only by sight. */
|
|
14
|
+
label?: string
|
|
15
|
+
}
|
|
5
16
|
|
|
6
17
|
/**
|
|
7
18
|
* One row of a fulfillment's item list, already joined to its line item by
|
|
@@ -18,50 +29,98 @@ export type FulfillmentItemRowData = {
|
|
|
18
29
|
thumbnailUrl?: string | null
|
|
19
30
|
displayPrice?: string | null
|
|
20
31
|
priceSource?: ReactNode
|
|
32
|
+
/** Beside the name, for state the row's own surface tracks (a return's receipt). */
|
|
33
|
+
badges?: ReactNode
|
|
34
|
+
productId?: string | null
|
|
35
|
+
/** An exchange's new item, shown under the one going back. */
|
|
36
|
+
replacement?: FulfillmentItemReplacement
|
|
21
37
|
quantity: number
|
|
22
38
|
}
|
|
23
39
|
|
|
40
|
+
/** Builds the link a product opens; routing belongs to the app. */
|
|
41
|
+
export type ProductLinkRender = (productId: string) => ReactElement
|
|
42
|
+
|
|
24
43
|
/**
|
|
25
44
|
* What a fulfillment holds: what it looks like, what it costs and how many
|
|
26
45
|
* units travel together.
|
|
27
46
|
*
|
|
28
47
|
* Read-only — changing what an order contains after placement belongs to the
|
|
29
48
|
* order edit screen, not here. Shared by the operator's order page and the
|
|
30
|
-
* seller's so a parcel's contents read the same on both
|
|
49
|
+
* seller's so a parcel's contents read the same on both, and by the return,
|
|
50
|
+
* exchange and claim cards so a unit looks the same wherever it goes.
|
|
31
51
|
*/
|
|
32
|
-
function ItemRow({
|
|
52
|
+
function ItemRow({
|
|
53
|
+
row,
|
|
54
|
+
productLink,
|
|
55
|
+
}: {
|
|
56
|
+
row: FulfillmentItemRowData
|
|
57
|
+
productLink?: ProductLinkRender
|
|
58
|
+
}) {
|
|
59
|
+
const linkFor = (productId?: string | null) =>
|
|
60
|
+
productId && productLink ? productLink(productId) : undefined
|
|
61
|
+
const replacement = row.replacement
|
|
62
|
+
|
|
33
63
|
return (
|
|
34
|
-
<
|
|
35
|
-
<
|
|
64
|
+
<div className="py-3">
|
|
65
|
+
<Item className="flex-nowrap gap-3 rounded-none border-0 p-0">
|
|
66
|
+
<ProductIdentity
|
|
67
|
+
name={row.name}
|
|
68
|
+
subtitle={row.optionsText}
|
|
69
|
+
thumbnailUrl={row.thumbnailUrl}
|
|
70
|
+
badges={row.badges}
|
|
71
|
+
render={linkFor(row.productId)}
|
|
72
|
+
className="flex-1"
|
|
73
|
+
/>
|
|
36
74
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
75
|
+
<ItemActions className="min-w-0 shrink flex-col items-end gap-0 text-right text-muted-foreground text-base">
|
|
76
|
+
{row.displayPrice ? (
|
|
77
|
+
<span className="whitespace-nowrap">
|
|
78
|
+
{row.displayPrice} × {row.quantity}
|
|
79
|
+
</span>
|
|
80
|
+
) : (
|
|
81
|
+
<span className="whitespace-nowrap">× {row.quantity}</span>
|
|
82
|
+
)}
|
|
83
|
+
{row.priceSource && <div className="max-w-full truncate text-xs">{row.priceSource}</div>}
|
|
84
|
+
</ItemActions>
|
|
85
|
+
</Item>
|
|
43
86
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
87
|
+
{replacement && (
|
|
88
|
+
<Item className="mt-4 flex-nowrap gap-3 rounded-none border-0 p-0 pl-3">
|
|
89
|
+
<CornerDownRightIcon aria-hidden className="size-4 shrink-0 text-muted-foreground" />
|
|
90
|
+
{replacement.label && <span className="sr-only">{replacement.label}</span>}
|
|
91
|
+
<ProductIdentity
|
|
92
|
+
name={replacement.name}
|
|
93
|
+
subtitle={replacement.optionsText}
|
|
94
|
+
thumbnailUrl={replacement.thumbnailUrl}
|
|
95
|
+
render={linkFor(replacement.productId)}
|
|
96
|
+
className="flex-1"
|
|
97
|
+
/>
|
|
98
|
+
{replacement.displayPrice && (
|
|
99
|
+
<ItemActions className="whitespace-nowrap text-muted-foreground text-base">
|
|
100
|
+
{replacement.displayPrice} × {row.quantity}
|
|
101
|
+
</ItemActions>
|
|
102
|
+
)}
|
|
103
|
+
</Item>
|
|
104
|
+
)}
|
|
105
|
+
</div>
|
|
55
106
|
)
|
|
56
107
|
}
|
|
57
108
|
|
|
58
|
-
export function FulfillmentItemList({
|
|
109
|
+
export function FulfillmentItemList({
|
|
110
|
+
rows,
|
|
111
|
+
productLink,
|
|
112
|
+
}: {
|
|
113
|
+
rows: FulfillmentItemRowData[]
|
|
114
|
+
/** Makes each product a link to its page. */
|
|
115
|
+
productLink?: ProductLinkRender
|
|
116
|
+
}) {
|
|
59
117
|
if (rows.length === 0) return null
|
|
60
118
|
|
|
61
119
|
return (
|
|
62
|
-
|
|
120
|
+
// Inset by margin so the rules between rows stop short of the card's edges.
|
|
121
|
+
<div className="mx-4 divide-y">
|
|
63
122
|
{rows.map((row) => (
|
|
64
|
-
<ItemRow key={row.key} row={row} />
|
|
123
|
+
<ItemRow key={row.key} row={row} productLink={productLink} />
|
|
65
124
|
))}
|
|
66
125
|
</div>
|
|
67
126
|
)
|
|
@@ -31,9 +31,9 @@ export function FulfillmentPanel({
|
|
|
31
31
|
children: ReactNode
|
|
32
32
|
}) {
|
|
33
33
|
return (
|
|
34
|
-
<Card
|
|
34
|
+
<Card>
|
|
35
35
|
<CardHeader>
|
|
36
|
-
<CardTitle className="min-w-0 font-normal text-
|
|
36
|
+
<CardTitle className="min-w-0 font-normal text-base">
|
|
37
37
|
<StatusBadge status={status} label={statusLabel} />
|
|
38
38
|
{location && (
|
|
39
39
|
<div className="flex min-w-0 items-center gap-1.5 text-muted-foreground text-xs">
|
package/src/spree/icons.ts
CHANGED
|
@@ -24,6 +24,7 @@ export {
|
|
|
24
24
|
BotIcon,
|
|
25
25
|
BoxesIcon,
|
|
26
26
|
BracesIcon,
|
|
27
|
+
BugIcon,
|
|
27
28
|
Building2Icon,
|
|
28
29
|
CalculatorIcon,
|
|
29
30
|
CalendarIcon,
|
|
@@ -38,6 +39,7 @@ export {
|
|
|
38
39
|
ChevronsLeftIcon,
|
|
39
40
|
ChevronsRightIcon,
|
|
40
41
|
ChevronsUpDownIcon,
|
|
42
|
+
ChevronUpIcon,
|
|
41
43
|
CircleAlertIcon,
|
|
42
44
|
CircleCheckIcon,
|
|
43
45
|
CircleDotIcon,
|
|
@@ -48,6 +50,7 @@ export {
|
|
|
48
50
|
CommandIcon,
|
|
49
51
|
CopyIcon,
|
|
50
52
|
CornerDownLeftIcon,
|
|
53
|
+
CornerDownRightIcon,
|
|
51
54
|
CreditCardIcon,
|
|
52
55
|
DownloadIcon,
|
|
53
56
|
EllipsisVerticalIcon,
|
|
@@ -73,6 +76,7 @@ export {
|
|
|
73
76
|
GlobeLockIcon,
|
|
74
77
|
GripVerticalIcon,
|
|
75
78
|
HandCoinsIcon,
|
|
79
|
+
HashIcon,
|
|
76
80
|
HistoryIcon,
|
|
77
81
|
HomeIcon,
|
|
78
82
|
ImageIcon,
|
|
@@ -83,6 +87,7 @@ export {
|
|
|
83
87
|
KeyRoundIcon,
|
|
84
88
|
LanguagesIcon,
|
|
85
89
|
LayersIcon,
|
|
90
|
+
LayoutGridIcon,
|
|
86
91
|
LibraryIcon,
|
|
87
92
|
LinkIcon,
|
|
88
93
|
ListChecksIcon,
|
|
@@ -100,6 +105,7 @@ export {
|
|
|
100
105
|
Maximize,
|
|
101
106
|
MenuIcon,
|
|
102
107
|
MessageCircleIcon,
|
|
108
|
+
MessageSquareIcon,
|
|
103
109
|
Minus,
|
|
104
110
|
MinusIcon,
|
|
105
111
|
MonitorIcon,
|
|
@@ -111,6 +117,7 @@ export {
|
|
|
111
117
|
PackageCheckIcon,
|
|
112
118
|
PackageIcon,
|
|
113
119
|
PackageXIcon,
|
|
120
|
+
PaletteIcon,
|
|
114
121
|
PanelLeftIcon,
|
|
115
122
|
PaperclipIcon,
|
|
116
123
|
PauseIcon,
|
|
@@ -166,6 +173,7 @@ export {
|
|
|
166
173
|
UploadIcon,
|
|
167
174
|
UserIcon,
|
|
168
175
|
UserMinusIcon,
|
|
176
|
+
UserPenIcon,
|
|
169
177
|
UserPlusIcon,
|
|
170
178
|
UsersIcon,
|
|
171
179
|
UsersRoundIcon,
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
ExternalLinkIcon,
|
|
20
20
|
Loader2Icon,
|
|
21
21
|
RotateCcwIcon,
|
|
22
|
+
TerminalIcon,
|
|
22
23
|
XIcon,
|
|
23
24
|
} from './icons'
|
|
24
25
|
import { type JsonValueLinkResolver, JsonValueView } from './json-value-view'
|
|
@@ -51,6 +52,12 @@ export interface JsonPreviewDrawerProps {
|
|
|
51
52
|
* convention.
|
|
52
53
|
*/
|
|
53
54
|
resolveLink?: JsonValueLinkResolver
|
|
55
|
+
/**
|
|
56
|
+
* A ready-made shell command that repeats the request. When given, the
|
|
57
|
+
* toolbar offers "Copy as curl". Build it with a placeholder in place of
|
|
58
|
+
* any credential — the drawer copies it verbatim.
|
|
59
|
+
*/
|
|
60
|
+
curl?: string
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
export function JsonPreviewDrawer({
|
|
@@ -60,6 +67,7 @@ export function JsonPreviewDrawer({
|
|
|
60
67
|
fetch,
|
|
61
68
|
endpoint,
|
|
62
69
|
resolveLink,
|
|
70
|
+
curl,
|
|
63
71
|
}: JsonPreviewDrawerProps) {
|
|
64
72
|
const { t } = useTranslation()
|
|
65
73
|
const resolvedTitle = title ?? t('admin.components.json_preview_drawer.title')
|
|
@@ -111,7 +119,8 @@ export function JsonPreviewDrawer({
|
|
|
111
119
|
}
|
|
112
120
|
|
|
113
121
|
const [collapsed, setCollapsed] = useState<boolean | number>(2)
|
|
114
|
-
const { copied:
|
|
122
|
+
const { copied: copiedJson, copy: copyJson } = useCopyToClipboard()
|
|
123
|
+
const { copied: copiedCurl, copy: copyCurl } = useCopyToClipboard()
|
|
115
124
|
|
|
116
125
|
const formatted = useMemo(() => (data ? JSON.stringify(data, null, 2) : ''), [data])
|
|
117
126
|
|
|
@@ -122,7 +131,7 @@ export function JsonPreviewDrawer({
|
|
|
122
131
|
showCloseButton={false}
|
|
123
132
|
className="w-full sm:max-w-2xl bg-zinc-950 text-zinc-100 border-zinc-800 overflow-hidden"
|
|
124
133
|
>
|
|
125
|
-
<SheetHeader className="border-b border-zinc-800 bg-zinc-950 text-zinc-100 rounded-t-
|
|
134
|
+
<SheetHeader className="border-b border-zinc-800 bg-zinc-950 text-zinc-100 rounded-t-2xl">
|
|
126
135
|
<SheetTitle className="text-zinc-100 font-mono text-base">{resolvedTitle}</SheetTitle>
|
|
127
136
|
{endpoint && (
|
|
128
137
|
<SheetDescription className="text-zinc-400 font-mono text-xs">
|
|
@@ -159,12 +168,28 @@ export function JsonPreviewDrawer({
|
|
|
159
168
|
<DepthControl value={collapsed} onChange={setCollapsed} />
|
|
160
169
|
|
|
161
170
|
<span className="ml-auto inline-flex items-center gap-1">
|
|
162
|
-
<ToolbarButton
|
|
163
|
-
|
|
164
|
-
{
|
|
171
|
+
<ToolbarButton
|
|
172
|
+
size="xs"
|
|
173
|
+
onClick={() => formatted && copyJson(formatted)}
|
|
174
|
+
disabled={!data}
|
|
175
|
+
>
|
|
176
|
+
{copiedJson ? <CheckIcon className="size-3.5" /> : <CopyIcon className="size-3.5" />}
|
|
177
|
+
{copiedJson
|
|
165
178
|
? t('admin.components.json_preview_drawer.copied')
|
|
166
|
-
: t('admin.components.json_preview_drawer.
|
|
179
|
+
: t('admin.components.json_preview_drawer.copy_json')}
|
|
167
180
|
</ToolbarButton>
|
|
181
|
+
{curl && (
|
|
182
|
+
<ToolbarButton size="xs" onClick={() => copyCurl(curl)}>
|
|
183
|
+
{copiedCurl ? (
|
|
184
|
+
<CheckIcon className="size-3.5" />
|
|
185
|
+
) : (
|
|
186
|
+
<TerminalIcon className="size-3.5" />
|
|
187
|
+
)}
|
|
188
|
+
{copiedCurl
|
|
189
|
+
? t('admin.components.json_preview_drawer.copied')
|
|
190
|
+
: t('admin.components.json_preview_drawer.copy_curl')}
|
|
191
|
+
</ToolbarButton>
|
|
192
|
+
)}
|
|
168
193
|
<ToolbarButton
|
|
169
194
|
size="icon-xs"
|
|
170
195
|
onClick={() => refetch()}
|
|
@@ -193,7 +218,7 @@ export function JsonPreviewDrawer({
|
|
|
193
218
|
</span>
|
|
194
219
|
</div>
|
|
195
220
|
|
|
196
|
-
<div className="themed-scrollbar flex-1 overflow-auto p-4 font-mono text-
|
|
221
|
+
<div className="themed-scrollbar flex-1 overflow-auto p-4 font-mono text-base">
|
|
197
222
|
{isLoading ? (
|
|
198
223
|
<p className="text-zinc-500">{t('admin.common.loading')}</p>
|
|
199
224
|
) : error ? (
|
|
@@ -71,7 +71,7 @@ export function JsonValueView({
|
|
|
71
71
|
return (
|
|
72
72
|
<div
|
|
73
73
|
className={cn(
|
|
74
|
-
'font-mono text-
|
|
74
|
+
'font-mono text-base',
|
|
75
75
|
// The vscode theme is dark — when the parent doesn't already paint a
|
|
76
76
|
// dark background, paint one here so the syntax colors stay legible.
|
|
77
77
|
!bare && 'bg-zinc-950 text-zinc-100 rounded-md p-3',
|
|
@@ -87,9 +87,19 @@ export function JsonValueView({
|
|
|
87
87
|
enableClipboard
|
|
88
88
|
shortenTextAfterLength={60}
|
|
89
89
|
components={{
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
// The viewer's own props, which React would warn about on a <span>.
|
|
91
|
+
value: ({
|
|
92
|
+
value: v,
|
|
93
|
+
type,
|
|
94
|
+
parentValue: _parentValue,
|
|
95
|
+
data: _data,
|
|
96
|
+
visible: _visible,
|
|
97
|
+
quotes: _quotes,
|
|
98
|
+
namespace: _namespace,
|
|
99
|
+
setValue: _setValue,
|
|
100
|
+
keyName: _keyName,
|
|
101
|
+
...rest
|
|
102
|
+
}) => <ValueRenderer value={v} type={type} resolveLink={resolveLink} {...rest} />,
|
|
93
103
|
}}
|
|
94
104
|
/>
|
|
95
105
|
</div>
|
|
@@ -90,7 +90,7 @@ export function LabelUploadDialog({
|
|
|
90
90
|
|
|
91
91
|
<DialogBody className="flex flex-col gap-4">
|
|
92
92
|
{errors.root?.message && (
|
|
93
|
-
<p className="text-destructive text-
|
|
93
|
+
<p className="text-destructive text-base" role="alert">
|
|
94
94
|
{errors.root.message}
|
|
95
95
|
</p>
|
|
96
96
|
)}
|
|
@@ -34,7 +34,7 @@ export function LanguageMenuItems({ label, locales, value, onSelect }: LanguageM
|
|
|
34
34
|
// Not a DropdownMenuItem: the row is a container for the select, and menu
|
|
35
35
|
// items steal the pointer and close the menu on click.
|
|
36
36
|
<div className="flex items-center justify-between gap-2 rounded-lg px-2 py-1.5">
|
|
37
|
-
<span className="text-
|
|
37
|
+
<span className="text-base text-foreground">{label}</span>
|
|
38
38
|
{/* The select is transparent and stretched over the pill, so the hit area
|
|
39
39
|
tracks the pill's width whatever the locale name is. `focus-within`
|
|
40
40
|
carries the focus ring, since `opacity-0` hides the native one. */}
|
|
@@ -72,7 +72,7 @@ export function MetadataList({
|
|
|
72
72
|
{externalReferencesTitle && (
|
|
73
73
|
<h4 className="text-xs font-medium text-muted-foreground">{externalReferencesTitle}</h4>
|
|
74
74
|
)}
|
|
75
|
-
<dl className="grid grid-cols-[minmax(160px,1fr)_2fr] items-center gap-x-4 gap-y-1 text-
|
|
75
|
+
<dl className="grid grid-cols-[minmax(160px,1fr)_2fr] items-center gap-x-4 gap-y-1 text-base">
|
|
76
76
|
{Object.entries(externalReferences).map(([system, key]) => (
|
|
77
77
|
<Fragment key={system}>
|
|
78
78
|
<dt>
|
|
@@ -94,7 +94,7 @@ export function MetadataList({
|
|
|
94
94
|
{both && metadataTitle && (
|
|
95
95
|
<h4 className="text-xs font-medium text-muted-foreground">{metadataTitle}</h4>
|
|
96
96
|
)}
|
|
97
|
-
<dl className="grid grid-cols-[minmax(160px,1fr)_2fr] gap-x-4 gap-y-2 text-
|
|
97
|
+
<dl className="grid grid-cols-[minmax(160px,1fr)_2fr] gap-x-4 gap-y-2 text-base">
|
|
98
98
|
{Object.entries(metadata).map(([key, value]) => (
|
|
99
99
|
<Fragment key={key}>
|
|
100
100
|
<dt>
|
|
@@ -67,7 +67,7 @@ export function OrderCancelDialog({
|
|
|
67
67
|
|
|
68
68
|
<DialogBody>
|
|
69
69
|
{errors.root?.message && (
|
|
70
|
-
<p className="text-destructive text-
|
|
70
|
+
<p className="text-destructive text-base" role="alert">
|
|
71
71
|
{errors.root.message}
|
|
72
72
|
</p>
|
|
73
73
|
)}
|
|
@@ -94,7 +94,7 @@ export function OrderCancelDialog({
|
|
|
94
94
|
render={({ field }) => (
|
|
95
95
|
<label
|
|
96
96
|
htmlFor="order-cancel-refund"
|
|
97
|
-
className="flex cursor-pointer items-start gap-2 text-
|
|
97
|
+
className="flex cursor-pointer items-start gap-2 text-base"
|
|
98
98
|
>
|
|
99
99
|
<Checkbox
|
|
100
100
|
id="order-cancel-refund"
|
|
@@ -119,7 +119,7 @@ export function OrderCancelDialog({
|
|
|
119
119
|
render={({ field }) => (
|
|
120
120
|
<label
|
|
121
121
|
htmlFor="order-cancel-notify"
|
|
122
|
-
className="flex cursor-pointer items-center gap-2 text-
|
|
122
|
+
className="flex cursor-pointer items-center gap-2 text-base"
|
|
123
123
|
>
|
|
124
124
|
<Checkbox
|
|
125
125
|
id="order-cancel-notify"
|
|
@@ -12,10 +12,12 @@ import {
|
|
|
12
12
|
import { Field, FieldLabel } from '../ui/field'
|
|
13
13
|
import { Input } from '../ui/input'
|
|
14
14
|
import { InputGroup, InputGroupAddon, InputGroupInput, InputGroupText } from '../ui/input-group'
|
|
15
|
+
import { Item, ItemActions, ItemContent, ItemFooter } from '../ui/item'
|
|
15
16
|
import { Textarea } from '../ui/textarea'
|
|
16
17
|
import {
|
|
17
18
|
type PostSaleSelection,
|
|
18
19
|
type PostSaleUnit,
|
|
20
|
+
PostSaleUnitLabel,
|
|
19
21
|
QuantityPicker,
|
|
20
22
|
selectedUnits,
|
|
21
23
|
} from './post-sale-fields'
|
|
@@ -158,12 +160,11 @@ export function CreateClaimDialog({
|
|
|
158
160
|
const chosenQuantity = selection[line.id] ?? 0
|
|
159
161
|
|
|
160
162
|
return (
|
|
161
|
-
<
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
<
|
|
166
|
-
<span className="min-w-0 truncate text-sm">{line.label}</span>
|
|
163
|
+
<Item variant="outline" key={line.id}>
|
|
164
|
+
<ItemContent className="min-w-0">
|
|
165
|
+
<PostSaleUnitLabel unit={line} />
|
|
166
|
+
</ItemContent>
|
|
167
|
+
<ItemActions>
|
|
167
168
|
<Input
|
|
168
169
|
type="number"
|
|
169
170
|
min={0}
|
|
@@ -187,35 +188,37 @@ export function CreateClaimDialog({
|
|
|
187
188
|
}
|
|
188
189
|
}}
|
|
189
190
|
/>
|
|
190
|
-
</
|
|
191
|
+
</ItemActions>
|
|
191
192
|
{chosenQuantity > 0 && (
|
|
192
|
-
<
|
|
193
|
-
<
|
|
194
|
-
{
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
<
|
|
198
|
-
<
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
193
|
+
<ItemFooter className="flex-col items-stretch">
|
|
194
|
+
<Field>
|
|
195
|
+
<FieldLabel htmlFor={`claim-amount-${line.id}`}>
|
|
196
|
+
{t('admin.pages.orders.detail.claims.refund_amount')}
|
|
197
|
+
</FieldLabel>
|
|
198
|
+
<InputGroup>
|
|
199
|
+
<InputGroupAddon>
|
|
200
|
+
<InputGroupText>{currencySymbol}</InputGroupText>
|
|
201
|
+
</InputGroupAddon>
|
|
202
|
+
<InputGroupInput
|
|
203
|
+
id={`claim-amount-${line.id}`}
|
|
204
|
+
type="number"
|
|
205
|
+
step="any"
|
|
206
|
+
min="0"
|
|
207
|
+
value={amounts[line.id] ?? ''}
|
|
208
|
+
onChange={(event) => {
|
|
209
|
+
setAmounts({ ...amounts, [line.id]: event.target.value })
|
|
210
|
+
// Clearing the field hands it back to the quantity.
|
|
211
|
+
setTypedAmounts({
|
|
212
|
+
...typedAmounts,
|
|
213
|
+
[line.id]: event.target.value !== '',
|
|
214
|
+
})
|
|
215
|
+
}}
|
|
216
|
+
/>
|
|
217
|
+
</InputGroup>
|
|
218
|
+
</Field>
|
|
219
|
+
</ItemFooter>
|
|
217
220
|
)}
|
|
218
|
-
</
|
|
221
|
+
</Item>
|
|
219
222
|
)
|
|
220
223
|
})}
|
|
221
224
|
</div>
|