@aglyn/plugins-commerce 1.0.0-beta.261 → 1.0.0-beta.263
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 +13 -13
- package/src/lib/components/console/order-detail-dialog.component.js +17 -45
- package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
- package/src/lib/components/console/order-fulfillment-panel.component.js +12 -25
- package/src/lib/components/console/order-fulfillment-panel.component.js.map +1 -1
- package/src/lib/components/console/order-invoice.component.js +2 -10
- package/src/lib/components/console/order-invoice.component.js.map +1 -1
- package/src/lib/components/console/order-shipping-actions.component.d.ts +1 -15
- package/src/lib/components/console/order-shipping-actions.component.js +3 -23
- package/src/lib/components/console/order-shipping-actions.component.js.map +1 -1
- package/src/lib/components/console/product-labels-dialog.component.d.ts +2 -5
- package/src/lib/components/console/product-labels-dialog.component.js +6 -24
- package/src/lib/components/console/product-labels-dialog.component.js.map +1 -1
- package/src/lib/components/console/products-hub-card.component.js +7 -20
- package/src/lib/components/console/products-hub-card.component.js.map +1 -1
- package/src/lib/model/commerce-shipping.d.ts +2 -0
- package/src/lib/model/commerce-shipping.js +1 -0
- package/src/lib/model/commerce-shipping.js.map +1 -1
- package/src/lib/model/order-dialog.d.ts +52 -0
- package/src/lib/model/order-dialog.js +85 -0
- package/src/lib/model/order-dialog.js.map +1 -0
- package/src/lib/model/product-write.d.ts +71 -0
- package/src/lib/model/product-write.js +28 -0
- package/src/lib/model/product-write.js.map +1 -1
- package/src/lib/printing/product-labels.d.ts +22 -0
- package/src/lib/printing/product-labels.js +39 -2
- package/src/lib/printing/product-labels.js.map +1 -1
- package/src/lib/server/billing-webhook.js +3 -3
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/order-figures.js +37 -0
- package/src/lib/server/order-figures.js.map +1 -1
- package/src/lib/server/order-print.d.ts +40 -0
- package/src/lib/server/order-print.js +113 -0
- package/src/lib/server/order-print.js.map +1 -0
- package/src/lib/server/products-write.d.ts +26 -0
- package/src/lib/server/products-write.js +184 -1
- package/src/lib/server/products-write.js.map +1 -1
- package/src/lib/server.js +8 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/transfer/shipping-presets.d.ts +16 -0
- package/src/lib/transfer/shipping-presets.js +34 -1
- package/src/lib/transfer/shipping-presets.js.map +1 -1
- package/src/lib/utils/order-invoice.d.ts +5 -0
- package/src/lib/utils/order-invoice.js +14 -0
- package/src/lib/utils/order-invoice.js.map +1 -1
- package/src/lib/utils/order-packing-slip.d.ts +31 -0
- package/src/lib/utils/order-packing-slip.js +46 -0
- package/src/lib/utils/order-packing-slip.js.map +1 -0
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
18
18
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
19
19
|
import * as CommerceModel from "../../model/index.js";
|
|
20
|
+
import { FULFILLMENT_OTHER_CARRIER, fulfillItemsIsEverything, fulfillItemsSelection, fulfillItemsStartQuantities, fulfillmentCarrierChoice } from "../../model/order-dialog.js";
|
|
20
21
|
import { Button, Checkbox, Chip, FormControlLabel, IconButton, InputAdornment, Link, Stack, TextField, Typography } from "@mui/material";
|
|
21
22
|
import { useCallback, useMemo, useState } from "react";
|
|
22
23
|
/**
|
|
@@ -27,7 +28,7 @@ import { useCallback, useMemo, useState } from "react";
|
|
|
27
28
|
* Neither writes. Every act goes through the dialog's `fulfill-order` route,
|
|
28
29
|
* which re-checks the quantities and the transition under the write; these
|
|
29
30
|
* only collect what the merchant means and hand it up.
|
|
30
|
-
*/ /** The carrier select's value for a carrier typed by hand. */ const OTHER_CARRIER =
|
|
31
|
+
*/ /** The carrier select's value for a carrier typed by hand. */ const OTHER_CARRIER = FULFILLMENT_OTHER_CARRIER;
|
|
31
32
|
/** The currency's symbol as the merchant reads it (`$`, `€`, `¥`), or its code. */ function currencySymbol(currency) {
|
|
32
33
|
try {
|
|
33
34
|
var _ref;
|
|
@@ -132,11 +133,7 @@ import { useCallback, useMemo, useState } from "react";
|
|
|
132
133
|
atMs: entry.atMs
|
|
133
134
|
};
|
|
134
135
|
}
|
|
135
|
-
/** The select's value for a typed carrier: a known name, or Other. */
|
|
136
|
-
if (!carrier) return '';
|
|
137
|
-
const label = CommerceModel.carrierLabelFor(carrier);
|
|
138
|
-
return CommerceModel.FULFILLMENT_CARRIER_CHOICES.includes(label) ? label : OTHER_CARRIER;
|
|
139
|
-
}
|
|
136
|
+
/** The select's value for a typed carrier: a known name, or Other. */ const carrierChoiceFor = fulfillmentCarrierChoice;
|
|
140
137
|
/**
|
|
141
138
|
* Carrier, tracking number and, for a carrier without a known tracker, the
|
|
142
139
|
* link. A native select so the choice is one keystroke and one change event.
|
|
@@ -237,12 +234,11 @@ import { useCallback, useMemo, useState } from "react";
|
|
|
237
234
|
const states = useMemo(()=>CommerceModel.orderLineFulfillmentStates(order), [
|
|
238
235
|
order
|
|
239
236
|
]);
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
states.some((candidate)=>candidate.requiresShipping && candidate.remainingQuantity > 0) ? state.requiresShipping ? state.remainingQuantity : 0 : state.remainingQuantity
|
|
237
|
+
// Digital and service lines start at zero (nothing to ship), unless
|
|
238
|
+
// nothing physical is left (`fulfillItemsStartQuantities`).
|
|
239
|
+
const [quantities, setQuantities] = useState(()=>Object.fromEntries(fulfillItemsStartQuantities(order).map((quantity, index)=>[
|
|
240
|
+
index,
|
|
241
|
+
quantity
|
|
246
242
|
])));
|
|
247
243
|
const [tracking, setTracking] = useState({
|
|
248
244
|
carrier: '',
|
|
@@ -261,23 +257,14 @@ import { useCallback, useMemo, useState } from "react";
|
|
|
261
257
|
]);
|
|
262
258
|
const costError = handEntered && 'error' in cost ? cost.error : null;
|
|
263
259
|
const [zoneKey, setZoneKey] = useState(0);
|
|
264
|
-
const selection = useMemo(()=>states.map((state)=>{
|
|
260
|
+
const selection = useMemo(()=>fulfillItemsSelection(states.map((state)=>{
|
|
265
261
|
var _quantities_state_lineItemId;
|
|
266
|
-
return
|
|
267
|
-
|
|
268
|
-
quantity: (_quantities_state_lineItemId = quantities[state.lineItemId]) != null ? _quantities_state_lineItemId : 0
|
|
269
|
-
};
|
|
270
|
-
}).filter((entry)=>entry.quantity > 0), [
|
|
262
|
+
return (_quantities_state_lineItemId = quantities[state.lineItemId]) != null ? _quantities_state_lineItemId : 0;
|
|
263
|
+
})), [
|
|
271
264
|
states,
|
|
272
265
|
quantities
|
|
273
266
|
]);
|
|
274
|
-
const everything = useMemo(()=>
|
|
275
|
-
const remaining = CommerceModel.remainingFulfillmentLines(order);
|
|
276
|
-
return remaining.length > 0 && remaining.length === selection.length && remaining.every((entry, index)=>{
|
|
277
|
-
var _selection_index, _selection_index1;
|
|
278
|
-
return ((_selection_index = selection[index]) == null ? void 0 : _selection_index.lineItemId) === entry.lineItemId && ((_selection_index1 = selection[index]) == null ? void 0 : _selection_index1.quantity) === entry.quantity;
|
|
279
|
-
});
|
|
280
|
-
}, [
|
|
267
|
+
const everything = useMemo(()=>fulfillItemsIsEverything(order, selection), [
|
|
281
268
|
order,
|
|
282
269
|
selection
|
|
283
270
|
]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/order-fulfillment-panel.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport * as CommerceModel from '../../model'\nimport {\n Button,\n Checkbox,\n Chip,\n FormControlLabel,\n IconButton,\n InputAdornment,\n Link,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useCallback, useMemo, useState, type ReactNode } from 'react'\nimport type {\n ConsoleFulfillmentTracking,\n ConsoleOrderZoneAddress,\n ConsoleOrderZoneFulfillment,\n ConsoleOrderZoneLocalDelivery,\n ConsoleOrderZoneOrder,\n} from './order-zones'\n\n/**\n * The order dialog's fulfillment surfaces (AGL-3611): the Fulfill items panel,\n * with a quantity per line, a carrier, a tracking number and the notify\n * switch; and the list of recorded shipments, each editable and cancellable.\n *\n * Neither writes. Every act goes through the dialog's `fulfill-order` route,\n * which re-checks the quantities and the transition under the write; these\n * only collect what the merchant means and hand it up.\n */\n\n/** The carrier select's value for a carrier typed by hand. */\nconst OTHER_CARRIER = 'other'\n\n/** What the panel asks the dialog to record. */\nexport interface FulfillItemsSubmission {\n /** Absent when the picked units are everything still to ship. */\n lineItems?: Array<{ lineItemId: number; quantity: number }>\n carrier: string\n trackingNumber: string\n trackingUrl?: string\n labelUrl?: string\n /**\n * What sending the shipment cost, typed by hand (AGL-3705): integer minor\n * units of the store's currency, 0 for free. Absent when the field was\n * left empty (not known) or a label was applied, whose own cost is kept.\n */\n shippingCostCents?: number\n notify: boolean\n}\n\n/** The currency's symbol as the merchant reads it (`$`, `€`, `¥`), or its code. */\nfunction currencySymbol(currency: string): string {\n try {\n const part = new Intl.NumberFormat('en-US', { style: 'currency', currency })\n .formatToParts(0)\n .find((entry) => entry.type === 'currency')\n return part?.value ?? currency\n } catch {\n return currency\n }\n}\n\n/** The order a widget reads: the dialog's order in the zone's words. */\nexport function consoleOrderZoneOrder(\n order: CommerceModel.HostOrder,\n orderId: string,\n): ConsoleOrderZoneOrder {\n const states = CommerceModel.orderLineFulfillmentStates(order)\n const address = order.shippingAddress\n const shippingAddress: ConsoleOrderZoneAddress | null = address\n ? {\n name: address.name ?? null,\n line1: address.line1 ?? null,\n line2: address.line2 ?? null,\n city: address.city ?? null,\n state: address.state ?? null,\n postalCode: address.postalCode ?? null,\n country: address.country ?? null,\n phone: address.phone ?? null,\n }\n : null\n return {\n id: orderId,\n number: CommerceModel.formatOrderNumber(order, orderId),\n status: order.status,\n channel: order.channel ?? 'online',\n // Orders are charged in US dollars today, as the public API reports them.\n currency: 'USD',\n customerEmail: order.customerEmail ?? null,\n customerName: order.customerName ?? null,\n shippingAddress,\n lines: (order.lineItems ?? []).map((line, index) => ({\n lineItemId: index,\n productId: line.productId,\n variantId: line.variantId ?? null,\n name: line.name,\n variantLabel: line.variantLabel ?? null,\n sku: line.sku ?? null,\n productType: line.productType ?? null,\n quantity: states[index]?.quantity ?? 0,\n unitAmountCents: line.unitAmountCents,\n fulfilledQuantity: states[index]?.fulfilledQuantity ?? 0,\n remainingQuantity: states[index]?.remainingQuantity ?? 0,\n requiresShipping: states[index]?.requiresShipping ?? true,\n })),\n fulfillments: (order.fulfillments ?? []).map((entry) => toZoneFulfillment(order, entry)),\n totals: {\n itemsCents: order.totals?.itemsCents ?? 0,\n shippingCents: order.totals?.shippingCents ?? 0,\n taxCents: order.totals?.taxCents ?? 0,\n discountCents: order.totals?.discountCents ?? 0,\n totalCents: order.totals?.totalCents ?? Number(order.amountCents ?? 0),\n },\n testMode: CommerceModel.orderIsTestMode(order),\n fulfillmentMethod: order.fulfillmentMethod ?? 'shipping',\n localDelivery: zoneLocalDelivery(order),\n }\n}\n\n/** The order's local delivery in the zone's words, or `null` on any other order (AGL-3695). */\nexport function zoneLocalDelivery(order: CommerceModel.HostOrder): ConsoleOrderZoneLocalDelivery | null {\n const delivery = order.fulfillmentMethod === 'local_delivery' ? order.localDelivery : undefined\n if (!delivery) return null\n const courier = delivery.courier\n return {\n status: CommerceModel.orderLocalDeliveryStatus(delivery.status),\n windowStartMs: Number(delivery.windowStartMs) || null,\n windowEndMs: Number(delivery.windowEndMs) || null,\n windowLabel: delivery.windowLabel ?? null,\n courier: courier\n ? {\n provider: String(courier.provider ?? ''),\n providerLabel: String(courier.providerLabel ?? courier.provider ?? 'Courier'),\n deliveryRef: String(courier.deliveryRef ?? ''),\n state: String(courier.state ?? ''),\n trackingUrl: courier.trackingUrl ?? null,\n etaMs: Number(courier.etaMs) || null,\n reason: courier.reason ?? null,\n testMode: courier.testMode === true,\n updatedAtMs: Number(courier.updatedAtMs) || 0,\n }\n : null,\n }\n}\n\n/** One recorded shipment in the zone's words. */\nexport function toZoneFulfillment(\n order: Pick<CommerceModel.HostOrder, 'lineItems'>,\n entry: CommerceModel.OrderFulfillment,\n): ConsoleOrderZoneFulfillment {\n return {\n id: entry.id,\n lines: CommerceModel.fulfillmentLineQuantities(order, entry),\n carrier: entry.carrier ?? null,\n trackingNumber: entry.trackingNumber ?? null,\n trackingUrl: CommerceModel.fulfillmentTrackingUrl(entry),\n labelUrl: entry.labelUrl ?? null,\n status: CommerceModel.fulfillmentIsActive(entry) ? 'active' : 'cancelled',\n atMs: entry.atMs,\n }\n}\n\n/** The select's value for a typed carrier: a known name, or Other. */\nfunction carrierChoiceFor(carrier: string): string {\n if (!carrier) return ''\n const label = CommerceModel.carrierLabelFor(carrier)\n return CommerceModel.FULFILLMENT_CARRIER_CHOICES.includes(label) ? label : OTHER_CARRIER\n}\n\ninterface CarrierFieldsProps {\n carrier: string\n trackingNumber: string\n trackingUrl: string\n onChange: (patch: { carrier?: string; trackingNumber?: string; trackingUrl?: string }) => void\n}\n\n/**\n * Carrier, tracking number and, for a carrier without a known tracker, the\n * link. A native select so the choice is one keystroke and one change event.\n */\nfunction CarrierFields(props: CarrierFieldsProps) {\n const { carrier, trackingNumber, trackingUrl, onChange } = props\n const [other, setOther] = useState(() => carrierChoiceFor(carrier) === OTHER_CARRIER)\n const choice = other ? OTHER_CARRIER : carrierChoiceFor(carrier)\n const derived = CommerceModel.trackingUrlFor(carrier, trackingNumber)\n return (\n <Stack spacing={1}>\n <Stack direction=\"row\" spacing={1}>\n <TextField\n select\n label=\"Carrier\"\n value={choice}\n onChange={(event) => {\n const value = event.target.value\n setOther(value === OTHER_CARRIER)\n onChange({ carrier: value === OTHER_CARRIER ? '' : value })\n }}\n size=\"small\"\n sx={{ width: 160 }}\n slotProps={{ select: { native: true }, inputLabel: { shrink: true } }}\n >\n <option value=\"\">{'None'}</option>\n {CommerceModel.FULFILLMENT_CARRIER_CHOICES.map((name) => (\n <option key={name} value={name}>\n {name}\n </option>\n ))}\n <option value={OTHER_CARRIER}>{'Other'}</option>\n </TextField>\n {other ? (\n <TextField\n label=\"Carrier name\"\n value={carrier}\n onChange={(event) => onChange({ carrier: event.target.value })}\n size=\"small\"\n sx={{ width: 160 }}\n />\n ) : null}\n <TextField\n label=\"Tracking number\"\n value={trackingNumber}\n onChange={(event) => onChange({ trackingNumber: event.target.value })}\n size=\"small\"\n sx={{ flex: 1 }}\n />\n </Stack>\n {trackingNumber && !derived ? (\n <TextField\n label=\"Tracking link\"\n value={trackingUrl}\n onChange={(event) => onChange({ trackingUrl: event.target.value })}\n size=\"small\"\n placeholder=\"https://\"\n helperText=\"Optional. USPS, UPS, FedEx, DHL, Canada Post, Royal Mail and Australia Post get a link on their own.\"\n />\n ) : null}\n </Stack>\n )\n}\n\nexport interface FulfillItemsPanelProps {\n order: CommerceModel.HostOrder\n busy: boolean\n onSubmit: (submission: FulfillItemsSubmission) => Promise<boolean>\n onCancel: () => void\n /** The store's currency (ISO 4217), which a hand-entered shipping cost is in. Default USD. */\n currency?: string\n /** Draws the `orderFulfillment` zone with the panel's current selection. */\n renderZone?: (\n selection: ReadonlyArray<{ lineItemId: number; quantity: number }>,\n applyTracking: (tracking: ConsoleFulfillmentTracking) => void,\n ) => ReactNode\n}\n\n/**\n * Fulfill items: one quantity per line, starting at what is left of each\n * shippable line, so the common case — ship everything — is one click.\n */\nexport function FulfillItemsPanel(props: FulfillItemsPanelProps) {\n const { order, busy, onSubmit, onCancel, renderZone } = props\n const currency = String(props.currency || 'USD').toUpperCase()\n const states = useMemo(() => CommerceModel.orderLineFulfillmentStates(order), [order])\n const [quantities, setQuantities] = useState<Record<number, number>>(() =>\n Object.fromEntries(\n states.map((state) => [\n state.lineItemId,\n // Digital and service lines start at zero: nothing to ship. When the\n // order has nothing physical left, they start full instead, so the\n // panel still closes a digital-only order in one click.\n states.some((candidate) => candidate.requiresShipping && candidate.remainingQuantity > 0)\n ? state.requiresShipping\n ? state.remainingQuantity\n : 0\n : state.remainingQuantity,\n ]),\n ),\n )\n const [tracking, setTracking] = useState({ carrier: '', trackingNumber: '', trackingUrl: '', labelUrl: '' })\n const [notify, setNotify] = useState(true)\n const [shippingCost, setShippingCost] = useState('')\n // A label applied by a widget carries its own cost, recorded where it was\n // bought; the hand-entered field is for a shipment sent some other way.\n const handEntered = !tracking.labelUrl\n const cost = useMemo(() => CommerceModel.parseShippingCostInput(shippingCost, currency), [shippingCost, currency])\n const costError = handEntered && 'error' in cost ? cost.error : null\n const [zoneKey, setZoneKey] = useState(0)\n\n const selection = useMemo(\n () =>\n states\n .map((state) => ({ lineItemId: state.lineItemId, quantity: quantities[state.lineItemId] ?? 0 }))\n .filter((entry) => entry.quantity > 0),\n [states, quantities],\n )\n const everything = useMemo(() => {\n const remaining = CommerceModel.remainingFulfillmentLines(order)\n return (\n remaining.length > 0 &&\n remaining.length === selection.length &&\n remaining.every(\n (entry, index) =>\n selection[index]?.lineItemId === entry.lineItemId && selection[index]?.quantity === entry.quantity,\n )\n )\n }, [order, selection])\n\n const setQuantity = (lineItemId: number, value: number, max: number) =>\n setQuantities((current) => ({\n ...current,\n [lineItemId]: Math.max(0, Math.min(max, Math.floor(Number.isFinite(value) ? value : 0))),\n }))\n\n const applyTracking = useCallback((next: ConsoleFulfillmentTracking) => {\n setTracking({\n carrier: String(next.carrier ?? ''),\n trackingNumber: String(next.trackingNumber ?? ''),\n trackingUrl: String(next.trackingUrl ?? ''),\n labelUrl: String(next.labelUrl ?? ''),\n })\n // The carrier select reads its Other state once; a remount re-reads it.\n setZoneKey((key) => key + 1)\n }, [])\n\n const submit = async () => {\n await onSubmit({\n ...(everything ? {} : { lineItems: selection }),\n carrier: tracking.carrier.trim(),\n trackingNumber: tracking.trackingNumber.trim(),\n ...(tracking.trackingUrl.trim() ? { trackingUrl: tracking.trackingUrl.trim() } : {}),\n ...(tracking.labelUrl ? { labelUrl: tracking.labelUrl } : {}),\n ...(handEntered && 'cents' in cost && cost.cents !== null ? { shippingCostCents: cost.cents } : {}),\n notify,\n })\n }\n\n return (\n <Stack spacing={1} sx={{ border: 1, borderColor: 'divider', borderRadius: 1, p: 1.5 }}>\n <Typography variant=\"subtitle2\">{'Fulfill items'}</Typography>\n {(order.lineItems ?? []).map((line, index) => {\n const state = states[index]\n if (!state || state.remainingQuantity === 0) return null\n return (\n <Stack key={index} direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\" sx={{ flex: 1 }}>\n {line.name + (line.variantLabel ? ` — ${line.variantLabel}` : '')}\n {state.requiresShipping ? null : (\n <Typography component=\"span\" variant=\"caption\" color=\"text.secondary\">\n {' · no shipping needed'}\n </Typography>\n )}\n </Typography>\n <IconButton\n size=\"small\"\n aria-label={`One fewer ${line.name}`}\n disabled={busy || (quantities[index] ?? 0) <= 0}\n onClick={() => setQuantity(index, (quantities[index] ?? 0) - 1, state.remainingQuantity)}\n >\n {'−'}\n </IconButton>\n <TextField\n size=\"small\"\n type=\"number\"\n value={quantities[index] ?? 0}\n onChange={(event) => setQuantity(index, Number(event.target.value), state.remainingQuantity)}\n sx={{ width: 72 }}\n slotProps={{\n htmlInput: {\n min: 0,\n max: state.remainingQuantity,\n 'aria-label': `Quantity of ${line.name} to fulfill`,\n },\n }}\n />\n <IconButton\n size=\"small\"\n aria-label={`One more ${line.name}`}\n disabled={busy || (quantities[index] ?? 0) >= state.remainingQuantity}\n onClick={() => setQuantity(index, (quantities[index] ?? 0) + 1, state.remainingQuantity)}\n >\n {'+'}\n </IconButton>\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ width: 56 }}>\n {`of ${state.remainingQuantity}`}\n </Typography>\n </Stack>\n )\n })}\n <CarrierFields\n key={zoneKey}\n carrier={tracking.carrier}\n trackingNumber={tracking.trackingNumber}\n trackingUrl={tracking.trackingUrl}\n onChange={(patch) => setTracking((current) => ({ ...current, ...patch }))}\n />\n {tracking.labelUrl ? (\n <Link href={tracking.labelUrl} target=\"_blank\" rel=\"noopener noreferrer\" variant=\"caption\">\n {'Shipping label'}\n </Link>\n ) : null}\n {handEntered ? (\n <TextField\n label=\"Shipping cost\"\n value={shippingCost}\n onChange={(event) => setShippingCost(event.target.value)}\n size=\"small\"\n sx={{ width: 220 }}\n error={Boolean(costError)}\n helperText={costError ?? `Optional. What sending this shipment cost you, in ${currency}.`}\n slotProps={{\n input: { startAdornment: <InputAdornment position=\"start\">{currencySymbol(currency)}</InputAdornment> },\n htmlInput: { inputMode: 'decimal' },\n }}\n />\n ) : null}\n {renderZone ? renderZone(selection, applyTracking) : null}\n <FormControlLabel\n control={<Checkbox checked={notify} onChange={(event) => setNotify(event.target.checked)} />}\n label=\"Notify customer\"\n />\n <Stack direction=\"row\" spacing={1} sx={{ justifyContent: 'flex-end' }}>\n <Button size=\"small\" onClick={onCancel} disabled={busy}>\n {'Cancel'}\n </Button>\n <Button\n size=\"small\"\n variant=\"contained\"\n color=\"primary\"\n disabled={busy || selection.length === 0 || Boolean(costError)}\n onClick={submit}\n >\n {'Fulfill'}\n </Button>\n </Stack>\n </Stack>\n )\n}\n\nexport interface FulfillmentListProps {\n order: CommerceModel.HostOrder\n busy: boolean\n onUpdateTracking: (\n fulfillmentId: string,\n tracking: { carrier: string; trackingNumber: string; trackingUrl?: string },\n ) => Promise<boolean>\n onCancelFulfillment: (fulfillmentId: string) => Promise<void>\n}\n\n/** The shipments recorded on the order, newest last, with edit and cancel. */\nexport function FulfillmentList(props: FulfillmentListProps) {\n const { order, busy, onUpdateTracking, onCancelFulfillment } = props\n const [editing, setEditing] = useState<{\n id: string\n carrier: string\n trackingNumber: string\n trackingUrl: string\n } | null>(null)\n const fulfillments = order.fulfillments ?? []\n if (fulfillments.length === 0) return null\n const editable = CommerceModel.orderFulfillmentsEditable(order)\n return (\n <Stack spacing={1}>\n <Typography variant=\"subtitle2\">{'Shipments'}</Typography>\n {fulfillments.map((entry) => {\n const active = CommerceModel.fulfillmentIsActive(entry)\n const lines = CommerceModel.fulfillmentLineQuantities(order, entry)\n const url = CommerceModel.fulfillmentTrackingUrl(entry)\n const carrier = entry.carrier ? CommerceModel.carrierLabelFor(entry.carrier) : ''\n return (\n <Stack key={entry.id} spacing={0.5}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography\n variant=\"body2\"\n sx={{ flex: 1, ...(active ? {} : { textDecoration: 'line-through' }) }}\n color={active ? 'text.primary' : 'text.secondary'}\n >\n {CommerceModel.describeFulfillmentLines(order, lines) || 'Fulfilled'}\n </Typography>\n {active ? null : <Chip label=\"canceled\" size=\"small\" variant=\"outlined\" />}\n {active && editable && editing?.id !== entry.id ? (\n <>\n <Button\n size=\"small\"\n disabled={busy}\n onClick={() =>\n setEditing({\n id: entry.id,\n carrier: entry.carrier ?? '',\n trackingNumber: entry.trackingNumber ?? '',\n trackingUrl: entry.trackingUrl ?? '',\n })\n }\n >\n {'Edit tracking'}\n </Button>\n <Button size=\"small\" color=\"error\" disabled={busy} onClick={() => onCancelFulfillment(entry.id)}>\n {'Cancel shipment'}\n </Button>\n </>\n ) : null}\n </Stack>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${new Date(entry.atMs).toLocaleString()}`}\n {entry.trackingNumber ? ` · ${carrier || 'Tracking'} ` : ''}\n {entry.trackingNumber ? (\n url ? (\n <Link href={url} target=\"_blank\" rel=\"noopener noreferrer\">\n {entry.trackingNumber}\n </Link>\n ) : (\n entry.trackingNumber\n )\n ) : null}\n {entry.labelUrl ? (\n <>\n {' · '}\n <Link href={entry.labelUrl} target=\"_blank\" rel=\"noopener noreferrer\">\n {'Label'}\n </Link>\n </>\n ) : null}\n </Typography>\n {editing?.id === entry.id ? (\n <Stack spacing={1}>\n <CarrierFields\n carrier={editing.carrier}\n trackingNumber={editing.trackingNumber}\n trackingUrl={editing.trackingUrl}\n onChange={(patch) => setEditing({ ...editing, ...patch })}\n />\n <Stack direction=\"row\" spacing={1} sx={{ justifyContent: 'flex-end' }}>\n <Button size=\"small\" disabled={busy} onClick={() => setEditing(null)}>\n {'Discard'}\n </Button>\n <Button\n size=\"small\"\n variant=\"contained\"\n disabled={busy}\n onClick={async () => {\n const saved = await onUpdateTracking(entry.id, {\n carrier: editing.carrier.trim(),\n trackingNumber: editing.trackingNumber.trim(),\n ...(editing.trackingUrl.trim() ? { trackingUrl: editing.trackingUrl.trim() } : {}),\n })\n if (saved) setEditing(null)\n }}\n >\n {'Save tracking'}\n </Button>\n </Stack>\n </Stack>\n ) : null}\n </Stack>\n )\n })}\n </Stack>\n )\n}\n"],"names":["CommerceModel","Button","Checkbox","Chip","FormControlLabel","IconButton","InputAdornment","Link","Stack","TextField","Typography","useCallback","useMemo","useState","OTHER_CARRIER","currencySymbol","currency","part","Intl","NumberFormat","style","formatToParts","find","entry","type","value","consoleOrderZoneOrder","order","orderId","address","states","orderLineFulfillmentStates","shippingAddress","name","line1","line2","city","state","postalCode","country","phone","id","number","formatOrderNumber","status","channel","customerEmail","customerName","lines","lineItems","map","line","index","lineItemId","productId","variantId","variantLabel","sku","productType","quantity","unitAmountCents","fulfilledQuantity","remainingQuantity","requiresShipping","fulfillments","toZoneFulfillment","totals","itemsCents","shippingCents","taxCents","discountCents","totalCents","Number","amountCents","testMode","orderIsTestMode","fulfillmentMethod","localDelivery","zoneLocalDelivery","delivery","courier","undefined","orderLocalDeliveryStatus","windowStartMs","windowEndMs","windowLabel","provider","String","providerLabel","deliveryRef","trackingUrl","etaMs","reason","updatedAtMs","fulfillmentLineQuantities","carrier","trackingNumber","fulfillmentTrackingUrl","labelUrl","fulfillmentIsActive","atMs","carrierChoiceFor","label","carrierLabelFor","FULFILLMENT_CARRIER_CHOICES","includes","CarrierFields","props","onChange","other","setOther","choice","derived","trackingUrlFor","spacing","direction","select","event","target","size","sx","width","slotProps","native","inputLabel","shrink","option","flex","placeholder","helperText","FulfillItemsPanel","busy","onSubmit","onCancel","renderZone","toUpperCase","quantities","setQuantities","Object","fromEntries","some","candidate","tracking","setTracking","notify","setNotify","shippingCost","setShippingCost","handEntered","cost","parseShippingCostInput","costError","error","zoneKey","setZoneKey","selection","filter","everything","remaining","remainingFulfillmentLines","length","every","setQuantity","max","current","Math","min","floor","isFinite","applyTracking","next","key","submit","trim","cents","shippingCostCents","border","borderColor","borderRadius","p","variant","alignItems","component","color","aria-label","disabled","onClick","htmlInput","patch","href","rel","Boolean","input","startAdornment","position","inputMode","control","checked","justifyContent","FulfillmentList","onUpdateTracking","onCancelFulfillment","editing","setEditing","editable","orderFulfillmentsEditable","active","url","textDecoration","describeFulfillmentLines","Date","toLocaleString","saved"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/order-fulfillment-panel.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport * as CommerceModel from '../../model'\nimport {\n FULFILLMENT_OTHER_CARRIER,\n fulfillItemsIsEverything,\n fulfillItemsSelection,\n fulfillItemsStartQuantities,\n fulfillmentCarrierChoice,\n} from '../../model/order-dialog'\nimport {\n Button,\n Checkbox,\n Chip,\n FormControlLabel,\n IconButton,\n InputAdornment,\n Link,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useCallback, useMemo, useState, type ReactNode } from 'react'\nimport type {\n ConsoleFulfillmentTracking,\n ConsoleOrderZoneAddress,\n ConsoleOrderZoneFulfillment,\n ConsoleOrderZoneLocalDelivery,\n ConsoleOrderZoneOrder,\n} from './order-zones'\n\n/**\n * The order dialog's fulfillment surfaces (AGL-3611): the Fulfill items panel,\n * with a quantity per line, a carrier, a tracking number and the notify\n * switch; and the list of recorded shipments, each editable and cancellable.\n *\n * Neither writes. Every act goes through the dialog's `fulfill-order` route,\n * which re-checks the quantities and the transition under the write; these\n * only collect what the merchant means and hand it up.\n */\n\n/** The carrier select's value for a carrier typed by hand. */\nconst OTHER_CARRIER = FULFILLMENT_OTHER_CARRIER\n\n/** What the panel asks the dialog to record. */\nexport interface FulfillItemsSubmission {\n /** Absent when the picked units are everything still to ship. */\n lineItems?: Array<{ lineItemId: number; quantity: number }>\n carrier: string\n trackingNumber: string\n trackingUrl?: string\n labelUrl?: string\n /**\n * What sending the shipment cost, typed by hand (AGL-3705): integer minor\n * units of the store's currency, 0 for free. Absent when the field was\n * left empty (not known) or a label was applied, whose own cost is kept.\n */\n shippingCostCents?: number\n notify: boolean\n}\n\n/** The currency's symbol as the merchant reads it (`$`, `€`, `¥`), or its code. */\nfunction currencySymbol(currency: string): string {\n try {\n const part = new Intl.NumberFormat('en-US', { style: 'currency', currency })\n .formatToParts(0)\n .find((entry) => entry.type === 'currency')\n return part?.value ?? currency\n } catch {\n return currency\n }\n}\n\n/** The order a widget reads: the dialog's order in the zone's words. */\nexport function consoleOrderZoneOrder(\n order: CommerceModel.HostOrder,\n orderId: string,\n): ConsoleOrderZoneOrder {\n const states = CommerceModel.orderLineFulfillmentStates(order)\n const address = order.shippingAddress\n const shippingAddress: ConsoleOrderZoneAddress | null = address\n ? {\n name: address.name ?? null,\n line1: address.line1 ?? null,\n line2: address.line2 ?? null,\n city: address.city ?? null,\n state: address.state ?? null,\n postalCode: address.postalCode ?? null,\n country: address.country ?? null,\n phone: address.phone ?? null,\n }\n : null\n return {\n id: orderId,\n number: CommerceModel.formatOrderNumber(order, orderId),\n status: order.status,\n channel: order.channel ?? 'online',\n // Orders are charged in US dollars today, as the public API reports them.\n currency: 'USD',\n customerEmail: order.customerEmail ?? null,\n customerName: order.customerName ?? null,\n shippingAddress,\n lines: (order.lineItems ?? []).map((line, index) => ({\n lineItemId: index,\n productId: line.productId,\n variantId: line.variantId ?? null,\n name: line.name,\n variantLabel: line.variantLabel ?? null,\n sku: line.sku ?? null,\n productType: line.productType ?? null,\n quantity: states[index]?.quantity ?? 0,\n unitAmountCents: line.unitAmountCents,\n fulfilledQuantity: states[index]?.fulfilledQuantity ?? 0,\n remainingQuantity: states[index]?.remainingQuantity ?? 0,\n requiresShipping: states[index]?.requiresShipping ?? true,\n })),\n fulfillments: (order.fulfillments ?? []).map((entry) => toZoneFulfillment(order, entry)),\n totals: {\n itemsCents: order.totals?.itemsCents ?? 0,\n shippingCents: order.totals?.shippingCents ?? 0,\n taxCents: order.totals?.taxCents ?? 0,\n discountCents: order.totals?.discountCents ?? 0,\n totalCents: order.totals?.totalCents ?? Number(order.amountCents ?? 0),\n },\n testMode: CommerceModel.orderIsTestMode(order),\n fulfillmentMethod: order.fulfillmentMethod ?? 'shipping',\n localDelivery: zoneLocalDelivery(order),\n }\n}\n\n/** The order's local delivery in the zone's words, or `null` on any other order (AGL-3695). */\nexport function zoneLocalDelivery(order: CommerceModel.HostOrder): ConsoleOrderZoneLocalDelivery | null {\n const delivery = order.fulfillmentMethod === 'local_delivery' ? order.localDelivery : undefined\n if (!delivery) return null\n const courier = delivery.courier\n return {\n status: CommerceModel.orderLocalDeliveryStatus(delivery.status),\n windowStartMs: Number(delivery.windowStartMs) || null,\n windowEndMs: Number(delivery.windowEndMs) || null,\n windowLabel: delivery.windowLabel ?? null,\n courier: courier\n ? {\n provider: String(courier.provider ?? ''),\n providerLabel: String(courier.providerLabel ?? courier.provider ?? 'Courier'),\n deliveryRef: String(courier.deliveryRef ?? ''),\n state: String(courier.state ?? ''),\n trackingUrl: courier.trackingUrl ?? null,\n etaMs: Number(courier.etaMs) || null,\n reason: courier.reason ?? null,\n testMode: courier.testMode === true,\n updatedAtMs: Number(courier.updatedAtMs) || 0,\n }\n : null,\n }\n}\n\n/** One recorded shipment in the zone's words. */\nexport function toZoneFulfillment(\n order: Pick<CommerceModel.HostOrder, 'lineItems'>,\n entry: CommerceModel.OrderFulfillment,\n): ConsoleOrderZoneFulfillment {\n return {\n id: entry.id,\n lines: CommerceModel.fulfillmentLineQuantities(order, entry),\n carrier: entry.carrier ?? null,\n trackingNumber: entry.trackingNumber ?? null,\n trackingUrl: CommerceModel.fulfillmentTrackingUrl(entry),\n labelUrl: entry.labelUrl ?? null,\n status: CommerceModel.fulfillmentIsActive(entry) ? 'active' : 'cancelled',\n atMs: entry.atMs,\n }\n}\n\n/** The select's value for a typed carrier: a known name, or Other. */\nconst carrierChoiceFor = fulfillmentCarrierChoice\n\ninterface CarrierFieldsProps {\n carrier: string\n trackingNumber: string\n trackingUrl: string\n onChange: (patch: { carrier?: string; trackingNumber?: string; trackingUrl?: string }) => void\n}\n\n/**\n * Carrier, tracking number and, for a carrier without a known tracker, the\n * link. A native select so the choice is one keystroke and one change event.\n */\nfunction CarrierFields(props: CarrierFieldsProps) {\n const { carrier, trackingNumber, trackingUrl, onChange } = props\n const [other, setOther] = useState(() => carrierChoiceFor(carrier) === OTHER_CARRIER)\n const choice = other ? OTHER_CARRIER : carrierChoiceFor(carrier)\n const derived = CommerceModel.trackingUrlFor(carrier, trackingNumber)\n return (\n <Stack spacing={1}>\n <Stack direction=\"row\" spacing={1}>\n <TextField\n select\n label=\"Carrier\"\n value={choice}\n onChange={(event) => {\n const value = event.target.value\n setOther(value === OTHER_CARRIER)\n onChange({ carrier: value === OTHER_CARRIER ? '' : value })\n }}\n size=\"small\"\n sx={{ width: 160 }}\n slotProps={{ select: { native: true }, inputLabel: { shrink: true } }}\n >\n <option value=\"\">{'None'}</option>\n {CommerceModel.FULFILLMENT_CARRIER_CHOICES.map((name) => (\n <option key={name} value={name}>\n {name}\n </option>\n ))}\n <option value={OTHER_CARRIER}>{'Other'}</option>\n </TextField>\n {other ? (\n <TextField\n label=\"Carrier name\"\n value={carrier}\n onChange={(event) => onChange({ carrier: event.target.value })}\n size=\"small\"\n sx={{ width: 160 }}\n />\n ) : null}\n <TextField\n label=\"Tracking number\"\n value={trackingNumber}\n onChange={(event) => onChange({ trackingNumber: event.target.value })}\n size=\"small\"\n sx={{ flex: 1 }}\n />\n </Stack>\n {trackingNumber && !derived ? (\n <TextField\n label=\"Tracking link\"\n value={trackingUrl}\n onChange={(event) => onChange({ trackingUrl: event.target.value })}\n size=\"small\"\n placeholder=\"https://\"\n helperText=\"Optional. USPS, UPS, FedEx, DHL, Canada Post, Royal Mail and Australia Post get a link on their own.\"\n />\n ) : null}\n </Stack>\n )\n}\n\nexport interface FulfillItemsPanelProps {\n order: CommerceModel.HostOrder\n busy: boolean\n onSubmit: (submission: FulfillItemsSubmission) => Promise<boolean>\n onCancel: () => void\n /** The store's currency (ISO 4217), which a hand-entered shipping cost is in. Default USD. */\n currency?: string\n /** Draws the `orderFulfillment` zone with the panel's current selection. */\n renderZone?: (\n selection: ReadonlyArray<{ lineItemId: number; quantity: number }>,\n applyTracking: (tracking: ConsoleFulfillmentTracking) => void,\n ) => ReactNode\n}\n\n/**\n * Fulfill items: one quantity per line, starting at what is left of each\n * shippable line, so the common case — ship everything — is one click.\n */\nexport function FulfillItemsPanel(props: FulfillItemsPanelProps) {\n const { order, busy, onSubmit, onCancel, renderZone } = props\n const currency = String(props.currency || 'USD').toUpperCase()\n const states = useMemo(() => CommerceModel.orderLineFulfillmentStates(order), [order])\n // Digital and service lines start at zero (nothing to ship), unless\n // nothing physical is left (`fulfillItemsStartQuantities`).\n const [quantities, setQuantities] = useState<Record<number, number>>(() =>\n Object.fromEntries(fulfillItemsStartQuantities(order).map((quantity, index) => [index, quantity])),\n )\n const [tracking, setTracking] = useState({ carrier: '', trackingNumber: '', trackingUrl: '', labelUrl: '' })\n const [notify, setNotify] = useState(true)\n const [shippingCost, setShippingCost] = useState('')\n // A label applied by a widget carries its own cost, recorded where it was\n // bought; the hand-entered field is for a shipment sent some other way.\n const handEntered = !tracking.labelUrl\n const cost = useMemo(() => CommerceModel.parseShippingCostInput(shippingCost, currency), [shippingCost, currency])\n const costError = handEntered && 'error' in cost ? cost.error : null\n const [zoneKey, setZoneKey] = useState(0)\n\n const selection = useMemo(\n () => fulfillItemsSelection(states.map((state) => quantities[state.lineItemId] ?? 0)),\n [states, quantities],\n )\n const everything = useMemo(() => fulfillItemsIsEverything(order, selection), [order, selection])\n\n const setQuantity = (lineItemId: number, value: number, max: number) =>\n setQuantities((current) => ({\n ...current,\n [lineItemId]: Math.max(0, Math.min(max, Math.floor(Number.isFinite(value) ? value : 0))),\n }))\n\n const applyTracking = useCallback((next: ConsoleFulfillmentTracking) => {\n setTracking({\n carrier: String(next.carrier ?? ''),\n trackingNumber: String(next.trackingNumber ?? ''),\n trackingUrl: String(next.trackingUrl ?? ''),\n labelUrl: String(next.labelUrl ?? ''),\n })\n // The carrier select reads its Other state once; a remount re-reads it.\n setZoneKey((key) => key + 1)\n }, [])\n\n const submit = async () => {\n await onSubmit({\n ...(everything ? {} : { lineItems: selection }),\n carrier: tracking.carrier.trim(),\n trackingNumber: tracking.trackingNumber.trim(),\n ...(tracking.trackingUrl.trim() ? { trackingUrl: tracking.trackingUrl.trim() } : {}),\n ...(tracking.labelUrl ? { labelUrl: tracking.labelUrl } : {}),\n ...(handEntered && 'cents' in cost && cost.cents !== null ? { shippingCostCents: cost.cents } : {}),\n notify,\n })\n }\n\n return (\n <Stack spacing={1} sx={{ border: 1, borderColor: 'divider', borderRadius: 1, p: 1.5 }}>\n <Typography variant=\"subtitle2\">{'Fulfill items'}</Typography>\n {(order.lineItems ?? []).map((line, index) => {\n const state = states[index]\n if (!state || state.remainingQuantity === 0) return null\n return (\n <Stack key={index} direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\" sx={{ flex: 1 }}>\n {line.name + (line.variantLabel ? ` — ${line.variantLabel}` : '')}\n {state.requiresShipping ? null : (\n <Typography component=\"span\" variant=\"caption\" color=\"text.secondary\">\n {' · no shipping needed'}\n </Typography>\n )}\n </Typography>\n <IconButton\n size=\"small\"\n aria-label={`One fewer ${line.name}`}\n disabled={busy || (quantities[index] ?? 0) <= 0}\n onClick={() => setQuantity(index, (quantities[index] ?? 0) - 1, state.remainingQuantity)}\n >\n {'−'}\n </IconButton>\n <TextField\n size=\"small\"\n type=\"number\"\n value={quantities[index] ?? 0}\n onChange={(event) => setQuantity(index, Number(event.target.value), state.remainingQuantity)}\n sx={{ width: 72 }}\n slotProps={{\n htmlInput: {\n min: 0,\n max: state.remainingQuantity,\n 'aria-label': `Quantity of ${line.name} to fulfill`,\n },\n }}\n />\n <IconButton\n size=\"small\"\n aria-label={`One more ${line.name}`}\n disabled={busy || (quantities[index] ?? 0) >= state.remainingQuantity}\n onClick={() => setQuantity(index, (quantities[index] ?? 0) + 1, state.remainingQuantity)}\n >\n {'+'}\n </IconButton>\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ width: 56 }}>\n {`of ${state.remainingQuantity}`}\n </Typography>\n </Stack>\n )\n })}\n <CarrierFields\n key={zoneKey}\n carrier={tracking.carrier}\n trackingNumber={tracking.trackingNumber}\n trackingUrl={tracking.trackingUrl}\n onChange={(patch) => setTracking((current) => ({ ...current, ...patch }))}\n />\n {tracking.labelUrl ? (\n <Link href={tracking.labelUrl} target=\"_blank\" rel=\"noopener noreferrer\" variant=\"caption\">\n {'Shipping label'}\n </Link>\n ) : null}\n {handEntered ? (\n <TextField\n label=\"Shipping cost\"\n value={shippingCost}\n onChange={(event) => setShippingCost(event.target.value)}\n size=\"small\"\n sx={{ width: 220 }}\n error={Boolean(costError)}\n helperText={costError ?? `Optional. What sending this shipment cost you, in ${currency}.`}\n slotProps={{\n input: { startAdornment: <InputAdornment position=\"start\">{currencySymbol(currency)}</InputAdornment> },\n htmlInput: { inputMode: 'decimal' },\n }}\n />\n ) : null}\n {renderZone ? renderZone(selection, applyTracking) : null}\n <FormControlLabel\n control={<Checkbox checked={notify} onChange={(event) => setNotify(event.target.checked)} />}\n label=\"Notify customer\"\n />\n <Stack direction=\"row\" spacing={1} sx={{ justifyContent: 'flex-end' }}>\n <Button size=\"small\" onClick={onCancel} disabled={busy}>\n {'Cancel'}\n </Button>\n <Button\n size=\"small\"\n variant=\"contained\"\n color=\"primary\"\n disabled={busy || selection.length === 0 || Boolean(costError)}\n onClick={submit}\n >\n {'Fulfill'}\n </Button>\n </Stack>\n </Stack>\n )\n}\n\nexport interface FulfillmentListProps {\n order: CommerceModel.HostOrder\n busy: boolean\n onUpdateTracking: (\n fulfillmentId: string,\n tracking: { carrier: string; trackingNumber: string; trackingUrl?: string },\n ) => Promise<boolean>\n onCancelFulfillment: (fulfillmentId: string) => Promise<void>\n}\n\n/** The shipments recorded on the order, newest last, with edit and cancel. */\nexport function FulfillmentList(props: FulfillmentListProps) {\n const { order, busy, onUpdateTracking, onCancelFulfillment } = props\n const [editing, setEditing] = useState<{\n id: string\n carrier: string\n trackingNumber: string\n trackingUrl: string\n } | null>(null)\n const fulfillments = order.fulfillments ?? []\n if (fulfillments.length === 0) return null\n const editable = CommerceModel.orderFulfillmentsEditable(order)\n return (\n <Stack spacing={1}>\n <Typography variant=\"subtitle2\">{'Shipments'}</Typography>\n {fulfillments.map((entry) => {\n const active = CommerceModel.fulfillmentIsActive(entry)\n const lines = CommerceModel.fulfillmentLineQuantities(order, entry)\n const url = CommerceModel.fulfillmentTrackingUrl(entry)\n const carrier = entry.carrier ? CommerceModel.carrierLabelFor(entry.carrier) : ''\n return (\n <Stack key={entry.id} spacing={0.5}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography\n variant=\"body2\"\n sx={{ flex: 1, ...(active ? {} : { textDecoration: 'line-through' }) }}\n color={active ? 'text.primary' : 'text.secondary'}\n >\n {CommerceModel.describeFulfillmentLines(order, lines) || 'Fulfilled'}\n </Typography>\n {active ? null : <Chip label=\"canceled\" size=\"small\" variant=\"outlined\" />}\n {active && editable && editing?.id !== entry.id ? (\n <>\n <Button\n size=\"small\"\n disabled={busy}\n onClick={() =>\n setEditing({\n id: entry.id,\n carrier: entry.carrier ?? '',\n trackingNumber: entry.trackingNumber ?? '',\n trackingUrl: entry.trackingUrl ?? '',\n })\n }\n >\n {'Edit tracking'}\n </Button>\n <Button size=\"small\" color=\"error\" disabled={busy} onClick={() => onCancelFulfillment(entry.id)}>\n {'Cancel shipment'}\n </Button>\n </>\n ) : null}\n </Stack>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${new Date(entry.atMs).toLocaleString()}`}\n {entry.trackingNumber ? ` · ${carrier || 'Tracking'} ` : ''}\n {entry.trackingNumber ? (\n url ? (\n <Link href={url} target=\"_blank\" rel=\"noopener noreferrer\">\n {entry.trackingNumber}\n </Link>\n ) : (\n entry.trackingNumber\n )\n ) : null}\n {entry.labelUrl ? (\n <>\n {' · '}\n <Link href={entry.labelUrl} target=\"_blank\" rel=\"noopener noreferrer\">\n {'Label'}\n </Link>\n </>\n ) : null}\n </Typography>\n {editing?.id === entry.id ? (\n <Stack spacing={1}>\n <CarrierFields\n carrier={editing.carrier}\n trackingNumber={editing.trackingNumber}\n trackingUrl={editing.trackingUrl}\n onChange={(patch) => setEditing({ ...editing, ...patch })}\n />\n <Stack direction=\"row\" spacing={1} sx={{ justifyContent: 'flex-end' }}>\n <Button size=\"small\" disabled={busy} onClick={() => setEditing(null)}>\n {'Discard'}\n </Button>\n <Button\n size=\"small\"\n variant=\"contained\"\n disabled={busy}\n onClick={async () => {\n const saved = await onUpdateTracking(entry.id, {\n carrier: editing.carrier.trim(),\n trackingNumber: editing.trackingNumber.trim(),\n ...(editing.trackingUrl.trim() ? { trackingUrl: editing.trackingUrl.trim() } : {}),\n })\n if (saved) setEditing(null)\n }}\n >\n {'Save tracking'}\n </Button>\n </Stack>\n </Stack>\n ) : null}\n </Stack>\n )\n })}\n </Stack>\n )\n}\n"],"names":["CommerceModel","FULFILLMENT_OTHER_CARRIER","fulfillItemsIsEverything","fulfillItemsSelection","fulfillItemsStartQuantities","fulfillmentCarrierChoice","Button","Checkbox","Chip","FormControlLabel","IconButton","InputAdornment","Link","Stack","TextField","Typography","useCallback","useMemo","useState","OTHER_CARRIER","currencySymbol","currency","part","Intl","NumberFormat","style","formatToParts","find","entry","type","value","consoleOrderZoneOrder","order","orderId","address","states","orderLineFulfillmentStates","shippingAddress","name","line1","line2","city","state","postalCode","country","phone","id","number","formatOrderNumber","status","channel","customerEmail","customerName","lines","lineItems","map","line","index","lineItemId","productId","variantId","variantLabel","sku","productType","quantity","unitAmountCents","fulfilledQuantity","remainingQuantity","requiresShipping","fulfillments","toZoneFulfillment","totals","itemsCents","shippingCents","taxCents","discountCents","totalCents","Number","amountCents","testMode","orderIsTestMode","fulfillmentMethod","localDelivery","zoneLocalDelivery","delivery","courier","undefined","orderLocalDeliveryStatus","windowStartMs","windowEndMs","windowLabel","provider","String","providerLabel","deliveryRef","trackingUrl","etaMs","reason","updatedAtMs","fulfillmentLineQuantities","carrier","trackingNumber","fulfillmentTrackingUrl","labelUrl","fulfillmentIsActive","atMs","carrierChoiceFor","CarrierFields","props","onChange","other","setOther","choice","derived","trackingUrlFor","spacing","direction","select","label","event","target","size","sx","width","slotProps","native","inputLabel","shrink","option","FULFILLMENT_CARRIER_CHOICES","flex","placeholder","helperText","FulfillItemsPanel","busy","onSubmit","onCancel","renderZone","toUpperCase","quantities","setQuantities","Object","fromEntries","tracking","setTracking","notify","setNotify","shippingCost","setShippingCost","handEntered","cost","parseShippingCostInput","costError","error","zoneKey","setZoneKey","selection","everything","setQuantity","max","current","Math","min","floor","isFinite","applyTracking","next","key","submit","trim","cents","shippingCostCents","border","borderColor","borderRadius","p","variant","alignItems","component","color","aria-label","disabled","onClick","htmlInput","patch","href","rel","Boolean","input","startAdornment","position","inputMode","control","checked","justifyContent","length","FulfillmentList","onUpdateTracking","onCancelFulfillment","editing","setEditing","editable","orderFulfillmentsEditable","active","url","carrierLabelFor","textDecoration","describeFulfillmentLines","Date","toLocaleString","saved"],"mappings":"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"}
|
|
@@ -19,7 +19,7 @@ import { useFirestore } from "@aglyn/tenant-feature-instance";
|
|
|
19
19
|
import { Button } from "@mui/material";
|
|
20
20
|
import { doc, getDoc } from "firebase/firestore";
|
|
21
21
|
import { useCallback, useState } from "react";
|
|
22
|
-
import { buildOrderInvoiceHtml } from "../../utils/order-invoice.js";
|
|
22
|
+
import { buildOrderInvoiceHtml, invoiceStoreFrom } from "../../utils/order-invoice.js";
|
|
23
23
|
/**
|
|
24
24
|
* "Invoice" (AGL-3611), beside "Packing slip" in the order dialog: the order
|
|
25
25
|
* as a printable bill, which the print dialog also saves as a PDF. The store's
|
|
@@ -36,22 +36,14 @@ import { buildOrderInvoiceHtml } from "../../utils/order-invoice.js";
|
|
|
36
36
|
if (!win) return;
|
|
37
37
|
setBusy(true);
|
|
38
38
|
try {
|
|
39
|
-
var _ref, _ref1, _ref2, _ref3, _hostData_businessName, _storeData_currency, _storeData_receiptFooter, _storeData_termsUrl;
|
|
40
39
|
const [host, store] = await Promise.all([
|
|
41
40
|
getDoc(doc(firestore, 'hosts', hostId)).catch(()=>null),
|
|
42
41
|
getDoc(doc(firestore, 'hosts', hostId, 'settings', 'store')).catch(()=>null)
|
|
43
42
|
]);
|
|
44
|
-
const hostData = (_ref = host == null ? void 0 : host.data()) != null ? _ref : {};
|
|
45
|
-
const storeData = (_ref1 = store == null ? void 0 : store.data()) != null ? _ref1 : {};
|
|
46
43
|
const html = buildOrderInvoiceHtml({
|
|
47
44
|
order,
|
|
48
45
|
orderId,
|
|
49
|
-
store:
|
|
50
|
-
name: String((_ref2 = (_ref3 = (_hostData_businessName = hostData['businessName']) != null ? _hostData_businessName : hostData['displayName']) != null ? _ref3 : hostData['name']) != null ? _ref2 : ''),
|
|
51
|
-
currency: String((_storeData_currency = storeData['currency']) != null ? _storeData_currency : 'USD'),
|
|
52
|
-
footer: (_storeData_receiptFooter = storeData['receiptFooter']) != null ? _storeData_receiptFooter : null,
|
|
53
|
-
termsUrl: (_storeData_termsUrl = storeData['termsUrl']) != null ? _storeData_termsUrl : null
|
|
54
|
-
}
|
|
46
|
+
store: invoiceStoreFrom(host == null ? void 0 : host.data(), store == null ? void 0 : store.data())
|
|
55
47
|
});
|
|
56
48
|
// Every value in `html` is escaped by the builder (AGL-2283).
|
|
57
49
|
win.document.write(html);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/order-invoice.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport { Button } from '@mui/material'\nimport { doc, getDoc } from 'firebase/firestore'\nimport { useCallback, useState } from 'react'\nimport type * as CommerceModel from '../../model'\nimport { buildOrderInvoiceHtml } from '../../utils/order-invoice'\n\nexport interface OrderInvoiceButtonProps {\n hostId: string\n orderId: string\n order: CommerceModel.HostOrder\n}\n\n/**\n * \"Invoice\" (AGL-3611), beside \"Packing slip\" in the order dialog: the order\n * as a printable bill, which the print dialog also saves as a PDF. The store's\n * name, currency and footer are read when it is clicked, not while the dialog\n * is open, so the dialog listens to nothing more.\n */\nexport function OrderInvoiceButton(props: OrderInvoiceButtonProps) {\n const { hostId, orderId, order } = props\n const firestore = useFirestore()\n const [busy, setBusy] = useState(false)\n\n const handleInvoice = useCallback(async () => {\n // Opened before the reads, inside the click, so a popup blocker sees the\n // user's gesture.\n const win = window.open('', '_blank', 'width=720,height=900')\n if (!win) return\n setBusy(true)\n try {\n const [host, store] = await Promise.all([\n getDoc(doc(firestore, 'hosts', hostId)).catch(() => null),\n getDoc(doc(firestore, 'hosts', hostId, 'settings', 'store')).catch(() => null),\n ])\n const
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/order-invoice.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport { Button } from '@mui/material'\nimport { doc, getDoc } from 'firebase/firestore'\nimport { useCallback, useState } from 'react'\nimport type * as CommerceModel from '../../model'\nimport { buildOrderInvoiceHtml, invoiceStoreFrom } from '../../utils/order-invoice'\n\nexport interface OrderInvoiceButtonProps {\n hostId: string\n orderId: string\n order: CommerceModel.HostOrder\n}\n\n/**\n * \"Invoice\" (AGL-3611), beside \"Packing slip\" in the order dialog: the order\n * as a printable bill, which the print dialog also saves as a PDF. The store's\n * name, currency and footer are read when it is clicked, not while the dialog\n * is open, so the dialog listens to nothing more.\n */\nexport function OrderInvoiceButton(props: OrderInvoiceButtonProps) {\n const { hostId, orderId, order } = props\n const firestore = useFirestore()\n const [busy, setBusy] = useState(false)\n\n const handleInvoice = useCallback(async () => {\n // Opened before the reads, inside the click, so a popup blocker sees the\n // user's gesture.\n const win = window.open('', '_blank', 'width=720,height=900')\n if (!win) return\n setBusy(true)\n try {\n const [host, store] = await Promise.all([\n getDoc(doc(firestore, 'hosts', hostId)).catch(() => null),\n getDoc(doc(firestore, 'hosts', hostId, 'settings', 'store')).catch(() => null),\n ])\n const html = buildOrderInvoiceHtml({\n order,\n orderId,\n store: invoiceStoreFrom(\n host?.data() as Record<string, unknown> | undefined,\n store?.data() as Record<string, unknown> | undefined,\n ),\n })\n // Every value in `html` is escaped by the builder (AGL-2283).\n win.document.write(html)\n win.document.close()\n // Print from the opener, not an injected script: the popup inherits\n // the console's CSP (AGL-523).\n win.focus()\n win.print()\n } finally {\n setBusy(false)\n }\n }, [firestore, hostId, orderId, order])\n\n return (\n <Button disabled={busy} onClick={handleInvoice}>\n {'Invoice'}\n </Button>\n )\n}\nOrderInvoiceButton.displayName = 'OrderInvoiceButton'\n\nexport default OrderInvoiceButton\n"],"names":["useFirestore","Button","doc","getDoc","useCallback","useState","buildOrderInvoiceHtml","invoiceStoreFrom","OrderInvoiceButton","props","hostId","orderId","order","firestore","busy","setBusy","handleInvoice","win","window","open","host","store","Promise","all","catch","html","data","document","write","close","focus","print","disabled","onClick","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;AAEA,SAASA,YAAY,QAAQ,iCAAgC;AAC7D,SAASC,MAAM,QAAQ,gBAAe;AACtC,SAASC,GAAG,EAAEC,MAAM,QAAQ,qBAAoB;AAChD,SAASC,WAAW,EAAEC,QAAQ,QAAQ,QAAO;AAE7C,SAASC,qBAAqB,EAAEC,gBAAgB,QAAQ,+BAA2B;AAQnF;;;;;CAKC,GACD,OAAO,SAASC,mBAAmBC,KAA8B;IAC/D,MAAM,EAAEC,MAAM,EAAEC,OAAO,EAAEC,KAAK,EAAE,GAAGH;IACnC,MAAMI,YAAYb;IAClB,MAAM,CAACc,MAAMC,QAAQ,GAAGV,SAAS;IAEjC,MAAMW,gBAAgBZ,YAAY;QAChC,yEAAyE;QACzE,kBAAkB;QAClB,MAAMa,MAAMC,OAAOC,IAAI,CAAC,IAAI,UAAU;QACtC,IAAI,CAACF,KAAK;QACVF,QAAQ;QACR,IAAI;YACF,MAAM,CAACK,MAAMC,MAAM,GAAG,MAAMC,QAAQC,GAAG,CAAC;gBACtCpB,OAAOD,IAAIW,WAAW,SAASH,SAASc,KAAK,CAAC,IAAM;gBACpDrB,OAAOD,IAAIW,WAAW,SAASH,QAAQ,YAAY,UAAUc,KAAK,CAAC,IAAM;aAC1E;YACD,MAAMC,OAAOnB,sBAAsB;gBACjCM;gBACAD;gBACAU,OAAOd,iBACLa,wBAAAA,KAAMM,IAAI,IACVL,yBAAAA,MAAOK,IAAI;YAEf;YACA,8DAA8D;YAC9DT,IAAIU,QAAQ,CAACC,KAAK,CAACH;YACnBR,IAAIU,QAAQ,CAACE,KAAK;YAClB,oEAAoE;YACpE,+BAA+B;YAC/BZ,IAAIa,KAAK;YACTb,IAAIc,KAAK;QACX,SAAU;YACRhB,QAAQ;QACV;IACF,GAAG;QAACF;QAAWH;QAAQC;QAASC;KAAM;IAEtC,qBACE,KAACX;QAAO+B,UAAUlB;QAAMmB,SAASjB;kBAC9B;;AAGP;AACAR,mBAAmB0B,WAAW,GAAG;AAEjC,eAAe1B,mBAAkB"}
|
|
@@ -21,21 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
export declare const ORDERS_TO_SHIP_FILTER: {
|
|
23
23
|
readonly label: "orders still to ship";
|
|
24
|
-
readonly value:
|
|
25
|
-
readonly filters: readonly [{
|
|
26
|
-
readonly path: "requiresShipping";
|
|
27
|
-
readonly op: "==";
|
|
28
|
-
readonly value: true;
|
|
29
|
-
}, {
|
|
30
|
-
readonly path: "status";
|
|
31
|
-
readonly op: "in";
|
|
32
|
-
readonly value: readonly ["paid", "partially_fulfilled"];
|
|
33
|
-
}];
|
|
34
|
-
readonly orderBy: {
|
|
35
|
-
readonly path: "createdAtMs";
|
|
36
|
-
readonly direction: "desc";
|
|
37
|
-
};
|
|
38
|
-
};
|
|
24
|
+
readonly value: unknown;
|
|
39
25
|
};
|
|
40
26
|
export interface OrderShippingActionsProps {
|
|
41
27
|
hostId: string;
|
|
@@ -20,35 +20,15 @@ import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
|
|
|
20
20
|
import { useUser } from "@aglyn/tenant-feature-instance";
|
|
21
21
|
import { Button, ListItemText, Menu, MenuItem, Stack } from "@mui/material";
|
|
22
22
|
import { useState } from "react";
|
|
23
|
-
import { ORDER_SHIPPING_PRESETS } from "../../transfer/shipping-presets.js";
|
|
23
|
+
import { ORDER_SHIPPING_PRESETS, ORDERS_TO_SHIP_FILTER_LABEL, ORDERS_TO_SHIP_FILTER_VALUE } from "../../transfer/shipping-presets.js";
|
|
24
24
|
import { COMMERCE_ORDERS_TRANSFER, COMMERCE_TRACKING_TRANSFER } from "../../transfer/transfer-keys.js";
|
|
25
25
|
/**
|
|
26
26
|
* The orders still to ship, as the export dialog's filter: orders that ship
|
|
27
27
|
* at all (`requiresShipping`, AGL-3613) that are paid or part-shipped —
|
|
28
28
|
* one indexed query, `(requiresShipping, status, createdAtMs)`.
|
|
29
29
|
*/ export const ORDERS_TO_SHIP_FILTER = {
|
|
30
|
-
label:
|
|
31
|
-
value:
|
|
32
|
-
filters: [
|
|
33
|
-
{
|
|
34
|
-
path: 'requiresShipping',
|
|
35
|
-
op: '==',
|
|
36
|
-
value: true
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
path: 'status',
|
|
40
|
-
op: 'in',
|
|
41
|
-
value: [
|
|
42
|
-
'paid',
|
|
43
|
-
'partially_fulfilled'
|
|
44
|
-
]
|
|
45
|
-
}
|
|
46
|
-
],
|
|
47
|
-
orderBy: {
|
|
48
|
-
path: 'createdAtMs',
|
|
49
|
-
direction: 'desc'
|
|
50
|
-
}
|
|
51
|
-
}
|
|
30
|
+
label: ORDERS_TO_SHIP_FILTER_LABEL,
|
|
31
|
+
value: ORDERS_TO_SHIP_FILTER_VALUE
|
|
52
32
|
};
|
|
53
33
|
/**
|
|
54
34
|
* The orders card's shipping actions, for its header (AGL-3613): Export for
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/order-shipping-actions.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport { useTransferLauncher } from '@aglyn/aglyn/app-utils/transfer-launcher-context'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { Button, ListItemText, Menu, MenuItem, Stack } from '@mui/material'\nimport { useState } from 'react'\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/order-shipping-actions.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport { useTransferLauncher } from '@aglyn/aglyn/app-utils/transfer-launcher-context'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { Button, ListItemText, Menu, MenuItem, Stack } from '@mui/material'\nimport { useState } from 'react'\nimport {\n ORDER_SHIPPING_PRESETS,\n ORDERS_TO_SHIP_FILTER_LABEL,\n ORDERS_TO_SHIP_FILTER_VALUE,\n} from '../../transfer/shipping-presets'\nimport { COMMERCE_ORDERS_TRANSFER, COMMERCE_TRACKING_TRANSFER } from '../../transfer/transfer-keys'\n\n/**\n * The orders still to ship, as the export dialog's filter: orders that ship\n * at all (`requiresShipping`, AGL-3613) that are paid or part-shipped —\n * one indexed query, `(requiresShipping, status, createdAtMs)`.\n */\nexport const ORDERS_TO_SHIP_FILTER = {\n label: ORDERS_TO_SHIP_FILTER_LABEL,\n value: ORDERS_TO_SHIP_FILTER_VALUE,\n} as const\n\nexport interface OrderShippingActionsProps {\n hostId: string\n}\n\n/**\n * The orders card's shipping actions, for its header (AGL-3613): Export for\n * shipping — the orders still to ship, laid out for Pirate Ship, Shippo,\n * EasyPost or any tool — and Import tracking, which records each parcel in a\n * label tool's file as a shipment. Each shows only when the transfer\n * launcher's `can` admits the person; off the console shell, nothing.\n */\nexport function OrderShippingActions(props: OrderShippingActionsProps) {\n const { hostId } = props\n const transfer = useTransferLauncher()\n const { data: user } = useUser()\n const [anchor, setAnchor] = useState<HTMLElement | null>(null)\n if (!transfer) return null\n const canExport = transfer.can('export', { resource: COMMERCE_ORDERS_TRANSFER, scope: 'host', hostId })\n const canImport = transfer.can('import', { resource: COMMERCE_TRACKING_TRANSFER, scope: 'host', hostId })\n if (!canExport && !canImport) return null\n\n const exportFor = async (preset: string) => {\n setAnchor(null)\n // Orders written before AGL-3613 carry no `requiresShipping`; stamping\n // the open ones first is what lets the query find them. Best effort: the\n // export still opens, and shows what is already stamped.\n try {\n await authorizedFetch(user as never, '/api/commerce/orders-shipping-prepare', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ hostId }),\n })\n } catch {\n // The export below is still right for every order written since.\n }\n transfer.openExport({\n resource: COMMERCE_ORDERS_TRANSFER,\n scope: 'host',\n hostId,\n title: 'Export orders for shipping',\n preset,\n filter: { label: ORDERS_TO_SHIP_FILTER.label, value: ORDERS_TO_SHIP_FILTER.value },\n })\n }\n\n return (\n <Stack direction=\"row\" spacing={1}>\n {canImport ? (\n <Button\n size=\"small\"\n onClick={() =>\n transfer.openImport({\n resource: COMMERCE_TRACKING_TRANSFER,\n scope: 'host',\n hostId,\n title: 'Import tracking numbers',\n })\n }\n >\n {'Import tracking'}\n </Button>\n ) : null}\n {canExport ? (\n <>\n <Button\n size=\"small\"\n aria-haspopup=\"menu\"\n aria-expanded={anchor ? 'true' : undefined}\n onClick={(event) => setAnchor(event.currentTarget)}\n >\n {'Export for shipping'}\n </Button>\n <Menu anchorEl={anchor} open={Boolean(anchor)} onClose={() => setAnchor(null)}>\n {ORDER_SHIPPING_PRESETS.map((preset) => (\n <MenuItem key={preset.id} onClick={() => void exportFor(preset.id)}>\n <ListItemText primary={preset.label} secondary={preset.description} />\n </MenuItem>\n ))}\n </Menu>\n </>\n ) : null}\n </Stack>\n )\n}\nOrderShippingActions.displayName = 'OrderShippingActions'\n\nexport default OrderShippingActions\n"],"names":["useTransferLauncher","authorizedFetch","useUser","Button","ListItemText","Menu","MenuItem","Stack","useState","ORDER_SHIPPING_PRESETS","ORDERS_TO_SHIP_FILTER_LABEL","ORDERS_TO_SHIP_FILTER_VALUE","COMMERCE_ORDERS_TRANSFER","COMMERCE_TRACKING_TRANSFER","ORDERS_TO_SHIP_FILTER","label","value","OrderShippingActions","props","hostId","transfer","data","user","anchor","setAnchor","canExport","can","resource","scope","canImport","exportFor","preset","method","headers","body","JSON","stringify","openExport","title","filter","direction","spacing","size","onClick","openImport","aria-haspopup","aria-expanded","undefined","event","currentTarget","anchorEl","open","Boolean","onClose","map","id","primary","secondary","description","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;AAEA,SAASA,mBAAmB,QAAQ,mDAAkD;AACtF,SAASC,eAAe,QAAQ,2CAA0C;AAC1E,SAASC,OAAO,QAAQ,iCAAgC;AACxD,SAASC,MAAM,EAAEC,YAAY,EAAEC,IAAI,EAAEC,QAAQ,EAAEC,KAAK,QAAQ,gBAAe;AAC3E,SAASC,QAAQ,QAAQ,QAAO;AAChC,SACEC,sBAAsB,EACtBC,2BAA2B,EAC3BC,2BAA2B,QACtB,qCAAiC;AACxC,SAASC,wBAAwB,EAAEC,0BAA0B,QAAQ,kCAA8B;AAEnG;;;;CAIC,GACD,OAAO,MAAMC,wBAAwB;IACnCC,OAAOL;IACPM,OAAOL;AACT,EAAU;AAMV;;;;;;CAMC,GACD,OAAO,SAASM,qBAAqBC,KAAgC;IACnE,MAAM,EAAEC,MAAM,EAAE,GAAGD;IACnB,MAAME,WAAWpB;IACjB,MAAM,EAAEqB,MAAMC,IAAI,EAAE,GAAGpB;IACvB,MAAM,CAACqB,QAAQC,UAAU,GAAGhB,SAA6B;IACzD,IAAI,CAACY,UAAU,OAAO;IACtB,MAAMK,YAAYL,SAASM,GAAG,CAAC,UAAU;QAAEC,UAAUf;QAA0BgB,OAAO;QAAQT;IAAO;IACrG,MAAMU,YAAYT,SAASM,GAAG,CAAC,UAAU;QAAEC,UAAUd;QAA4Be,OAAO;QAAQT;IAAO;IACvG,IAAI,CAACM,aAAa,CAACI,WAAW,OAAO;IAErC,MAAMC,YAAY,OAAOC;QACvBP,UAAU;QACV,uEAAuE;QACvE,yEAAyE;QACzE,yDAAyD;QACzD,IAAI;YACF,MAAMvB,gBAAgBqB,MAAe,yCAAyC;gBAC5EU,QAAQ;gBACRC,SAAS;oBAAE,gBAAgB;gBAAmB;gBAC9CC,MAAMC,KAAKC,SAAS,CAAC;oBAAEjB;gBAAO;YAChC;QACF,EAAE,eAAM;QACN,iEAAiE;QACnE;QACAC,SAASiB,UAAU,CAAC;YAClBV,UAAUf;YACVgB,OAAO;YACPT;YACAmB,OAAO;YACPP;YACAQ,QAAQ;gBAAExB,OAAOD,sBAAsBC,KAAK;gBAAEC,OAAOF,sBAAsBE,KAAK;YAAC;QACnF;IACF;IAEA,qBACE,MAACT;QAAMiC,WAAU;QAAMC,SAAS;;YAC7BZ,0BACC,KAAC1B;gBACCuC,MAAK;gBACLC,SAAS,IACPvB,SAASwB,UAAU,CAAC;wBAClBjB,UAAUd;wBACVe,OAAO;wBACPT;wBACAmB,OAAO;oBACT;0BAGD;iBAED;YACHb,0BACC;;kCACE,KAACtB;wBACCuC,MAAK;wBACLG,iBAAc;wBACdC,iBAAevB,SAAS,SAASwB;wBACjCJ,SAAS,CAACK,QAAUxB,UAAUwB,MAAMC,aAAa;kCAEhD;;kCAEH,KAAC5C;wBAAK6C,UAAU3B;wBAAQ4B,MAAMC,QAAQ7B;wBAAS8B,SAAS,IAAM7B,UAAU;kCACrEf,uBAAuB6C,GAAG,CAAC,CAACvB,uBAC3B,KAACzB;gCAAyBqC,SAAS,IAAM,KAAKb,UAAUC,OAAOwB,EAAE;0CAC/D,cAAA,KAACnD;oCAAaoD,SAASzB,OAAOhB,KAAK;oCAAE0C,WAAW1B,OAAO2B,WAAW;;+BADrD3B,OAAOwB,EAAE;;;iBAM5B;;;AAGV;AACAtC,qBAAqB0C,WAAW,GAAG;AAEnC,eAAe1C,qBAAoB"}
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import * as CommerceModel from '../../model';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
export declare function productLabelRows(product: CommerceModel.HostProduct): Array<ProductLabel & {
|
|
5
|
-
variantId: string;
|
|
6
|
-
}>;
|
|
2
|
+
import { productLabelRows } from '../../printing/product-labels';
|
|
3
|
+
export { productLabelRows };
|
|
7
4
|
export declare function ProductLabelsDialog(props: {
|
|
8
5
|
product: (CommerceModel.HostProduct & {
|
|
9
6
|
$id: string;
|
|
@@ -19,7 +19,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
19
19
|
import { Button, Dialog, DialogActions, DialogContent, DialogTitle, MenuItem, Stack, TextField, Typography } from "@mui/material";
|
|
20
20
|
import { useSnackbar } from "@aglyn/shared-ui-snackstack";
|
|
21
21
|
import { useEffect, useMemo, useState } from "react";
|
|
22
|
-
import { LABEL_SIZES, MAX_LABEL_COPIES, productLabelsHtml, productLabelsZpl } from "../../printing/product-labels.js";
|
|
22
|
+
import { LABEL_SIZES, MAX_LABEL_COPIES, labelCopiesFromInput, labelCopiesTotal, labelsZplFileName, printableLabelRows, productLabelRows, productLabelsHtml, productLabelsZpl } from "../../printing/product-labels.js";
|
|
23
23
|
/**
|
|
24
24
|
* Print product labels (AGL-3619): a label per variant with its name,
|
|
25
25
|
* options, price and a barcode the register scans back, on a thermal label
|
|
@@ -31,24 +31,7 @@ import { LABEL_SIZES, MAX_LABEL_COPIES, productLabelsHtml, productLabelsZpl } fr
|
|
|
31
31
|
max: MAX_LABEL_COPIES
|
|
32
32
|
}
|
|
33
33
|
};
|
|
34
|
-
|
|
35
|
-
return product.variants.map((variant)=>{
|
|
36
|
-
var _variant_options;
|
|
37
|
-
const options = Object.values((_variant_options = variant.options) != null ? _variant_options : {}).filter(Boolean);
|
|
38
|
-
return _extends({
|
|
39
|
-
variantId: variant.id,
|
|
40
|
-
name: product.name
|
|
41
|
-
}, options.length ? {
|
|
42
|
-
detail: options.join(' / ')
|
|
43
|
-
} : {}, Number.isFinite(Number(variant.priceUsd)) ? {
|
|
44
|
-
priceCents: Math.round(Number(variant.priceUsd) * 100)
|
|
45
|
-
} : {}, {
|
|
46
|
-
currency: 'usd',
|
|
47
|
-
code: String(variant.barcode || variant.sku || '').trim(),
|
|
48
|
-
copies: 1
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
}
|
|
34
|
+
export { productLabelRows };
|
|
52
35
|
export function ProductLabelsDialog(props) {
|
|
53
36
|
var _ref;
|
|
54
37
|
const { product, onClose } = props;
|
|
@@ -61,10 +44,10 @@ export function ProductLabelsDialog(props) {
|
|
|
61
44
|
useEffect(()=>setRows(initial), [
|
|
62
45
|
initial
|
|
63
46
|
]);
|
|
64
|
-
const printable = rows
|
|
65
|
-
const total =
|
|
47
|
+
const printable = printableLabelRows(rows);
|
|
48
|
+
const total = labelCopiesTotal(rows);
|
|
66
49
|
const setCopies = (variantId, value)=>setRows((current)=>current.map((row)=>row.variantId === variantId ? _extends({}, row, {
|
|
67
|
-
copies:
|
|
50
|
+
copies: labelCopiesFromInput(value)
|
|
68
51
|
}) : row));
|
|
69
52
|
const print = ()=>{
|
|
70
53
|
const win = window.open('', '_blank', 'width=480,height=640');
|
|
@@ -81,7 +64,6 @@ export function ProductLabelsDialog(props) {
|
|
|
81
64
|
win.print();
|
|
82
65
|
};
|
|
83
66
|
const downloadZpl = ()=>{
|
|
84
|
-
var _ref;
|
|
85
67
|
const blob = new Blob([
|
|
86
68
|
productLabelsZpl(printable, sizeId)
|
|
87
69
|
], {
|
|
@@ -90,7 +72,7 @@ export function ProductLabelsDialog(props) {
|
|
|
90
72
|
const url = URL.createObjectURL(blob);
|
|
91
73
|
const link = document.createElement('a');
|
|
92
74
|
link.href = url;
|
|
93
|
-
link.download =
|
|
75
|
+
link.download = labelsZplFileName(product == null ? void 0 : product.name);
|
|
94
76
|
link.click();
|
|
95
77
|
setTimeout(()=>URL.revokeObjectURL(url), 1000);
|
|
96
78
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/product-labels-dialog.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport * as CommerceModel from '../../model'\nimport {\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n MenuItem,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { useEffect, useMemo, useState } from 'react'\nimport {\n LABEL_SIZES,\n MAX_LABEL_COPIES,\n productLabelsHtml,\n productLabelsZpl,\n type ProductLabel,\n} from '../../printing/product-labels'\n\n/**\n * Print product labels (AGL-3619): a label per variant with its name,\n * options, price and a barcode the register scans back, on a thermal label\n * printer through the browser, or as ZPL for a Zebra.\n */\n\nconst COPIES_INPUT = { htmlInput: { inputMode: 'numeric', min: 0, max: MAX_LABEL_COPIES } } as const\n\n/** The labels a product prints: one row per variant that has a barcode or SKU. */\nexport function productLabelRows(product: CommerceModel.HostProduct): Array<ProductLabel & { variantId: string }> {\n return product.variants.map((variant) => {\n const options = Object.values(variant.options ?? {}).filter(Boolean)\n return {\n variantId: variant.id,\n name: product.name,\n ...(options.length ? { detail: options.join(' / ') } : {}),\n ...(Number.isFinite(Number(variant.priceUsd))\n ? { priceCents: Math.round(Number(variant.priceUsd) * 100) }\n : {}),\n currency: 'usd',\n code: String(variant.barcode || variant.sku || '').trim(),\n copies: 1,\n }\n })\n}\n\nexport function ProductLabelsDialog(props: {\n product: (CommerceModel.HostProduct & { $id: string }) | null\n onClose: () => void\n}) {\n const { product, onClose } = props\n const { enqueueSnackbar } = useSnackbar()\n const initial = useMemo(() => (product ? productLabelRows(product) : []), [product])\n const [rows, setRows] = useState(initial)\n const [sizeId, setSizeId] = useState(LABEL_SIZES[0].id)\n useEffect(() => setRows(initial), [initial])\n const printable = rows.filter((row) => row.code && row.copies > 0)\n const total = printable.reduce((sum, row) => sum + row.copies, 0)\n\n const setCopies = (variantId: string, value: string) =>\n setRows((current) =>\n current.map((row) =>\n row.variantId === variantId\n ? { ...row, copies: Math.max(0, Math.min(MAX_LABEL_COPIES, Math.floor(Number(value) || 0))) }\n : row,\n ),\n )\n\n const print = () => {\n const win = window.open('', '_blank', 'width=480,height=640')\n if (!win) {\n return void enqueueSnackbar('Allow pop-ups for this site to print labels', {\n variant: 'warning',\n persist: false,\n })\n }\n // Every merchant-authored value on the page is escaped by the renderer.\n win.document.write(productLabelsHtml(printable, sizeId))\n win.document.close()\n win.focus()\n win.print()\n }\n\n const downloadZpl = () => {\n const blob = new Blob([productLabelsZpl(printable, sizeId)], { type: 'text/plain' })\n const url = URL.createObjectURL(blob)\n const link = document.createElement('a')\n link.href = url\n link.download = `labels-${(product?.name ?? 'product').replace(/[^A-Za-z0-9]+/g, '-').toLowerCase()}.zpl`\n link.click()\n setTimeout(() => URL.revokeObjectURL(url), 1000)\n }\n\n return (\n <Dialog open={Boolean(product)} onClose={onClose} fullWidth maxWidth=\"sm\">\n <DialogTitle>{`Labels — ${product?.name ?? ''}`}</DialogTitle>\n <DialogContent>\n <Stack spacing={2} sx={{ pt: 1 }}>\n <TextField select label=\"Label size\" value={sizeId} onChange={(event) => setSizeId(event.target.value)}>\n {LABEL_SIZES.map((size) => (\n <MenuItem key={size.id} value={size.id}>\n {size.label}\n </MenuItem>\n ))}\n </TextField>\n {rows.map((row) => (\n <Stack key={row.variantId} direction=\"row\" spacing={2} sx={{ alignItems: 'center' }}>\n <Stack sx={{ flex: 1, minWidth: 0 }}>\n <Typography variant=\"body2\" noWrap>\n {row.detail ?? row.name}\n </Typography>\n <Typography variant=\"caption\" color={row.code ? 'text.secondary' : 'warning.main'} noWrap>\n {row.code || 'Add a barcode or SKU to print a label'}\n </Typography>\n </Stack>\n <TextField\n label=\"Copies\"\n type=\"number\"\n size=\"small\"\n value={row.code ? row.copies : 0}\n disabled={!row.code}\n onChange={(event) => setCopies(row.variantId, event.target.value)}\n slotProps={COPIES_INPUT}\n sx={{ width: 96 }}\n />\n </Stack>\n ))}\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Print to a label printer at 100% scale, or download ZPL to send straight to a Zebra.'}\n </Typography>\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose}>{'Cancel'}</Button>\n <Button disabled={!total} onClick={downloadZpl}>\n {'Download ZPL'}\n </Button>\n <Button disabled={!total} onClick={print}>\n {total === 1 ? 'Print 1 label' : `Print ${total} labels`}\n </Button>\n </DialogActions>\n </Dialog>\n )\n}\n\nexport default ProductLabelsDialog\n"],"names":["Button","Dialog","DialogActions","DialogContent","DialogTitle","MenuItem","Stack","TextField","Typography","useSnackbar","useEffect","useMemo","useState","LABEL_SIZES","MAX_LABEL_COPIES","productLabelsHtml","productLabelsZpl","COPIES_INPUT","htmlInput","inputMode","min","max","productLabelRows","product","variants","map","variant","options","Object","values","filter","Boolean","variantId","id","name","length","detail","join","Number","isFinite","priceUsd","priceCents","Math","round","currency","code","String","barcode","sku","trim","copies","ProductLabelsDialog","props","onClose","enqueueSnackbar","initial","rows","setRows","sizeId","setSizeId","printable","row","total","reduce","sum","setCopies","value","current","floor","print","win","window","open","persist","document","write","close","focus","downloadZpl","blob","Blob","type","url","URL","createObjectURL","link","createElement","href","download","replace","toLowerCase","click","setTimeout","revokeObjectURL","fullWidth","maxWidth","spacing","sx","pt","select","label","onChange","event","target","size","direction","alignItems","flex","minWidth","noWrap","color","disabled","slotProps","width","onClick"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;;AAGA,SACEA,MAAM,EACNC,MAAM,EACNC,aAAa,EACbC,aAAa,EACbC,WAAW,EACXC,QAAQ,EACRC,KAAK,EACLC,SAAS,EACTC,UAAU,QACL,gBAAe;AACtB,SAASC,WAAW,QAAQ,8BAA6B;AACzD,SAASC,SAAS,EAAEC,OAAO,EAAEC,QAAQ,QAAQ,QAAO;AACpD,SACEC,WAAW,EACXC,gBAAgB,EAChBC,iBAAiB,EACjBC,gBAAgB,QAEX,mCAA+B;AAEtC;;;;CAIC,GAED,MAAMC,eAAe;IAAEC,WAAW;QAAEC,WAAW;QAAWC,KAAK;QAAGC,KAAKP;IAAiB;AAAE;AAE1F,gFAAgF,GAChF,OAAO,SAASQ,iBAAiBC,OAAkC;IACjE,OAAOA,QAAQC,QAAQ,CAACC,GAAG,CAAC,CAACC;YACGA;QAA9B,MAAMC,UAAUC,OAAOC,MAAM,EAACH,mBAAAA,QAAQC,OAAO,YAAfD,mBAAmB,CAAC,GAAGI,MAAM,CAACC;QAC5D,OAAO;YACLC,WAAWN,QAAQO,EAAE;YACrBC,MAAMX,QAAQW,IAAI;WACdP,QAAQQ,MAAM,GAAG;YAAEC,QAAQT,QAAQU,IAAI,CAAC;QAAO,IAAI,CAAC,GACpDC,OAAOC,QAAQ,CAACD,OAAOZ,QAAQc,QAAQ,KACvC;YAAEC,YAAYC,KAAKC,KAAK,CAACL,OAAOZ,QAAQc,QAAQ,IAAI;QAAK,IACzD,CAAC;YACLI,UAAU;YACVC,MAAMC,OAAOpB,QAAQqB,OAAO,IAAIrB,QAAQsB,GAAG,IAAI,IAAIC,IAAI;YACvDC,QAAQ;;IAEZ;AACF;AAEA,OAAO,SAASC,oBAAoBC,KAGnC;;IACC,MAAM,EAAE7B,OAAO,EAAE8B,OAAO,EAAE,GAAGD;IAC7B,MAAM,EAAEE,eAAe,EAAE,GAAG7C;IAC5B,MAAM8C,UAAU5C,QAAQ,IAAOY,UAAUD,iBAAiBC,WAAW,EAAE,EAAG;QAACA;KAAQ;IACnF,MAAM,CAACiC,MAAMC,QAAQ,GAAG7C,SAAS2C;IACjC,MAAM,CAACG,QAAQC,UAAU,GAAG/C,SAASC,WAAW,CAAC,EAAE,CAACoB,EAAE;IACtDvB,UAAU,IAAM+C,QAAQF,UAAU;QAACA;KAAQ;IAC3C,MAAMK,YAAYJ,KAAK1B,MAAM,CAAC,CAAC+B,MAAQA,IAAIhB,IAAI,IAAIgB,IAAIX,MAAM,GAAG;IAChE,MAAMY,QAAQF,UAAUG,MAAM,CAAC,CAACC,KAAKH,MAAQG,MAAMH,IAAIX,MAAM,EAAE;IAE/D,MAAMe,YAAY,CAACjC,WAAmBkC,QACpCT,QAAQ,CAACU,UACPA,QAAQ1C,GAAG,CAAC,CAACoC,MACXA,IAAI7B,SAAS,KAAKA,YACd,aAAK6B;oBAAKX,QAAQR,KAAKrB,GAAG,CAAC,GAAGqB,KAAKtB,GAAG,CAACN,kBAAkB4B,KAAK0B,KAAK,CAAC9B,OAAO4B,UAAU;qBACrFL;IAIV,MAAMQ,QAAQ;QACZ,MAAMC,MAAMC,OAAOC,IAAI,CAAC,IAAI,UAAU;QACtC,IAAI,CAACF,KAAK;YACR,OAAO,KAAKhB,gBAAgB,+CAA+C;gBACzE5B,SAAS;gBACT+C,SAAS;YACX;QACF;QACA,wEAAwE;QACxEH,IAAII,QAAQ,CAACC,KAAK,CAAC5D,kBAAkB6C,WAAWF;QAChDY,IAAII,QAAQ,CAACE,KAAK;QAClBN,IAAIO,KAAK;QACTP,IAAID,KAAK;IACX;IAEA,MAAMS,cAAc;;QAClB,MAAMC,OAAO,IAAIC,KAAK;YAAChE,iBAAiB4C,WAAWF;SAAQ,EAAE;YAAEuB,MAAM;QAAa;QAClF,MAAMC,MAAMC,IAAIC,eAAe,CAACL;QAChC,MAAMM,OAAOX,SAASY,aAAa,CAAC;QACpCD,KAAKE,IAAI,GAAGL;QACZG,KAAKG,QAAQ,GAAG,CAAC,OAAO,EAAE,SAACjE,2BAAAA,QAASW,IAAI,mBAAI,WAAWuD,OAAO,CAAC,kBAAkB,KAAKC,WAAW,GAAG,IAAI,CAAC;QACzGL,KAAKM,KAAK;QACVC,WAAW,IAAMT,IAAIU,eAAe,CAACX,MAAM;IAC7C;IAEA,qBACE,MAACjF;QAAOuE,MAAMzC,QAAQR;QAAU8B,SAASA;QAASyC,SAAS;QAACC,UAAS;;0BACnE,KAAC3F;0BAAa,CAAC,SAAS,UAAEmB,2BAAAA,QAASW,IAAI,mBAAI,IAAI;;0BAC/C,KAAC/B;0BACC,cAAA,MAACG;oBAAM0F,SAAS;oBAAGC,IAAI;wBAAEC,IAAI;oBAAE;;sCAC7B,KAAC3F;4BAAU4F,MAAM;4BAACC,OAAM;4BAAalC,OAAOR;4BAAQ2C,UAAU,CAACC,QAAU3C,UAAU2C,MAAMC,MAAM,CAACrC,KAAK;sCAClGrD,YAAYY,GAAG,CAAC,CAAC+E,qBAChB,KAACnG;oCAAuB6D,OAAOsC,KAAKvE,EAAE;8CACnCuE,KAAKJ,KAAK;mCADEI,KAAKvE,EAAE;;wBAKzBuB,KAAK/B,GAAG,CAAC,CAACoC;gCAIFA;iDAHP,MAACvD;gCAA0BmG,WAAU;gCAAMT,SAAS;gCAAGC,IAAI;oCAAES,YAAY;gCAAS;;kDAChF,MAACpG;wCAAM2F,IAAI;4CAAEU,MAAM;4CAAGC,UAAU;wCAAE;;0DAChC,KAACpG;gDAAWkB,SAAQ;gDAAQmF,MAAM;2DAC/BhD,cAAAA,IAAIzB,MAAM,YAAVyB,cAAcA,IAAI3B,IAAI;;0DAEzB,KAAC1B;gDAAWkB,SAAQ;gDAAUoF,OAAOjD,IAAIhB,IAAI,GAAG,mBAAmB;gDAAgBgE,MAAM;0DACtFhD,IAAIhB,IAAI,IAAI;;;;kDAGjB,KAACtC;wCACC6F,OAAM;wCACNnB,MAAK;wCACLuB,MAAK;wCACLtC,OAAOL,IAAIhB,IAAI,GAAGgB,IAAIX,MAAM,GAAG;wCAC/B6D,UAAU,CAAClD,IAAIhB,IAAI;wCACnBwD,UAAU,CAACC,QAAUrC,UAAUJ,IAAI7B,SAAS,EAAEsE,MAAMC,MAAM,CAACrC,KAAK;wCAChE8C,WAAW/F;wCACXgF,IAAI;4CAAEgB,OAAO;wCAAG;;;+BAjBRpD,IAAI7B,SAAS;;sCAqB3B,KAACxB;4BAAWkB,SAAQ;4BAAQoF,OAAM;sCAC/B;;;;;0BAIP,MAAC5G;;kCACC,KAACF;wBAAOkH,SAAS7D;kCAAU;;kCAC3B,KAACrD;wBAAO+G,UAAU,CAACjD;wBAAOoD,SAASpC;kCAChC;;kCAEH,KAAC9E;wBAAO+G,UAAU,CAACjD;wBAAOoD,SAAS7C;kCAChCP,UAAU,IAAI,kBAAkB,CAAC,MAAM,EAAEA,MAAM,OAAO,CAAC;;;;;;AAKlE;AAEA,eAAeX,oBAAmB"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/product-labels-dialog.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport * as CommerceModel from '../../model'\nimport {\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n MenuItem,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { useEffect, useMemo, useState } from 'react'\nimport {\n LABEL_SIZES,\n MAX_LABEL_COPIES,\n labelCopiesFromInput,\n labelCopiesTotal,\n labelsZplFileName,\n printableLabelRows,\n productLabelRows,\n productLabelsHtml,\n productLabelsZpl,\n} from '../../printing/product-labels'\n\n/**\n * Print product labels (AGL-3619): a label per variant with its name,\n * options, price and a barcode the register scans back, on a thermal label\n * printer through the browser, or as ZPL for a Zebra.\n */\n\nconst COPIES_INPUT = { htmlInput: { inputMode: 'numeric', min: 0, max: MAX_LABEL_COPIES } } as const\n\nexport { productLabelRows }\n\nexport function ProductLabelsDialog(props: {\n product: (CommerceModel.HostProduct & { $id: string }) | null\n onClose: () => void\n}) {\n const { product, onClose } = props\n const { enqueueSnackbar } = useSnackbar()\n const initial = useMemo(() => (product ? productLabelRows(product) : []), [product])\n const [rows, setRows] = useState(initial)\n const [sizeId, setSizeId] = useState(LABEL_SIZES[0].id)\n useEffect(() => setRows(initial), [initial])\n const printable = printableLabelRows(rows)\n const total = labelCopiesTotal(rows)\n\n const setCopies = (variantId: string, value: string) =>\n setRows((current) =>\n current.map((row) =>\n row.variantId === variantId\n ? { ...row, copies: labelCopiesFromInput(value) }\n : row,\n ),\n )\n\n const print = () => {\n const win = window.open('', '_blank', 'width=480,height=640')\n if (!win) {\n return void enqueueSnackbar('Allow pop-ups for this site to print labels', {\n variant: 'warning',\n persist: false,\n })\n }\n // Every merchant-authored value on the page is escaped by the renderer.\n win.document.write(productLabelsHtml(printable, sizeId))\n win.document.close()\n win.focus()\n win.print()\n }\n\n const downloadZpl = () => {\n const blob = new Blob([productLabelsZpl(printable, sizeId)], { type: 'text/plain' })\n const url = URL.createObjectURL(blob)\n const link = document.createElement('a')\n link.href = url\n link.download = labelsZplFileName(product?.name)\n link.click()\n setTimeout(() => URL.revokeObjectURL(url), 1000)\n }\n\n return (\n <Dialog open={Boolean(product)} onClose={onClose} fullWidth maxWidth=\"sm\">\n <DialogTitle>{`Labels — ${product?.name ?? ''}`}</DialogTitle>\n <DialogContent>\n <Stack spacing={2} sx={{ pt: 1 }}>\n <TextField select label=\"Label size\" value={sizeId} onChange={(event) => setSizeId(event.target.value)}>\n {LABEL_SIZES.map((size) => (\n <MenuItem key={size.id} value={size.id}>\n {size.label}\n </MenuItem>\n ))}\n </TextField>\n {rows.map((row) => (\n <Stack key={row.variantId} direction=\"row\" spacing={2} sx={{ alignItems: 'center' }}>\n <Stack sx={{ flex: 1, minWidth: 0 }}>\n <Typography variant=\"body2\" noWrap>\n {row.detail ?? row.name}\n </Typography>\n <Typography variant=\"caption\" color={row.code ? 'text.secondary' : 'warning.main'} noWrap>\n {row.code || 'Add a barcode or SKU to print a label'}\n </Typography>\n </Stack>\n <TextField\n label=\"Copies\"\n type=\"number\"\n size=\"small\"\n value={row.code ? row.copies : 0}\n disabled={!row.code}\n onChange={(event) => setCopies(row.variantId, event.target.value)}\n slotProps={COPIES_INPUT}\n sx={{ width: 96 }}\n />\n </Stack>\n ))}\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Print to a label printer at 100% scale, or download ZPL to send straight to a Zebra.'}\n </Typography>\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose}>{'Cancel'}</Button>\n <Button disabled={!total} onClick={downloadZpl}>\n {'Download ZPL'}\n </Button>\n <Button disabled={!total} onClick={print}>\n {total === 1 ? 'Print 1 label' : `Print ${total} labels`}\n </Button>\n </DialogActions>\n </Dialog>\n )\n}\n\nexport default ProductLabelsDialog\n"],"names":["Button","Dialog","DialogActions","DialogContent","DialogTitle","MenuItem","Stack","TextField","Typography","useSnackbar","useEffect","useMemo","useState","LABEL_SIZES","MAX_LABEL_COPIES","labelCopiesFromInput","labelCopiesTotal","labelsZplFileName","printableLabelRows","productLabelRows","productLabelsHtml","productLabelsZpl","COPIES_INPUT","htmlInput","inputMode","min","max","ProductLabelsDialog","props","product","onClose","enqueueSnackbar","initial","rows","setRows","sizeId","setSizeId","id","printable","total","setCopies","variantId","value","current","map","row","copies","print","win","window","open","variant","persist","document","write","close","focus","downloadZpl","blob","Blob","type","url","URL","createObjectURL","link","createElement","href","download","name","click","setTimeout","revokeObjectURL","Boolean","fullWidth","maxWidth","spacing","sx","pt","select","label","onChange","event","target","size","direction","alignItems","flex","minWidth","noWrap","detail","color","code","disabled","slotProps","width","onClick"],"mappings":"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"}
|