Created
November 6, 2025 18:30
-
-
Save jay-babu/1974d4776ac2c44f7165279af0d3b414 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/src/components/common/PurchaseOrderFinalizationDialog.tsx b/src/components/common/PurchaseOrderFinalizationDialog.tsx | |
| index 7d12de80d..38429496b 100644 | |
| --- a/src/components/common/PurchaseOrderFinalizationDialog.tsx | |
| +++ b/src/components/common/PurchaseOrderFinalizationDialog.tsx | |
| @@ -11,6 +11,7 @@ import { | |
| import { Label } from "components/ui/label"; | |
| import { ReactNode, useEffect, useMemo } from "react"; | |
| import { useForm } from "react-hook-form"; | |
| +import { Loader2 } from "lucide-react"; | |
| export interface FinalizationOption { | |
| id: string; | |
| @@ -30,6 +31,8 @@ export interface PurchaseOrderFinalizationDialogProps { | |
| description?: string; | |
| isLoading?: boolean; | |
| onActionComplete?: (actionId: string) => void; | |
| + replyToEmail?: string; | |
| + onReplyToEmailChange?: (v: string) => void; | |
| } | |
| interface FormData { | |
| @@ -46,6 +49,8 @@ export const PurchaseOrderFinalizationDialog: React.FC< | |
| description = "Select the actions you'd like to perform after finalizing this purchase order:", | |
| isLoading = false, | |
| onActionComplete, | |
| + replyToEmail, | |
| + onReplyToEmailChange, | |
| }) => { | |
| // Create default values for the form | |
| const defaultValues = useMemo(() => { | |
| @@ -106,49 +111,78 @@ export const PurchaseOrderFinalizationDialog: React.FC< | |
| <DialogDescription>{description}</DialogDescription> | |
| </DialogHeader> | |
| - <form onSubmit={form.handleSubmit(onSubmit)}> | |
| - <div className="space-y-4 py-4"> | |
| - {options.map( | |
| - (option) => | |
| - option.visible !== false && ( | |
| - <div key={option.id} className="flex items-start gap-3"> | |
| - <Checkbox | |
| - id={option.id} | |
| - checked={ | |
| - form.watch(`selectedOptions.${option.id}`) || false | |
| - } | |
| - onCheckedChange={(checked) => { | |
| - form.setValue( | |
| - `selectedOptions.${option.id}`, | |
| - checked as boolean, | |
| - ); | |
| - }} | |
| - disabled={ | |
| - option.enabled === false || isLoading || isProcessing | |
| - } | |
| - /> | |
| - <div className="grid gap-1.5"> | |
| - <Label | |
| - htmlFor={option.id} | |
| - className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" | |
| - > | |
| - {option.label} | |
| - </Label> | |
| - <p className="text-sm text-muted-foreground"> | |
| - {option.description} | |
| - </p> | |
| - </div> | |
| - </div> | |
| - ), | |
| - )} | |
| + {isLoading ? ( | |
| + <div className="py-10 flex items-center justify-center"> | |
| + <Loader2 className="h-6 w-6 animate-spin" /> | |
| </div> | |
| + ) : ( | |
| + <form onSubmit={form.handleSubmit(onSubmit)}> | |
| + <div className="space-y-4 py-4"> | |
| + {onReplyToEmailChange && ( | |
| + <div className="grid gap-1.5"> | |
| + <Label | |
| + htmlFor="reply-to" | |
| + className="text-sm font-medium leading-none" | |
| + > | |
| + Reply-To Email | |
| + </Label> | |
| + <input | |
| + id="reply-to" | |
| + type="email" | |
| + value={replyToEmail} | |
| + onChange={(e) => onReplyToEmailChange(e.target.value)} | |
| + placeholder="[email protected]" | |
| + className="border rounded-md px-2 py-1 text-sm" | |
| + /> | |
| + <p className="text-xs text-muted-foreground"> | |
| + This is the email receipents will send to by default when | |
| + replying. You can update the default address in Settings → | |
| + Store Preferences → Purchase Order Email. | |
| + </p> | |
| + </div> | |
| + )} | |
| + {options.map( | |
| + (option) => | |
| + option.visible !== false && ( | |
| + <div key={option.id} className="flex items-start gap-3"> | |
| + <Checkbox | |
| + id={option.id} | |
| + checked={ | |
| + form.watch(`selectedOptions.${option.id}`) || false | |
| + } | |
| + onCheckedChange={(checked) => { | |
| + form.setValue( | |
| + `selectedOptions.${option.id}`, | |
| + checked as boolean, | |
| + ); | |
| + }} | |
| + disabled={ | |
| + option.enabled === false || isLoading || isProcessing | |
| + } | |
| + /> | |
| + <div className="grid gap-1.5"> | |
| + <Label | |
| + htmlFor={option.id} | |
| + className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" | |
| + > | |
| + {option.label} | |
| + </Label> | |
| + <p className="text-sm text-muted-foreground"> | |
| + {option.description} | |
| + </p> | |
| + </div> | |
| + </div> | |
| + ), | |
| + )} | |
| + </div> | |
| - <DialogFooter> | |
| - <Button type="submit" disabled={isLoading || isProcessing}> | |
| - {isProcessing ? "Processing..." : "Confirm"} | |
| - </Button> | |
| - </DialogFooter> | |
| - </form> | |
| + <DialogFooter> | |
| + <Button type="submit" disabled={isLoading || isProcessing}> | |
| + {isProcessing ? "Processing..." : "Confirm"} | |
| + </Button> | |
| + </DialogFooter> | |
| + </form> | |
| + )} | |
| </DialogContent> | |
| </Dialog> | |
| ); | |
| diff --git a/src/components/po/EmailPOButton.tsx b/src/components/po/EmailPOButton.tsx | |
| index 5bbd79de1..603a64641 100644 | |
| --- a/src/components/po/EmailPOButton.tsx | |
| +++ b/src/components/po/EmailPOButton.tsx | |
| @@ -7,12 +7,15 @@ import { flushSync } from "react-dom"; | |
| import { FaEnvelope } from "react-icons/fa6"; | |
| import PrintPO, { PrintPOProps } from "./PrintPO"; | |
| -export type EmailPOButtonProps = PrintPOProps & {}; | |
| +export type EmailPOButtonProps = PrintPOProps & { | |
| + promptForReplyTo?: (cb: (email: string) => Promise<void>) => void; | |
| +}; | |
| export const EmailPOButton: React.FC<EmailPOButtonProps> = ({ ...props }) => { | |
| // conditionally render the content otherwise the page gets really laggy | |
| const [show, setShow] = useState(false); | |
| const [isLoading, setIsLoading] = useState(false); | |
| + const { promptForReplyTo } = props; | |
| const { | |
| isEmailPending, | |
| printViewContentRef, | |
| @@ -21,21 +24,24 @@ export const EmailPOButton: React.FC<EmailPOButtonProps> = ({ ...props }) => { | |
| onDismissEmailResponse, | |
| } = useEmailPurchaseOrder(props.purchaseOrder?.metadata.id ?? 0); | |
| - const handleRenderAndEmail = useCallback(async () => { | |
| - flushSync(() => { | |
| - setIsLoading(true); | |
| - setShow(true); | |
| - }); | |
| - try { | |
| - await handleEmailPurchaseOrder(); | |
| - } catch (error) { | |
| - console.error(error); | |
| - captureException(error); | |
| - } finally { | |
| - setIsLoading(false); | |
| - setShow(false); | |
| - } | |
| - }, [handleEmailPurchaseOrder]); | |
| + const handleRenderAndEmail = useCallback( | |
| + async (opts?: { replyTo?: string }) => { | |
| + flushSync(() => { | |
| + setIsLoading(true); | |
| + setShow(true); | |
| + }); | |
| + try { | |
| + await handleEmailPurchaseOrder(opts); | |
| + } catch (error) { | |
| + console.error(error); | |
| + captureException(error); | |
| + } finally { | |
| + setIsLoading(false); | |
| + setShow(false); | |
| + } | |
| + }, | |
| + [handleEmailPurchaseOrder], | |
| + ); | |
| return ( | |
| <> | |
| @@ -44,7 +50,11 @@ export const EmailPOButton: React.FC<EmailPOButtonProps> = ({ ...props }) => { | |
| colorScheme="blue" | |
| leftIcon={<FaEnvelope />} | |
| isLoading={isLoading || isEmailPending} | |
| - onClick={handleRenderAndEmail} | |
| + onClick={() => | |
| + promptForReplyTo?.(async (email) => { | |
| + await handleRenderAndEmail({ replyTo: email }); | |
| + }) | |
| + } | |
| > | |
| </Button> | |
| diff --git a/src/components/po/PurchaseOrderItemCard.tsx b/src/components/po/PurchaseOrderItemCard.tsx | |
| index ebdf17601..d6a2ce8f8 100644 | |
| --- a/src/components/po/PurchaseOrderItemCard.tsx | |
| +++ b/src/components/po/PurchaseOrderItemCard.tsx | |
| @@ -88,6 +88,7 @@ export type PurchaseOrderItemCardProps = CardProps & { | |
| hideTransfers?: boolean; | |
| transferStatus?: PredicateState; | |
| metadata?: PurchaseOrderEntityDb["metadata"]; | |
| + promptForReplyTo?: (cb: (email: string) => Promise<void>) => void; | |
| }; | |
| const columnHelper = createColumnHelper<CandidatePurchaseOrderItem>(); | |
| @@ -108,6 +109,7 @@ export const PurchaseOrderItemCard: React.FC<PurchaseOrderItemCardProps> = ({ | |
| hideTransfers, | |
| transferStatus, | |
| metadata, | |
| + promptForReplyTo, | |
| ...props | |
| }) => { | |
| const [showNotes, setShowNotes] = useState(false); | |
| @@ -732,6 +734,7 @@ export const PurchaseOrderItemCard: React.FC<PurchaseOrderItemCardProps> = ({ | |
| <ButtonGroup> | |
| {poStatus === PurchaseOrderStatusEnum.PURCHASED && ( | |
| <EmailPOButton | |
| + promptForReplyTo={promptForReplyTo} | |
| purchaseOrder={purchaseOrder} | |
| distributor={row.original.cohortVendorId} | |
| entityId={entityId} | |
| diff --git a/src/components/po/ReplyToPrompt.tsx b/src/components/po/ReplyToPrompt.tsx | |
| new file mode 100644 | |
| index 000000000..c1d613058 | |
| --- /dev/null | |
| +++ b/src/components/po/ReplyToPrompt.tsx | |
| @@ -0,0 +1,83 @@ | |
| +import { | |
| + Modal, | |
| + ModalOverlay, | |
| + ModalContent, | |
| + ModalHeader, | |
| + ModalBody, | |
| + ModalFooter, | |
| + Button, | |
| + FormControl, | |
| + FormLabel, | |
| + Input, | |
| + Spinner, | |
| + Center, | |
| +} from "@chakra-ui/react"; | |
| +import { useEffect, useState } from "react"; | |
| + | |
| +type ReplyToPromptProps = { | |
| + isOpen: boolean; | |
| + onSubmit: (email: string) => void; | |
| + onCancel: () => void; | |
| + defaultEmail: string; | |
| + isLoading?: boolean; | |
| +}; | |
| + | |
| +export const ReplyToPrompt: React.FC<ReplyToPromptProps> = ({ | |
| + isOpen, | |
| + onSubmit, | |
| + onCancel, | |
| + defaultEmail, | |
| + isLoading = false, | |
| +}) => { | |
| + const [email, setEmail] = useState(defaultEmail); | |
| + | |
| + useEffect(() => { | |
| + if (isOpen) setEmail(defaultEmail); | |
| + }, [isOpen, defaultEmail]); | |
| + | |
| + const handleSubmit = () => onSubmit(email.trim()); | |
| + | |
| + return ( | |
| + <Modal | |
| + isOpen={isOpen} | |
| + onClose={onCancel} | |
| + onCloseComplete={() => setEmail(defaultEmail ?? "")} | |
| + isCentered | |
| + > | |
| + <ModalOverlay /> | |
| + <ModalContent> | |
| + <ModalHeader>Reply-To Email</ModalHeader> | |
| + <ModalBody> | |
| + {isLoading ? ( | |
| + <Center py={10}> | |
| + <Spinner size="lg" /> | |
| + </Center> | |
| + ) : ( | |
| + <> | |
| + <FormControl> | |
| + <FormLabel>Reply-To address</FormLabel> | |
| + <Input | |
| + type="email" | |
| + value={email} | |
| + onChange={(e) => setEmail(e.target.value)} | |
| + /> | |
| + </FormControl> | |
| + <p className="text-xs text-gray-500 mt-2"> | |
| + You can change the default reply-to email in Settings → Store | |
| + Preferences → Purchase Order Reply-To Email. | |
| + </p> | |
| + </> | |
| + )} | |
| + </ModalBody> | |
| + <ModalFooter gap={2}> | |
| + <Button variant="ghost" onClick={onCancel}> | |
| + Cancel | |
| + </Button> | |
| + <Button colorScheme="blue" onClick={handleSubmit}> | |
| + Send | |
| + </Button> | |
| + </ModalFooter> | |
| + </ModalContent> | |
| + </Modal> | |
| + ); | |
| +}; | |
| diff --git a/src/context/Sales/SalesContextProvider.tsx b/src/context/Sales/SalesContextProvider.tsx | |
| index 89c6c39dc..8518a921d 100644 | |
| --- a/src/context/Sales/SalesContextProvider.tsx | |
| +++ b/src/context/Sales/SalesContextProvider.tsx | |
| @@ -389,7 +389,6 @@ export const SalesContextProvider: React.FC<SalesContextProviderProps> = ({ | |
| if (!register.isRegisterOpen) return; | |
| if (!itemsUndefined) return; | |
| if (!isPaymentMethodsFetched) return; | |
| - if (transactionStoreLoading) return; | |
| setItems(new Map()); | |
| const t = async () => { | |
| try { | |
| @@ -493,7 +492,6 @@ export const SalesContextProvider: React.FC<SalesContextProviderProps> = ({ | |
| toast, | |
| transactionExternalId, | |
| transactionId, | |
| - transactionStoreLoading, | |
| ]); | |
| // State | |
| diff --git a/src/pages/PurchaseOrders/PurchaseOrderDetails.tsx b/src/pages/PurchaseOrders/PurchaseOrderDetails.tsx | |
| index e30785d71..dfbc6e0f8 100644 | |
| --- a/src/pages/PurchaseOrders/PurchaseOrderDetails.tsx | |
| +++ b/src/pages/PurchaseOrders/PurchaseOrderDetails.tsx | |
| @@ -62,6 +62,7 @@ import { DefaultLayoutWithNavbar } from "../../components/layouts/DefaultLayoutW | |
| import { PurchaseOrderCandidateItems } from "../../components/po/PurchaseOrderCandidateItems"; | |
| import { PurchaseOrderItemCard } from "../../components/po/PurchaseOrderItemCard"; | |
| import { PurchaseOrderStatusTag } from "../../components/po/PurchaseOrderStatusTag"; | |
| +import { useAuthorization } from "context/AuthorizationContext/useAuthorizationContext"; | |
| import { | |
| CandidatePurchaseOrderItem, | |
| CohortVendorId, | |
| @@ -74,6 +75,7 @@ import { | |
| useModifyPurchaseOrderItemBulk, | |
| usePatchPurchaseOrder, | |
| } from "../../generated"; | |
| +import { ReplyToPrompt } from "components/po/ReplyToPrompt"; | |
| import { useAllVendors } from "../../hooks/useAllVendors"; | |
| import { useHybridUserConfiguration } from "../../hooks/useHybridUserConfiguration"; | |
| @@ -91,12 +93,24 @@ const PurchaseOrderDetails: React.FC<PurchaseOrderDetailsProps> = ({ | |
| setQuery, | |
| }) => { | |
| const [currentEntity, setCurrentEntity] = useState(0); | |
| + const { authorizedUser } = useAuthorization(); | |
| const { id } = useParams(); | |
| const { edit: isEditing } = query; | |
| const { value: SHOULD_SHOW_VENDOR_PRICING_CARD } = useEntityConfig( | |
| EntityConfigKey.SHOW_VENDOR_PRICING_CARD, | |
| true, | |
| ); | |
| + const replyToPrompt = useDisclosure(); | |
| + const [replyToCallback, setReplyToCallback] = useState< | |
| + ((email: string) => Promise<void>) | null | |
| + >(null); | |
| + const promptForReplyTo = useCallback( | |
| + (cb: (email: string) => Promise<void>) => { | |
| + setReplyToCallback(() => cb); | |
| + replyToPrompt.onOpen(); | |
| + }, | |
| + [replyToPrompt], | |
| + ); | |
| const idAsNumber = id ? parseInt(id) : 0; | |
| const queryClient = useQueryClient(); | |
| const toast = useToast(); | |
| @@ -340,6 +354,14 @@ const PurchaseOrderDetails: React.FC<PurchaseOrderDetailsProps> = ({ | |
| ]); | |
| const activeEntity = currentEntity || tabs[0]?.id; | |
| + const { value: replyToDefault, isFetching: isReplyToFetching } = | |
| + useEntityConfig<string>( | |
| + EntityConfigKey.PURCHASE_ORDER_REPLY_TO_EMAIL, | |
| + authorizedUser?.user?.email ?? "", | |
| + activeEntity, | |
| + ); | |
| + | |
| + const [replyToEmail, setReplyToEmail] = useState(replyToDefault ?? ""); | |
| const currentItemsByVendor = useMemo( | |
| // @ts-expect-error | |
| @@ -507,6 +529,7 @@ const PurchaseOrderDetails: React.FC<PurchaseOrderDetailsProps> = ({ | |
| }); | |
| // Then show the dialog for additional actions | |
| + setReplyToEmail(replyToDefault ?? ""); | |
| finalizationDialog.onOpen(); | |
| } catch (error) { | |
| toast({ title: "Failed to finalize PO", status: "error" }); | |
| @@ -521,6 +544,7 @@ const PurchaseOrderDetails: React.FC<PurchaseOrderDetailsProps> = ({ | |
| finalizationDialog, | |
| checkVendorValidity, | |
| vendorReviewDisclosure, | |
| + replyToDefault, | |
| ]); | |
| const handleCreateTransfers = useCallback(async () => { | |
| @@ -607,6 +631,7 @@ const PurchaseOrderDetails: React.FC<PurchaseOrderDetailsProps> = ({ | |
| }); | |
| // Then show the dialog for additional actions | |
| + setReplyToEmail(replyToDefault ?? ""); | |
| finalizationDialog.onOpen(); | |
| }, | |
| [ | |
| @@ -617,6 +642,7 @@ const PurchaseOrderDetails: React.FC<PurchaseOrderDetailsProps> = ({ | |
| idAsNumber, | |
| updatePurchaseOrder, | |
| finalizationDialog, | |
| + replyToDefault, | |
| ], | |
| ); | |
| @@ -758,7 +784,14 @@ const PurchaseOrderDetails: React.FC<PurchaseOrderDetailsProps> = ({ | |
| isUpdatePending || | |
| isFetchingPurchaseOrder | |
| } | |
| - onClick={handleEmailPurchaseOrder} | |
| + onClick={() => { | |
| + setReplyToCallback(() => async (email: string) => { | |
| + await handleEmailPurchaseOrder({ | |
| + replyTo: email.trim(), | |
| + }); | |
| + }); | |
| + replyToPrompt.onOpen(); | |
| + }} | |
| > | |
| Email POs | |
| </Button> | |
| @@ -805,6 +838,20 @@ const PurchaseOrderDetails: React.FC<PurchaseOrderDetailsProps> = ({ | |
| </Button> | |
| </> | |
| )} | |
| + <ReplyToPrompt | |
| + isOpen={replyToPrompt.isOpen} | |
| + onCancel={() => { | |
| + setReplyToCallback(null); | |
| + replyToPrompt.onClose(); | |
| + }} | |
| + onSubmit={async (email) => { | |
| + replyToPrompt.onClose(); | |
| + setReplyToCallback(null); | |
| + if (replyToCallback) await replyToCallback(email); | |
| + }} | |
| + defaultEmail={replyToDefault} | |
| + isLoading={isReplyToFetching} | |
| + /> | |
| <ConfirmationDialog | |
| title="Caution: Complete PO Without Receiving" | |
| description="This will mark all items across all locations as received. Is this what you'd like to do?" | |
| @@ -897,6 +944,7 @@ const PurchaseOrderDetails: React.FC<PurchaseOrderDetailsProps> = ({ | |
| </div> | |
| )} | |
| <PurchaseOrderItemCard | |
| + promptForReplyTo={promptForReplyTo} | |
| title={ | |
| allPossibleEntities.find((e) => activeEntity === e.id) | |
| ?.name | |
| @@ -997,12 +1045,13 @@ const PurchaseOrderDetails: React.FC<PurchaseOrderDetailsProps> = ({ | |
| { | |
| id: "send-emails", | |
| label: "Send Emails", | |
| - description: | |
| - "Send purchase order emails to vendors with contact information", | |
| + description: `Send purchase order emails to vendors with contact information.`, | |
| enabled: true, | |
| action: async () => { | |
| // Then send emails | |
| - await handleEmailPurchaseOrder(); | |
| + await handleEmailPurchaseOrder({ | |
| + replyTo: replyToEmail.trim(), | |
| + }); | |
| toast({ | |
| title: "Emails sent", | |
| status: "success", | |
| @@ -1015,8 +1064,13 @@ const PurchaseOrderDetails: React.FC<PurchaseOrderDetailsProps> = ({ | |
| }, | |
| ]} | |
| isLoading={ | |
| - isTransferCreationPending || isEmailPending || isUpdatePending | |
| + isReplyToFetching || | |
| + isTransferCreationPending || | |
| + isEmailPending || | |
| + isUpdatePending | |
| } | |
| + replyToEmail={replyToEmail} | |
| + onReplyToEmailChange={setReplyToEmail} | |
| /> | |
| <ConfirmationDialog | |
| title="Warning: Duplicate Transfers" | |
| diff --git a/src/pages/PurchaseOrders/useEmailPurchaseOrder.tsx b/src/pages/PurchaseOrders/useEmailPurchaseOrder.tsx | |
| index 47d8f5361..85b4c830b 100644 | |
| --- a/src/pages/PurchaseOrders/useEmailPurchaseOrder.tsx | |
| +++ b/src/pages/PurchaseOrders/useEmailPurchaseOrder.tsx | |
| @@ -36,46 +36,51 @@ export const useEmailPurchaseOrder = (purchaseOrderId: number) => { | |
| }); | |
| const printViewContentRef = useRef<HTMLDivElement>(null); | |
| - const handleEmailPurchaseOrder = useCallback(async () => { | |
| - if (!printViewContentRef.current) { | |
| - return; | |
| - } | |
| + const handleEmailPurchaseOrder = useCallback( | |
| + async (opts?: { replyTo?: string }) => { | |
| + if (!printViewContentRef.current) { | |
| + return; | |
| + } | |
| - setPending(true); | |
| + setPending(true); | |
| - const purchaseOrderPrintViews: HTMLElement[] = Array.from( | |
| - printViewContentRef.current.children[0]?.children ?? [], | |
| - ).map((child) => child as HTMLElement); | |
| + const purchaseOrderPrintViews: HTMLElement[] = Array.from( | |
| + printViewContentRef.current.children[0]?.children ?? [], | |
| + ).map((child) => child as HTMLElement); | |
| - const cleanupFromPrint = initializeDocumentForPrint( | |
| - printViewContentRef.current, | |
| - ); | |
| + const cleanupFromPrint = initializeDocumentForPrint( | |
| + printViewContentRef.current, | |
| + ); | |
| - try { | |
| - const formData = new FormData(); | |
| - await Promise.all( | |
| - purchaseOrderPrintViews.map( | |
| - async (purchaseOrderPrintView: HTMLElement) => { | |
| - const entityId = purchaseOrderPrintView.dataset.entityId!!; | |
| - const vendorId = purchaseOrderPrintView.dataset.vendorId!!; | |
| + try { | |
| + const formData = new FormData(); | |
| + const replyToEmail = (opts?.replyTo ?? "").trim(); | |
| + formData.append("replyToEmail", replyToEmail); | |
| + await Promise.all( | |
| + purchaseOrderPrintViews.map( | |
| + async (purchaseOrderPrintView: HTMLElement) => { | |
| + const entityId = purchaseOrderPrintView.dataset.entityId!!; | |
| + const vendorId = purchaseOrderPrintView.dataset.vendorId!!; | |
| - const pdf = await generateMultiPagePDFFromHTMLElement( | |
| - purchaseOrderPrintView, | |
| - ); | |
| + const pdf = await generateMultiPagePDFFromHTMLElement( | |
| + purchaseOrderPrintView, | |
| + ); | |
| - formData.append("vendorIds", vendorId); | |
| - formData.append("entityIds", entityId); | |
| - formData.append("files", pdf.output("blob")); | |
| - }, | |
| - ), | |
| - ); | |
| + formData.append("vendorIds", vendorId); | |
| + formData.append("entityIds", entityId); | |
| + formData.append("files", pdf.output("blob")); | |
| + }, | |
| + ), | |
| + ); | |
| - await emailPurchaseOrder(formData); | |
| - } finally { | |
| - cleanupFromPrint(); | |
| - setPending(false); | |
| - } | |
| - }, [emailPurchaseOrder]); | |
| + await emailPurchaseOrder(formData); | |
| + } finally { | |
| + cleanupFromPrint(); | |
| + setPending(false); | |
| + } | |
| + }, | |
| + [emailPurchaseOrder], | |
| + ); | |
| return { | |
| printViewContentRef, | |
| diff --git a/src/pages/Settings/EntityConfiguration/EntityConfiguationPanel.tsx b/src/pages/Settings/EntityConfiguration/EntityConfiguationPanel.tsx | |
| index 7f034f359..ec944f423 100644 | |
| --- a/src/pages/Settings/EntityConfiguration/EntityConfiguationPanel.tsx | |
| +++ b/src/pages/Settings/EntityConfiguration/EntityConfiguationPanel.tsx | |
| @@ -53,7 +53,6 @@ type EntityConfigKeyWOFeeReport = Exclude< | |
| | EntityConfigKey.CUSTOMER_FACING_DISPLAY_LOGO | |
| | EntityConfigKey.TERMINAL_SCREEN_LOGO | |
| | EntityConfigKey.ENABLE_SALE_HOTKEYS | |
| - | EntityConfigKey.PURCHASE_ORDER_REPLY_TO_EMAIL | |
| >; | |
| const entityConfigMap: Record< | |
| @@ -77,6 +76,11 @@ const entityConfigMap: Record< | |
| label: "One Click Receipt Printing", | |
| defaultValue: false, | |
| }, | |
| + [EntityConfigKey.PURCHASE_ORDER_REPLY_TO_EMAIL]: { | |
| + type: z.string().email("Please enter a valid email").optional(), | |
| + label: "Purchase Order Reply-To Email", | |
| + defaultValue: "", | |
| + }, | |
| [EntityConfigKey.LABEL_PRICE_AT_TOP]: { | |
| type: z.boolean().optional(), | |
| label: "Label Price at Top", |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment