@aglyn/plugins-commerce 1.0.0-beta.290 → 1.0.0-beta.292
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/README.md +4 -2
- package/package.json +13 -13
- package/src/lib/components/cart-fulfillment.d.ts +3 -0
- package/src/lib/components/cart-fulfillment.js.map +1 -1
- package/src/lib/components/cart.js +14 -4
- package/src/lib/components/cart.js.map +1 -1
- package/src/lib/components/commerce-console-page.js +2 -1
- package/src/lib/components/commerce-console-page.js.map +1 -1
- package/src/lib/components/console/host-orders-card.component.d.ts +6 -0
- package/src/lib/components/console/host-orders-card.component.js +3 -2
- package/src/lib/components/console/host-orders-card.component.js.map +1 -1
- package/src/lib/components/console/invoice-send.component.d.ts +44 -0
- package/src/lib/components/console/invoice-send.component.js +237 -0
- package/src/lib/components/console/invoice-send.component.js.map +1 -0
- package/src/lib/components/console/order-detail-dialog.component.d.ts +5 -0
- package/src/lib/components/console/order-detail-dialog.component.js +9 -2
- package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
- package/src/lib/components/console/order-invoice.component.d.ts +9 -0
- package/src/lib/components/console/order-invoice.component.js +27 -2
- package/src/lib/components/console/order-invoice.component.js.map +1 -1
- package/src/lib/components/console/pos/pos-item-dialog.component.js +3 -2
- package/src/lib/components/console/pos/pos-item-dialog.component.js.map +1 -1
- package/src/lib/components/console/pos-kiosk-page.component.js +2 -1
- package/src/lib/components/console/pos-kiosk-page.component.js.map +1 -1
- package/src/lib/components/console/pos-page.component.js +1 -1
- package/src/lib/components/console/pos-page.component.js.map +1 -1
- package/src/lib/components/console/product-add-ons-fields.component.d.ts +41 -0
- package/src/lib/components/console/product-add-ons-fields.component.js +356 -0
- package/src/lib/components/console/product-add-ons-fields.component.js.map +1 -0
- package/src/lib/components/console/product-editor-dialog.component.js +31 -5
- package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
- package/src/lib/components/console/product-register-fields.component.d.ts +4 -5
- package/src/lib/components/console/product-register-fields.component.js +8 -197
- package/src/lib/components/console/product-register-fields.component.js.map +1 -1
- package/src/lib/components/console/products-hub-zone.component.js +6 -0
- package/src/lib/components/console/products-hub-zone.component.js.map +1 -1
- package/src/lib/components/console/quote-editor-dialog.component.d.ts +29 -0
- package/src/lib/components/console/quote-editor-dialog.component.js +102 -4
- package/src/lib/components/console/quote-editor-dialog.component.js.map +1 -1
- package/src/lib/components/console/quote-pay-link.component.d.ts +8 -1
- package/src/lib/components/console/quote-pay-link.component.js +29 -3
- package/src/lib/components/console/quote-pay-link.component.js.map +1 -1
- package/src/lib/components/console/quote-proofs.component.d.ts +11 -0
- package/src/lib/components/console/quote-proofs.component.js +68 -0
- package/src/lib/components/console/quote-proofs.component.js.map +1 -1
- package/src/lib/components/console/quote-send.component.d.ts +42 -0
- package/src/lib/components/console/quote-send.component.js +304 -0
- package/src/lib/components/console/quote-send.component.js.map +1 -0
- package/src/lib/components/console/quote-start.component.d.ts +45 -0
- package/src/lib/components/console/quote-start.component.js +182 -0
- package/src/lib/components/console/quote-start.component.js.map +1 -0
- package/src/lib/components/product-add-ons.d.ts +28 -0
- package/src/lib/components/product-add-ons.js +136 -0
- package/src/lib/components/product-add-ons.js.map +1 -0
- package/src/lib/components/product-detail.d.ts +16 -0
- package/src/lib/components/product-detail.js +112 -19
- package/src/lib/components/product-detail.js.map +1 -1
- package/src/lib/components/product-grid.d.ts +8 -2
- package/src/lib/components/product-grid.js +6 -1
- package/src/lib/components/product-grid.js.map +1 -1
- package/src/lib/components/product-options-field-body.d.ts +44 -0
- package/src/lib/components/product-options-field-body.js +260 -0
- package/src/lib/components/product-options-field-body.js.map +1 -0
- package/src/lib/components/product-options-field.d.ts +32 -0
- package/src/lib/components/product-options-field.js +128 -0
- package/src/lib/components/product-options-field.js.map +1 -0
- package/src/lib/components/quote-page-body.js +39 -20
- package/src/lib/components/quote-page-body.js.map +1 -1
- package/src/lib/model/commerce-cart.d.ts +15 -3
- package/src/lib/model/commerce-cart.js +10 -3
- package/src/lib/model/commerce-cart.js.map +1 -1
- package/src/lib/model/commerce-invoices.d.ts +139 -0
- package/src/lib/model/commerce-invoices.js +343 -0
- package/src/lib/model/commerce-invoices.js.map +1 -0
- package/src/lib/model/commerce-orders.d.ts +35 -5
- package/src/lib/model/commerce-orders.js +3 -0
- package/src/lib/model/commerce-orders.js.map +1 -1
- package/src/lib/model/commerce-pos-kiosk.d.ts +1 -1
- package/src/lib/model/commerce-pos-kiosk.js +3 -2
- package/src/lib/model/commerce-pos-kiosk.js.map +1 -1
- package/src/lib/model/commerce-product-field.d.ts +23 -0
- package/src/lib/model/commerce-product-field.js +26 -0
- package/src/lib/model/commerce-product-field.js.map +1 -0
- package/src/lib/model/commerce-quote-email.d.ts +81 -0
- package/src/lib/model/commerce-quote-email.js +170 -0
- package/src/lib/model/commerce-quote-email.js.map +1 -0
- package/src/lib/model/commerce-quote-pay.d.ts +49 -6
- package/src/lib/model/commerce-quote-pay.js +69 -36
- package/src/lib/model/commerce-quote-pay.js.map +1 -1
- package/src/lib/model/commerce-quote-request.d.ts +93 -0
- package/src/lib/model/commerce-quote-request.js +207 -0
- package/src/lib/model/commerce-quote-request.js.map +1 -0
- package/src/lib/model/commerce-quotes.d.ts +81 -0
- package/src/lib/model/commerce-quotes.js +1 -1
- package/src/lib/model/commerce-quotes.js.map +1 -1
- package/src/lib/model/commerce.d.ts +18 -0
- package/src/lib/model/commerce.js +26 -0
- package/src/lib/model/commerce.js.map +1 -1
- package/src/lib/model/index.d.ts +3 -0
- package/src/lib/model/index.js +3 -0
- package/src/lib/model/index.js.map +1 -1
- package/src/lib/model/product-modifiers.d.ts +74 -4
- package/src/lib/model/product-modifiers.js +131 -10
- package/src/lib/model/product-modifiers.js.map +1 -1
- package/src/lib/plugin.js +19 -0
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/server/billing-webhook.js +164 -98
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/cart-checkout.js +14 -2
- package/src/lib/server/cart-checkout.js.map +1 -1
- package/src/lib/server/cart.d.ts +5 -0
- package/src/lib/server/cart.js +41 -4
- package/src/lib/server/cart.js.map +1 -1
- package/src/lib/server/catalog.d.ts +6 -0
- package/src/lib/server/catalog.js +2 -0
- package/src/lib/server/catalog.js.map +1 -1
- package/src/lib/server/checkout-extras.js +3 -1
- package/src/lib/server/checkout-extras.js.map +1 -1
- package/src/lib/server/checkout.js +28 -3
- package/src/lib/server/checkout.js.map +1 -1
- package/src/lib/server/commerce-pdf.d.ts +5 -1
- package/src/lib/server/commerce-pdf.js +37 -10
- package/src/lib/server/commerce-pdf.js.map +1 -1
- package/src/lib/server/draft-order.js +23 -3
- package/src/lib/server/draft-order.js.map +1 -1
- package/src/lib/server/invoice-email.d.ts +76 -0
- package/src/lib/server/invoice-email.js +183 -0
- package/src/lib/server/invoice-email.js.map +1 -0
- package/src/lib/server/local-fulfillment.js +5 -1
- package/src/lib/server/local-fulfillment.js.map +1 -1
- package/src/lib/server/order-invoice-send.d.ts +43 -0
- package/src/lib/server/order-invoice-send.js +218 -0
- package/src/lib/server/order-invoice-send.js.map +1 -0
- package/src/lib/server/order-status.d.ts +11 -0
- package/src/lib/server/order-status.js +44 -8
- package/src/lib/server/order-status.js.map +1 -1
- package/src/lib/server/person-eraser.d.ts +3 -2
- package/src/lib/server/person-eraser.js +25 -6
- package/src/lib/server/person-eraser.js.map +1 -1
- package/src/lib/server/pos-offline-sync.js +1 -1
- package/src/lib/server/pos-offline-sync.js.map +1 -1
- package/src/lib/server/pos-order.js +1 -1
- package/src/lib/server/pos-order.js.map +1 -1
- package/src/lib/server/product.d.ts +26 -0
- package/src/lib/server/product.js +18 -0
- package/src/lib/server/product.js.map +1 -1
- package/src/lib/server/products-write.js +5 -2
- package/src/lib/server/products-write.js.map +1 -1
- package/src/lib/server/quote-email.d.ts +85 -0
- package/src/lib/server/quote-email.js +211 -0
- package/src/lib/server/quote-email.js.map +1 -0
- package/src/lib/server/quote-link-token.d.ts +11 -0
- package/src/lib/server/quote-link-token.js +21 -0
- package/src/lib/server/quote-link-token.js.map +1 -1
- package/src/lib/server/quote-pay.d.ts +8 -3
- package/src/lib/server/quote-pay.js +105 -43
- package/src/lib/server/quote-pay.js.map +1 -1
- package/src/lib/server/quote.d.ts +11 -0
- package/src/lib/server/quote.js +309 -12
- package/src/lib/server/quote.js.map +1 -1
- package/src/lib/server/site-page-resolver.js +2 -1
- package/src/lib/server/site-page-resolver.js.map +1 -1
- package/src/lib/server.js +5 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/site.js +8 -0
- package/src/lib/site.js.map +1 -1
- package/src/lib/tenant-emails.js +243 -0
- package/src/lib/tenant-emails.js.map +1 -1
- package/src/lib/utils/add-to-cart.d.ts +8 -0
- package/src/lib/utils/add-to-cart.js +10 -2
- package/src/lib/utils/add-to-cart.js.map +1 -1
- package/src/lib/utils/commerce-document-pdf.d.ts +5 -0
- package/src/lib/utils/commerce-document-pdf.js +6 -1
- package/src/lib/utils/commerce-document-pdf.js.map +1 -1
- package/src/lib/utils/invoice-email-content.d.ts +45 -0
- package/src/lib/utils/invoice-email-content.js +126 -0
- package/src/lib/utils/invoice-email-content.js.map +1 -0
- package/src/lib/utils/order-invoice.d.ts +1 -1
- package/src/lib/utils/order-invoice.js +14 -6
- package/src/lib/utils/order-invoice.js.map +1 -1
- package/src/lib/utils/quote-email-content.d.ts +63 -0
- package/src/lib/utils/quote-email-content.js +157 -0
- package/src/lib/utils/quote-email-content.js.map +1 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ 'use client';
|
|
17
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
18
|
+
import * as CommerceModel from "../../model/index.js";
|
|
19
|
+
import { siteContactEmail, resolveHostToken } from "@aglyn/aglyn/app-utils/host-tokens";
|
|
20
|
+
import { useSnackbar } from "@aglyn/shared-ui-snackstack";
|
|
21
|
+
import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
|
|
22
|
+
import { useFirestore, useUser } from "@aglyn/tenant-feature-instance";
|
|
23
|
+
import { Button, Dialog, DialogActions, DialogContent, DialogTitle, Stack, TextField, Typography } from "@mui/material";
|
|
24
|
+
import { doc, getDoc } from "firebase/firestore";
|
|
25
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
26
|
+
/** `Oct 29, 2026, 3:04 PM`. */ const sentAt = (atMs)=>new Date(atMs).toLocaleString('en-US', {
|
|
27
|
+
dateStyle: 'medium',
|
|
28
|
+
timeStyle: 'short'
|
|
29
|
+
});
|
|
30
|
+
/**
|
|
31
|
+
* The order's (or the quote's) last invoice email, as the console says it:
|
|
32
|
+
* `Invoice sent Oct 29, 2026, 3:04 PM to jordan@example.com.` Null when none
|
|
33
|
+
* was sent.
|
|
34
|
+
*/ export function invoiceSentLine(order) {
|
|
35
|
+
const last = CommerceModel.lastInvoiceSend(order);
|
|
36
|
+
if (!last) return null;
|
|
37
|
+
return `${last.paid ? 'Paid invoice' : 'Invoice'} sent ${sentAt(Number(last.atMs))} to ${last.to || 'the customer'}.`;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Send invoice (AGL-4099), for an order or a quote: a short form (To,
|
|
41
|
+
* Subject, Message) that emails the invoice in the site's look with the
|
|
42
|
+
* invoice PDF attached. Unpaid, the email carries a Pay button to the order's
|
|
43
|
+
* lasting pay link on the business's site; paid, it is the customer's record.
|
|
44
|
+
* A quote's invoice lands in the quote's conversation, and an order's in its
|
|
45
|
+
* own. The site's name and contact email are read once when the form opens,
|
|
46
|
+
* so the dialog that offers it listens to nothing more.
|
|
47
|
+
*/ export function InvoiceSendDialog(props) {
|
|
48
|
+
const { open, onClose, hostId, orderId, order } = props;
|
|
49
|
+
const firestore = useFirestore();
|
|
50
|
+
const { data: user } = useUser();
|
|
51
|
+
const { enqueueSnackbar } = useSnackbar();
|
|
52
|
+
const [busy, setBusy] = useState(false);
|
|
53
|
+
const [to, setTo] = useState('');
|
|
54
|
+
const [subject, setSubject] = useState('');
|
|
55
|
+
const [message, setMessage] = useState('');
|
|
56
|
+
const [contact, setContact] = useState(null);
|
|
57
|
+
/*
|
|
58
|
+
* One attempt per form (AGL-1697's rule): a retried Send presents the same
|
|
59
|
+
* key and is answered from the first, so the customer never gets two; a
|
|
60
|
+
* changed form is a new attempt.
|
|
61
|
+
*/ const attemptKey = useRef('');
|
|
62
|
+
useEffect(()=>{
|
|
63
|
+
attemptKey.current = '';
|
|
64
|
+
}, [
|
|
65
|
+
to,
|
|
66
|
+
subject,
|
|
67
|
+
message
|
|
68
|
+
]);
|
|
69
|
+
useEffect(()=>{
|
|
70
|
+
if (!open) return;
|
|
71
|
+
let live = true;
|
|
72
|
+
setTo(CommerceModel.invoiceSendPrefill(order));
|
|
73
|
+
setSubject(CommerceModel.invoiceEmailDefaultSubject(order, '', orderId));
|
|
74
|
+
setMessage('');
|
|
75
|
+
void getDoc(doc(firestore, 'hosts', hostId)).then((snapshot)=>{
|
|
76
|
+
var _snapshot_data, _resolveHostToken;
|
|
77
|
+
if (!live) return;
|
|
78
|
+
const host = (_snapshot_data = snapshot.data()) != null ? _snapshot_data : {};
|
|
79
|
+
const businessName = String((_resolveHostToken = resolveHostToken('businessName', host)) != null ? _resolveHostToken : '').trim();
|
|
80
|
+
setSubject(CommerceModel.invoiceEmailDefaultSubject(order, businessName, orderId));
|
|
81
|
+
const listed = siteContactEmail(host);
|
|
82
|
+
setContact(listed && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(listed) ? listed : null);
|
|
83
|
+
}).catch(()=>undefined);
|
|
84
|
+
return ()=>{
|
|
85
|
+
live = false;
|
|
86
|
+
};
|
|
87
|
+
// Filled once per opening; edits after that are the member's.
|
|
88
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
89
|
+
}, [
|
|
90
|
+
open
|
|
91
|
+
]);
|
|
92
|
+
const paid = CommerceModel.invoiceIsPaid(order);
|
|
93
|
+
const refused = CommerceModel.invoiceSendRefusal(order, orderId, to.trim());
|
|
94
|
+
// The address is the form's to fix; every other refusal is the order's, said up front.
|
|
95
|
+
const blocker = refused && refused !== 'no-address' && refused !== 'bad-address' ? refused : null;
|
|
96
|
+
const threaded = Boolean(CommerceModel.invoiceEmailThread(order));
|
|
97
|
+
const number = CommerceModel.formatInvoiceNumber(order, orderId);
|
|
98
|
+
const send = useCallback(async ()=>{
|
|
99
|
+
setBusy(true);
|
|
100
|
+
try {
|
|
101
|
+
var _payload_to;
|
|
102
|
+
if (!attemptKey.current) {
|
|
103
|
+
var _ref;
|
|
104
|
+
var _globalThis_crypto_randomUUID, _globalThis_crypto;
|
|
105
|
+
attemptKey.current = (_ref = (_globalThis_crypto = globalThis.crypto) == null ? void 0 : (_globalThis_crypto_randomUUID = _globalThis_crypto.randomUUID) == null ? void 0 : _globalThis_crypto_randomUUID.call(_globalThis_crypto)) != null ? _ref : `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
106
|
+
}
|
|
107
|
+
const response = await authorizedFetch(user, '/api/commerce/order-invoice-send', {
|
|
108
|
+
method: 'POST',
|
|
109
|
+
headers: {
|
|
110
|
+
'Content-Type': 'application/json',
|
|
111
|
+
'Idempotency-Key': attemptKey.current
|
|
112
|
+
},
|
|
113
|
+
body: JSON.stringify({
|
|
114
|
+
hostId,
|
|
115
|
+
orderId,
|
|
116
|
+
to,
|
|
117
|
+
subject,
|
|
118
|
+
message
|
|
119
|
+
})
|
|
120
|
+
});
|
|
121
|
+
const payload = await response.json().catch(()=>({}));
|
|
122
|
+
if (!response.ok) {
|
|
123
|
+
var _ref1;
|
|
124
|
+
return void enqueueSnackbar((_ref1 = payload == null ? void 0 : payload.error) != null ? _ref1 : 'The invoice could not be sent', {
|
|
125
|
+
variant: 'error',
|
|
126
|
+
allowDuplicate: true
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
enqueueSnackbar(`${number} sent to ${String((_payload_to = payload.to) != null ? _payload_to : to)}`, {
|
|
130
|
+
variant: 'success',
|
|
131
|
+
persist: false
|
|
132
|
+
});
|
|
133
|
+
onClose();
|
|
134
|
+
} finally{
|
|
135
|
+
setBusy(false);
|
|
136
|
+
}
|
|
137
|
+
}, [
|
|
138
|
+
user,
|
|
139
|
+
hostId,
|
|
140
|
+
orderId,
|
|
141
|
+
to,
|
|
142
|
+
subject,
|
|
143
|
+
message,
|
|
144
|
+
number,
|
|
145
|
+
enqueueSnackbar,
|
|
146
|
+
onClose
|
|
147
|
+
]);
|
|
148
|
+
return /*#__PURE__*/ _jsxs(Dialog, {
|
|
149
|
+
open: open,
|
|
150
|
+
onClose: busy ? undefined : onClose,
|
|
151
|
+
maxWidth: "sm",
|
|
152
|
+
fullWidth: true,
|
|
153
|
+
"aria-labelledby": "invoice-send-title",
|
|
154
|
+
children: [
|
|
155
|
+
/*#__PURE__*/ _jsx(DialogTitle, {
|
|
156
|
+
id: "invoice-send-title",
|
|
157
|
+
children: `Send invoice: ${number}`
|
|
158
|
+
}),
|
|
159
|
+
/*#__PURE__*/ _jsx(DialogContent, {
|
|
160
|
+
children: /*#__PURE__*/ _jsxs(Stack, {
|
|
161
|
+
spacing: 2,
|
|
162
|
+
sx: {
|
|
163
|
+
pt: 1
|
|
164
|
+
},
|
|
165
|
+
children: [
|
|
166
|
+
blocker ? /*#__PURE__*/ _jsx(Typography, {
|
|
167
|
+
variant: "body2",
|
|
168
|
+
color: "error",
|
|
169
|
+
children: CommerceModel.invoiceSendRefusalMessage(blocker)
|
|
170
|
+
}) : null,
|
|
171
|
+
/*#__PURE__*/ _jsx(TextField, {
|
|
172
|
+
label: "To",
|
|
173
|
+
type: "email",
|
|
174
|
+
value: to,
|
|
175
|
+
onChange: (event)=>setTo(event.target.value),
|
|
176
|
+
required: true,
|
|
177
|
+
fullWidth: true,
|
|
178
|
+
disabled: busy
|
|
179
|
+
}),
|
|
180
|
+
/*#__PURE__*/ _jsx(TextField, {
|
|
181
|
+
label: "Subject",
|
|
182
|
+
value: subject,
|
|
183
|
+
onChange: (event)=>setSubject(event.target.value),
|
|
184
|
+
fullWidth: true,
|
|
185
|
+
disabled: busy,
|
|
186
|
+
slotProps: {
|
|
187
|
+
htmlInput: {
|
|
188
|
+
maxLength: CommerceModel.QUOTE_MAX_SEND_SUBJECT
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
helperText: threaded ? 'Keep the subject the same to keep these emails in one conversation in your customer’s inbox.' : undefined
|
|
192
|
+
}),
|
|
193
|
+
/*#__PURE__*/ _jsx(TextField, {
|
|
194
|
+
label: "Message",
|
|
195
|
+
value: message,
|
|
196
|
+
onChange: (event)=>setMessage(event.target.value),
|
|
197
|
+
multiline: true,
|
|
198
|
+
minRows: 4,
|
|
199
|
+
fullWidth: true,
|
|
200
|
+
disabled: busy,
|
|
201
|
+
slotProps: {
|
|
202
|
+
htmlInput: {
|
|
203
|
+
maxLength: CommerceModel.QUOTE_MAX_SEND_MESSAGE
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
helperText: "Shown at the top of the email, in your words."
|
|
207
|
+
}),
|
|
208
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
209
|
+
variant: "body2",
|
|
210
|
+
color: "text.secondary",
|
|
211
|
+
children: (paid ? 'The email shows the invoice as paid, with the date it was paid, for your customer’s records. ' : 'The email shows the invoice number, date, total and what is due, with a button to pay it by card on your site. The button keeps working until it is paid. ') + `The invoice is attached as a PDF. Replies go to ${contact || 'your email'}.`
|
|
212
|
+
})
|
|
213
|
+
]
|
|
214
|
+
})
|
|
215
|
+
}),
|
|
216
|
+
/*#__PURE__*/ _jsxs(DialogActions, {
|
|
217
|
+
children: [
|
|
218
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
219
|
+
onClick: onClose,
|
|
220
|
+
disabled: busy,
|
|
221
|
+
children: 'Cancel'
|
|
222
|
+
}),
|
|
223
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
224
|
+
variant: "contained",
|
|
225
|
+
disabled: busy || !to.trim() || Boolean(blocker),
|
|
226
|
+
onClick: ()=>void send(),
|
|
227
|
+
children: busy ? 'Sending…' : 'Send'
|
|
228
|
+
})
|
|
229
|
+
]
|
|
230
|
+
})
|
|
231
|
+
]
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
InvoiceSendDialog.displayName = 'InvoiceSendDialog';
|
|
235
|
+
export default InvoiceSendDialog;
|
|
236
|
+
|
|
237
|
+
//# sourceMappingURL=invoice-send.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/invoice-send.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 * as CommerceModel from '../../model'\nimport { siteContactEmail, resolveHostToken, type HostTokenSource } from '@aglyn/aglyn/app-utils/host-tokens'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useFirestore, useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { doc, getDoc } from 'firebase/firestore'\nimport { useCallback, useEffect, useRef, useState } from 'react'\n\n/** `Oct 29, 2026, 3:04 PM`. */\nconst sentAt = (atMs: number) =>\n new Date(atMs).toLocaleString('en-US', { dateStyle: 'medium', timeStyle: 'short' })\n\n/**\n * The order's (or the quote's) last invoice email, as the console says it:\n * `Invoice sent Oct 29, 2026, 3:04 PM to jordan@example.com.` Null when none\n * was sent.\n */\nexport function invoiceSentLine(order: Pick<CommerceModel.HostOrder, 'invoiceSends'>): string | null {\n const last = CommerceModel.lastInvoiceSend(order)\n if (!last) return null\n return `${last.paid ? 'Paid invoice' : 'Invoice'} sent ${sentAt(Number(last.atMs))} to ${last.to || 'the customer'}.`\n}\n\nexport interface InvoiceSendDialogProps {\n open: boolean\n onClose: () => void\n hostId: string\n orderId: string\n order: CommerceModel.HostOrder\n}\n\n/**\n * Send invoice (AGL-4099), for an order or a quote: a short form (To,\n * Subject, Message) that emails the invoice in the site's look with the\n * invoice PDF attached. Unpaid, the email carries a Pay button to the order's\n * lasting pay link on the business's site; paid, it is the customer's record.\n * A quote's invoice lands in the quote's conversation, and an order's in its\n * own. The site's name and contact email are read once when the form opens,\n * so the dialog that offers it listens to nothing more.\n */\nexport function InvoiceSendDialog(props: InvoiceSendDialogProps) {\n const { open, onClose, hostId, orderId, order } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const [busy, setBusy] = useState(false)\n const [to, setTo] = useState('')\n const [subject, setSubject] = useState('')\n const [message, setMessage] = useState('')\n const [contact, setContact] = useState<string | null>(null)\n\n /*\n * One attempt per form (AGL-1697's rule): a retried Send presents the same\n * key and is answered from the first, so the customer never gets two; a\n * changed form is a new attempt.\n */\n const attemptKey = useRef('')\n useEffect(() => {\n attemptKey.current = ''\n }, [to, subject, message])\n\n useEffect(() => {\n if (!open) return\n let live = true\n setTo(CommerceModel.invoiceSendPrefill(order))\n setSubject(CommerceModel.invoiceEmailDefaultSubject(order, '', orderId))\n setMessage('')\n void getDoc(doc(firestore, 'hosts', hostId))\n .then((snapshot) => {\n if (!live) return\n const host = (snapshot.data() ?? {}) as Record<string, unknown>\n const businessName = String(resolveHostToken('businessName', host as HostTokenSource) ?? '').trim()\n setSubject(CommerceModel.invoiceEmailDefaultSubject(order, businessName, orderId))\n const listed = siteContactEmail(host as never)\n setContact(listed && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(listed) ? listed : null)\n })\n .catch(() => undefined)\n return () => {\n live = false\n }\n // Filled once per opening; edits after that are the member's.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open])\n\n const paid = CommerceModel.invoiceIsPaid(order)\n const refused = CommerceModel.invoiceSendRefusal(order, orderId, to.trim())\n // The address is the form's to fix; every other refusal is the order's, said up front.\n const blocker = refused && refused !== 'no-address' && refused !== 'bad-address' ? refused : null\n const threaded = Boolean(CommerceModel.invoiceEmailThread(order))\n const number = CommerceModel.formatInvoiceNumber(order, orderId)\n\n const send = useCallback(async () => {\n setBusy(true)\n try {\n if (!attemptKey.current) {\n attemptKey.current = globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`\n }\n const response = await authorizedFetch(user, '/api/commerce/order-invoice-send', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Idempotency-Key': attemptKey.current },\n body: JSON.stringify({ hostId, orderId, to, subject, message }),\n })\n const payload = await response.json().catch(() => ({}))\n if (!response.ok) {\n return void enqueueSnackbar(payload?.error ?? 'The invoice could not be sent', { variant: 'error', allowDuplicate: true })\n }\n enqueueSnackbar(`${number} sent to ${String(payload.to ?? to)}`, { variant: 'success', persist: false })\n onClose()\n } finally {\n setBusy(false)\n }\n }, [user, hostId, orderId, to, subject, message, number, enqueueSnackbar, onClose])\n\n return (\n <Dialog open={open} onClose={busy ? undefined : onClose} maxWidth=\"sm\" fullWidth aria-labelledby=\"invoice-send-title\">\n <DialogTitle id=\"invoice-send-title\">{`Send invoice: ${number}`}</DialogTitle>\n <DialogContent>\n <Stack spacing={2} sx={{ pt: 1 }}>\n {blocker ? (\n <Typography variant=\"body2\" color=\"error\">\n {CommerceModel.invoiceSendRefusalMessage(blocker)}\n </Typography>\n ) : null}\n <TextField\n label=\"To\"\n type=\"email\"\n value={to}\n onChange={(event) => setTo(event.target.value)}\n required\n fullWidth\n disabled={busy}\n />\n <TextField\n label=\"Subject\"\n value={subject}\n onChange={(event) => setSubject(event.target.value)}\n fullWidth\n disabled={busy}\n slotProps={{ htmlInput: { maxLength: CommerceModel.QUOTE_MAX_SEND_SUBJECT } }}\n helperText={\n threaded\n ? 'Keep the subject the same to keep these emails in one conversation in your customer’s inbox.'\n : undefined\n }\n />\n <TextField\n label=\"Message\"\n value={message}\n onChange={(event) => setMessage(event.target.value)}\n multiline\n minRows={4}\n fullWidth\n disabled={busy}\n slotProps={{ htmlInput: { maxLength: CommerceModel.QUOTE_MAX_SEND_MESSAGE } }}\n helperText=\"Shown at the top of the email, in your words.\"\n />\n <Typography variant=\"body2\" color=\"text.secondary\">\n {(paid\n ? 'The email shows the invoice as paid, with the date it was paid, for your customer’s records. '\n : 'The email shows the invoice number, date, total and what is due, with a button to pay it by card on your site. The button keeps working until it is paid. ') +\n `The invoice is attached as a PDF. Replies go to ${contact || 'your email'}.`}\n </Typography>\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} disabled={busy}>\n {'Cancel'}\n </Button>\n <Button variant=\"contained\" disabled={busy || !to.trim() || Boolean(blocker)} onClick={() => void send()}>\n {busy ? 'Sending…' : 'Send'}\n </Button>\n </DialogActions>\n </Dialog>\n )\n}\nInvoiceSendDialog.displayName = 'InvoiceSendDialog'\n\nexport default InvoiceSendDialog\n"],"names":["CommerceModel","siteContactEmail","resolveHostToken","useSnackbar","authorizedFetch","useFirestore","useUser","Button","Dialog","DialogActions","DialogContent","DialogTitle","Stack","TextField","Typography","doc","getDoc","useCallback","useEffect","useRef","useState","sentAt","atMs","Date","toLocaleString","dateStyle","timeStyle","invoiceSentLine","order","last","lastInvoiceSend","paid","Number","to","InvoiceSendDialog","props","open","onClose","hostId","orderId","firestore","data","user","enqueueSnackbar","busy","setBusy","setTo","subject","setSubject","message","setMessage","contact","setContact","attemptKey","current","live","invoiceSendPrefill","invoiceEmailDefaultSubject","then","snapshot","host","businessName","String","trim","listed","test","catch","undefined","invoiceIsPaid","refused","invoiceSendRefusal","blocker","threaded","Boolean","invoiceEmailThread","number","formatInvoiceNumber","send","payload","globalThis","crypto","randomUUID","now","Math","random","toString","slice","response","method","headers","body","JSON","stringify","json","ok","error","variant","allowDuplicate","persist","maxWidth","fullWidth","aria-labelledby","id","spacing","sx","pt","color","invoiceSendRefusalMessage","label","type","value","onChange","event","target","required","disabled","slotProps","htmlInput","maxLength","QUOTE_MAX_SEND_SUBJECT","helperText","multiline","minRows","QUOTE_MAX_SEND_MESSAGE","onClick","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;AAEA,YAAYA,mBAAmB,uBAAa;AAC5C,SAASC,gBAAgB,EAAEC,gBAAgB,QAA8B,qCAAoC;AAC7G,SAASC,WAAW,QAAQ,8BAA6B;AACzD,SAASC,eAAe,QAAQ,2CAA0C;AAC1E,SAASC,YAAY,EAAEC,OAAO,QAAQ,iCAAgC;AACtE,SACEC,MAAM,EACNC,MAAM,EACNC,aAAa,EACbC,aAAa,EACbC,WAAW,EACXC,KAAK,EACLC,SAAS,EACTC,UAAU,QACL,gBAAe;AACtB,SAASC,GAAG,EAAEC,MAAM,QAAQ,qBAAoB;AAChD,SAASC,WAAW,EAAEC,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,QAAO;AAEhE,6BAA6B,GAC7B,MAAMC,SAAS,CAACC,OACd,IAAIC,KAAKD,MAAME,cAAc,CAAC,SAAS;QAAEC,WAAW;QAAUC,WAAW;IAAQ;AAEnF;;;;CAIC,GACD,OAAO,SAASC,gBAAgBC,KAAoD;IAClF,MAAMC,OAAO7B,cAAc8B,eAAe,CAACF;IAC3C,IAAI,CAACC,MAAM,OAAO;IAClB,OAAO,GAAGA,KAAKE,IAAI,GAAG,iBAAiB,UAAU,MAAM,EAAEV,OAAOW,OAAOH,KAAKP,IAAI,GAAG,IAAI,EAAEO,KAAKI,EAAE,IAAI,eAAe,CAAC,CAAC;AACvH;AAUA;;;;;;;;CAQC,GACD,OAAO,SAASC,kBAAkBC,KAA6B;IAC7D,MAAM,EAAEC,IAAI,EAAEC,OAAO,EAAEC,MAAM,EAAEC,OAAO,EAAEX,KAAK,EAAE,GAAGO;IAClD,MAAMK,YAAYnC;IAClB,MAAM,EAAEoC,MAAMC,IAAI,EAAE,GAAGpC;IACvB,MAAM,EAAEqC,eAAe,EAAE,GAAGxC;IAC5B,MAAM,CAACyC,MAAMC,QAAQ,GAAGzB,SAAS;IACjC,MAAM,CAACa,IAAIa,MAAM,GAAG1B,SAAS;IAC7B,MAAM,CAAC2B,SAASC,WAAW,GAAG5B,SAAS;IACvC,MAAM,CAAC6B,SAASC,WAAW,GAAG9B,SAAS;IACvC,MAAM,CAAC+B,SAASC,WAAW,GAAGhC,SAAwB;IAEtD;;;;GAIC,GACD,MAAMiC,aAAalC,OAAO;IAC1BD,UAAU;QACRmC,WAAWC,OAAO,GAAG;IACvB,GAAG;QAACrB;QAAIc;QAASE;KAAQ;IAEzB/B,UAAU;QACR,IAAI,CAACkB,MAAM;QACX,IAAImB,OAAO;QACXT,MAAM9C,cAAcwD,kBAAkB,CAAC5B;QACvCoB,WAAWhD,cAAcyD,0BAA0B,CAAC7B,OAAO,IAAIW;QAC/DW,WAAW;QACX,KAAKlC,OAAOD,IAAIyB,WAAW,SAASF,SACjCoB,IAAI,CAAC,CAACC;gBAESA,gBACczD;YAF5B,IAAI,CAACqD,MAAM;YACX,MAAMK,QAAQD,iBAAAA,SAASlB,IAAI,cAAbkB,iBAAmB,CAAC;YAClC,MAAME,eAAeC,QAAO5D,oBAAAA,iBAAiB,gBAAgB0D,iBAAjC1D,oBAA6D,IAAI6D,IAAI;YACjGf,WAAWhD,cAAcyD,0BAA0B,CAAC7B,OAAOiC,cAActB;YACzE,MAAMyB,SAAS/D,iBAAiB2D;YAChCR,WAAWY,UAAU,6BAA6BC,IAAI,CAACD,UAAUA,SAAS;QAC5E,GACCE,KAAK,CAAC,IAAMC;QACf,OAAO;YACLZ,OAAO;QACT;IACA,8DAA8D;IAC9D,uDAAuD;IACzD,GAAG;QAACnB;KAAK;IAET,MAAML,OAAO/B,cAAcoE,aAAa,CAACxC;IACzC,MAAMyC,UAAUrE,cAAcsE,kBAAkB,CAAC1C,OAAOW,SAASN,GAAG8B,IAAI;IACxE,uFAAuF;IACvF,MAAMQ,UAAUF,WAAWA,YAAY,gBAAgBA,YAAY,gBAAgBA,UAAU;IAC7F,MAAMG,WAAWC,QAAQzE,cAAc0E,kBAAkB,CAAC9C;IAC1D,MAAM+C,SAAS3E,cAAc4E,mBAAmB,CAAChD,OAAOW;IAExD,MAAMsC,OAAO5D,YAAY;QACvB4B,QAAQ;QACR,IAAI;gBAa0CiC;YAZ5C,IAAI,CAACzB,WAAWC,OAAO,EAAE;;oBACFyB,+BAAAA;gBAArB1B,WAAWC,OAAO,YAAGyB,qBAAAA,WAAWC,MAAM,sBAAjBD,gCAAAA,mBAAmBE,UAAU,qBAA7BF,mCAAAA,sCAAqC,GAAGxD,KAAK2D,GAAG,GAAG,CAAC,EAAEC,KAAKC,MAAM,GAAGC,QAAQ,CAAC,IAAIC,KAAK,CAAC,IAAI;YAClH;YACA,MAAMC,WAAW,MAAMnF,gBAAgBsC,MAAM,oCAAoC;gBAC/E8C,QAAQ;gBACRC,SAAS;oBAAE,gBAAgB;oBAAoB,mBAAmBpC,WAAWC,OAAO;gBAAC;gBACrFoC,MAAMC,KAAKC,SAAS,CAAC;oBAAEtD;oBAAQC;oBAASN;oBAAIc;oBAASE;gBAAQ;YAC/D;YACA,MAAM6B,UAAU,MAAMS,SAASM,IAAI,GAAG3B,KAAK,CAAC,IAAO,CAAA,CAAC,CAAA;YACpD,IAAI,CAACqB,SAASO,EAAE,EAAE;;gBAChB,OAAO,KAAKnD,yBAAgBmC,2BAAAA,QAASiB,KAAK,oBAAI,iCAAiC;oBAAEC,SAAS;oBAASC,gBAAgB;gBAAK;YAC1H;YACAtD,gBAAgB,GAAGgC,OAAO,SAAS,EAAEb,QAAOgB,cAAAA,QAAQ7C,EAAE,YAAV6C,cAAc7C,KAAK,EAAE;gBAAE+D,SAAS;gBAAWE,SAAS;YAAM;YACtG7D;QACF,SAAU;YACRQ,QAAQ;QACV;IACF,GAAG;QAACH;QAAMJ;QAAQC;QAASN;QAAIc;QAASE;QAAS0B;QAAQhC;QAAiBN;KAAQ;IAElF,qBACE,MAAC7B;QAAO4B,MAAMA;QAAMC,SAASO,OAAOuB,YAAY9B;QAAS8D,UAAS;QAAKC,SAAS;QAACC,mBAAgB;;0BAC/F,KAAC1F;gBAAY2F,IAAG;0BAAsB,CAAC,cAAc,EAAE3B,QAAQ;;0BAC/D,KAACjE;0BACC,cAAA,MAACE;oBAAM2F,SAAS;oBAAGC,IAAI;wBAAEC,IAAI;oBAAE;;wBAC5BlC,wBACC,KAACzD;4BAAWkF,SAAQ;4BAAQU,OAAM;sCAC/B1G,cAAc2G,yBAAyB,CAACpC;6BAEzC;sCACJ,KAAC1D;4BACC+F,OAAM;4BACNC,MAAK;4BACLC,OAAO7E;4BACP8E,UAAU,CAACC,QAAUlE,MAAMkE,MAAMC,MAAM,CAACH,KAAK;4BAC7CI,QAAQ;4BACRd,SAAS;4BACTe,UAAUvE;;sCAEZ,KAAC/B;4BACC+F,OAAM;4BACNE,OAAO/D;4BACPgE,UAAU,CAACC,QAAUhE,WAAWgE,MAAMC,MAAM,CAACH,KAAK;4BAClDV,SAAS;4BACTe,UAAUvE;4BACVwE,WAAW;gCAAEC,WAAW;oCAAEC,WAAWtH,cAAcuH,sBAAsB;gCAAC;4BAAE;4BAC5EC,YACEhD,WACI,iGACAL;;sCAGR,KAACtD;4BACC+F,OAAM;4BACNE,OAAO7D;4BACP8D,UAAU,CAACC,QAAU9D,WAAW8D,MAAMC,MAAM,CAACH,KAAK;4BAClDW,SAAS;4BACTC,SAAS;4BACTtB,SAAS;4BACTe,UAAUvE;4BACVwE,WAAW;gCAAEC,WAAW;oCAAEC,WAAWtH,cAAc2H,sBAAsB;gCAAC;4BAAE;4BAC5EH,YAAW;;sCAEb,KAAC1G;4BAAWkF,SAAQ;4BAAQU,OAAM;sCAC/B,AAAC3E,CAAAA,OACE,kGACA,4JAA2J,IAC7J,CAAC,gDAAgD,EAAEoB,WAAW,aAAa,CAAC,CAAC;;;;;0BAIrF,MAAC1C;;kCACC,KAACF;wBAAOqH,SAASvF;wBAAS8E,UAAUvE;kCACjC;;kCAEH,KAACrC;wBAAOyF,SAAQ;wBAAYmB,UAAUvE,QAAQ,CAACX,GAAG8B,IAAI,MAAMU,QAAQF;wBAAUqD,SAAS,IAAM,KAAK/C;kCAC/FjC,OAAO,aAAa;;;;;;AAK/B;AACAV,kBAAkB2F,WAAW,GAAG;AAEhC,eAAe3F,kBAAiB"}
|
|
@@ -5,6 +5,11 @@ export interface OrderDetailDialogProps {
|
|
|
5
5
|
$id: string;
|
|
6
6
|
}) | null;
|
|
7
7
|
onClose: () => void;
|
|
8
|
+
/**
|
|
9
|
+
* Holds "Send quotes and invoices to customers" (`commerce.quotes.send`),
|
|
10
|
+
* for Send invoice (AGL-4099). Absent reads as held: the route decides.
|
|
11
|
+
*/
|
|
12
|
+
canSendInvoices?: boolean;
|
|
8
13
|
}
|
|
9
14
|
/**
|
|
10
15
|
* The dispute chip's colour (AGL-1796).
|
|
@@ -39,6 +39,7 @@ import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
|
|
|
39
39
|
import { useConsoleWidgetSlot } from "@aglyn/aglyn/app-utils/console-widget-slot-context";
|
|
40
40
|
import { consoleOrderZoneOrder, FulfillItemsPanel, FulfillmentList, toZoneFulfillment } from "./order-fulfillment-panel.component.js";
|
|
41
41
|
import { ORDER_DETAIL_ZONE, ORDER_FULFILLMENT_ZONE } from "./order-zones.js";
|
|
42
|
+
import { invoiceSentLine } from "./invoice-send.component.js";
|
|
42
43
|
import { OrderInvoiceButton } from "./order-invoice.component.js";
|
|
43
44
|
import { OrderReceiptResend } from "./order-receipt-resend.component.js";
|
|
44
45
|
import { OrderReturns } from "./order-returns.component.js";
|
|
@@ -89,7 +90,7 @@ const usd = (cents)=>`$${((cents != null ? cents : 0) / 100).toFixed(2)}`;
|
|
|
89
90
|
* write), notes, printable packing slip.
|
|
90
91
|
*/ export function OrderDetailDialog(props) {
|
|
91
92
|
var _STATUS_COLOR_order_status, _order_lineItems, _order_extras, _order_timeline;
|
|
92
|
-
const { hostId, order: rawOrder, onClose } = props;
|
|
93
|
+
const { hostId, order: rawOrder, onClose, canSendInvoices = true } = props;
|
|
93
94
|
const firestore = useFirestore();
|
|
94
95
|
const { data: user } = useUser();
|
|
95
96
|
const { enqueueSnackbar } = useSnackbar();
|
|
@@ -1024,6 +1025,11 @@ const usd = (cents)=>`$${((cents != null ? cents : 0) / 100).toFixed(2)}`;
|
|
|
1024
1025
|
orderId: orderId,
|
|
1025
1026
|
order: order
|
|
1026
1027
|
}) : null,
|
|
1028
|
+
invoiceSentLine(order) ? /*#__PURE__*/ _jsx(Typography, {
|
|
1029
|
+
variant: "body2",
|
|
1030
|
+
color: "text.secondary",
|
|
1031
|
+
children: invoiceSentLine(order)
|
|
1032
|
+
}) : null,
|
|
1027
1033
|
order.paymentLinkUrl && order.status === 'pending' ? /*#__PURE__*/ _jsx(Button, {
|
|
1028
1034
|
size: "small",
|
|
1029
1035
|
sx: {
|
|
@@ -1058,7 +1064,8 @@ const usd = (cents)=>`$${((cents != null ? cents : 0) / 100).toFixed(2)}`;
|
|
|
1058
1064
|
orderId ? /*#__PURE__*/ _jsx(OrderInvoiceButton, {
|
|
1059
1065
|
hostId: hostId,
|
|
1060
1066
|
orderId: orderId,
|
|
1061
|
-
order: order
|
|
1067
|
+
order: order,
|
|
1068
|
+
canSend: canSendInvoices
|
|
1062
1069
|
}) : null,
|
|
1063
1070
|
orderId ? /*#__PURE__*/ _jsx(OrderReceiptResend, {
|
|
1064
1071
|
hostId: hostId,
|