\n) => () => {\n props.onRequestInvoice!(props.data.invoice, requestInvoiceButtonReference);\n};\n\nconst getDesktopRowCells = (props: IInvoicesTableRowProps, onSelectLine: () => void) => {\n const {\n data: { invoice, customer }\n } = props;\n\n const isCurrentUser =\n !StringExtensions.isNullOrWhitespace(customer.AccountNumber) && customer.AccountNumber === invoice.customerAccountNumber;\n\n const linkToInvoiceDetails = getInvoiceDetailsPageUrlSync(props.data.invoice.id, props.context.actionContext);\n const invoiceRowArialabel = format(props.resources.invoiceListSelectRadioAriaLabelText, invoice.id);\n return (\n <>\n \n \n | \n \n \n {invoice.id}\n \n | \n \n {formatDate(props.context, invoice.invoiceDate)}\n | \n \n {invoice.customerName}\n {isCurrentUser ? {props.resources.invoiceCurrentUser} : null}\n | \n {formatAmount(props.context, invoice.amount, invoice.currencyCode)} | \n \n {formatDate(props.context, invoice.dueDate)}\n | \n {formatAmount(props.context, invoice.amountDue, invoice.currencyCode)} | \n \n {getInvoiceStatusText(props.resources, invoice.status)}\n | \n {extraActionsPopup(props)} | \n >\n );\n};\n\nlet setShowPreviousActions: React.Dispatch> | undefined;\n\n/**\n * On Click view details function.\n * @param linkToInvoiceDetails -Link to invoice details.\n * @returns Go to invoice details.\n */\nconst onClickViewDetails = (linkToInvoiceDetails: string) => () => {\n if (MsDyn365.isBrowser) {\n window.location.href = linkToInvoiceDetails;\n }\n return;\n};\n\n/**\n * Toggle function.\n * @param isShowActions -Link to invoice details.\n * @param setShowActions -Link to invoice details.\n * @returns Go to invoice details.\n */\nconst toggle = (isShowActions: boolean, setShowActions: React.Dispatch>) => () => {\n const shouldShowActions = !isShowActions;\n if (shouldShowActions) {\n if (setShowPreviousActions) {\n setShowPreviousActions(false);\n }\n setShowActions(shouldShowActions);\n setShowPreviousActions = setShowActions;\n } else {\n setShowPreviousActions = undefined;\n setShowActions(shouldShowActions);\n }\n};\n\nconst extraActionsPopup = (props: IInvoicesTableRowProps): JSX.Element | null => {\n const {\n resources: {\n invoiceViewDetailsButtonAriaLabel,\n invoiceViewDetailsButtonText,\n invoiceActionsButtonTextAriaLabel,\n invoiceActionsButtonIsOnBehalfOfTextAriaLabel\n },\n data: { index }\n } = props;\n\n const [showActions, setShowActions] = useState(false);\n const isOnBehalfOfRequest = isOboRequest(props.context.request);\n\n const linkToInvoiceDetails = getInvoiceDetailsPageUrlSync(props.data.invoice.id, props.context.actionContext);\n\n const className = `${props.className}__extra-actions-cell`;\n const actionsContainerClassName = `${className}__actions-container`;\n const payInvoiceClassName = `${actionsContainerClassName}__pay-invoice`;\n return (\n \n
\n {showActions && !isOnBehalfOfRequest && (\n
\n
\n
\n
\n
\n )}\n
\n );\n};\n\nconst getMobileRowCells = (props: IInvoicesTableRowProps, onSelectLine: () => void) => {\n const {\n data: { invoice }\n } = props;\n\n const linkToInvoiceDetails = getInvoiceDetailsPageUrlSync(props.data.invoice.id, props.context.actionContext);\n const invoiceRowArialabel = format(props.resources.invoiceListSelectRadioAriaLabelText, invoice.id);\n\n return (\n <>\n \n \n | \n \n \n {invoice.id}\n \n \n {invoice.customerName}\n | \n \n {formatDate(props.context, invoice.dueDate)}\n \n {getInvoiceStatusText(props.resources, invoice.status)}\n | \n {extraActionsPopup(props)} | \n >\n );\n};\n\nconst InvoicesTableRow: React.FC = (props: IInvoicesTableRowProps): JSX.Element => {\n const {\n data: { invoice }\n } = props;\n\n const onSelectLine = () => {\n invoice.isSelected = !invoice.isSelected;\n props.onChange();\n };\n\n let rowCells: JSX.Element;\n if (props.isMobile) {\n rowCells = getMobileRowCells(props, onSelectLine);\n } else {\n rowCells = getDesktopRowCells(props, onSelectLine);\n }\n\n return {rowCells}
;\n};\n\nexport const InvoicesTableRowComponent: React.FunctionComponent = msdyn365Commerce.createComponent<\n // @ts-expect-error\n IInvoicesTableRowComponent\n>('InvoicesTableRowComponent', { component: InvoicesTableRow });\n","/*--------------------------------------------------------------\r\n * Copyright (c) Microsoft Corporation. All rights reserved.\r\n * See License.txt in the project root for license information.\r\n *--------------------------------------------------------------*/\r\n\r\nimport { Module, Node } from '@msdyn365-commerce-modules/utilities';\r\nimport * as React from 'react';\r\n\r\nimport { ICheckoutCustomerAccountPaymentViewProps } from '@msdyn365-commerce-modules/checkout/src/modules/checkout-customer-account-payment/./checkout-customer-account-payment';\r\nimport { IAccountPaymentEditViewForm } from '@msdyn365-commerce-modules/checkout/src/modules/checkout-customer-account-payment/./components/get-account-payment-form-edit-mode';\r\nimport { IAccountPaymentSummaryViewForm } from '@msdyn365-commerce-modules/checkout/src/modules/checkout-customer-account-payment/./components/get-account-payment-form-summary-mode';\r\nimport { ICheckoutCustomerAccountPaymentProps as ICheckoutCustomerAccountPaymentExtensionProps } from '../definition-extensions/checkout-customer-account-payment.ext.props.autogenerated';\r\n\r\nimport { PaymentContext, IPaymentTenderType } from '../../../modules/payment-container/context/payment-context';\r\n// import { CustomerBalances } from '@msdyn365-commerce/retail-proxy';\r\nimport MsDyn365 from \"@msdyn365-commerce/core\";\r\n\r\nexport const SummaryForm: React.FC = ({ formProps, label, paymentAmount, addPaymentButton, appliedLine, bottomBorder }) => (\r\n \r\n <>\r\n {label}\r\n {appliedLine}\r\n {bottomBorder}\r\n >\r\n \r\n);\r\n\r\nexport const EditForm: React.FC = ({ formProps, inputLabel, inputAmount, customerName, customerAccountNumber, addPaymentButton, customerSince, accountCredit, alert, accountDetails, appliedLine, bottomBorder }, props) => {\r\n return (\r\n \r\n <>\r\n\r\n {customerName}\r\n {customerAccountNumber}\r\n {customerSince}\r\n {/* {accountCredit} */}\r\n {/* {accountDetails} */}\r\n {/* {inputLabel} */}\r\n {getInputLabel()}\r\n {/* {alert} */}\r\n {appliedLine}\r\n {bottomBorder}\r\n >\r\n \r\n)};\r\n\r\nlet customInputLabel = '';\r\n\r\nconst getInputLabel = () => {\r\n return (\r\n \r\n {customInputLabel}\r\n \r\n );\r\n}\r\n\r\n// const getAvailableCredit = (creditBalances: CustomerBalances | undefined) => {\r\n// let availableCredit = 0;\r\n\r\n// if (!creditBalances) {\r\n// return 0;\r\n// }\r\n\r\n// if (creditBalances.InvoiceAccountCreditLimit === 0) {\r\n// availableCredit = creditBalances.CreditLimit - creditBalances.Balance - creditBalances.PendingBalance;\r\n// } else {\r\n// availableCredit = creditBalances.InvoiceAccountCreditLimit - creditBalances.InvoiceAccountBalance - creditBalances.InvoiceAccountPendingBalance;\r\n// }\r\n\r\n// return availableCredit;\r\n// };\r\n\r\nconst addPayment = async (props: ICheckoutCustomerAccountPaymentViewProps): Promise => {\r\n const checkoutState = props.data.checkout.result;\r\n\r\n if (!checkoutState) {\r\n props.context.telemetry.error('checkout state not found');\r\n return;\r\n }\r\n\r\n // const availableCredit = getAvailableCredit(props.data.creditBalances?.result);\r\n const cartTotal = (await props.data.checkout).checkoutCart.cart.TotalAmount;\r\n\r\n // console.log(availableCredit);\r\n // console.log(cartTotal);\r\n // if (cartTotal && availableCredit >= cartTotal) {\r\n if (cartTotal) {\r\n await checkoutState.updateCustomerAccountAmount({ newAmount: cartTotal });\r\n props.context.telemetry.information('customer account payment amount updated');\r\n }\r\n};\r\n\r\nconst CheckoutCustomerAccountView: React.FC> = props => {\r\n const { checkoutCustomerAccount, summaryView, editView, moduleState, id, config: { tenderType } } = props;\r\n \r\n const paymentContext = React.useContext(PaymentContext);\r\n const paymentTenderTypes: IPaymentTenderType[] = [];\r\n // console.log(props);\r\n\r\n React.useEffect(() => {\r\n tenderType?.map(tt => {\r\n paymentTenderTypes.push({ tenderTypeId: tt.tenderId, tenderName: tt.tenderName });\r\n });\r\n paymentContext.registerPaymentMethod(id, paymentTenderTypes);\r\n sessionStorage.removeItem('ON_ACCOUNT_APPLIED');\r\n customInputLabel = props.config.processedOnAccountText || '';\r\n }, []);\r\n\r\n if (MsDyn365.isBrowser) {\r\n document.querySelector('.payment-selector__button-container--on-account')?.addEventListener('click', () => {\r\n if (sessionStorage.getItem('ON_ACCOUNT_APPLIED') === '1') {\r\n return;\r\n }\r\n sessionStorage.setItem('ON_ACCOUNT_APPLIED', '1');\r\n addPayment(props);\r\n });\r\n }\r\n\r\n return (\r\n \r\n {moduleState.isReady && summaryView && }\r\n {!moduleState.isReady && editView && }\r\n \r\n );\r\n};\r\n\r\nexport default CheckoutCustomerAccountView;\r\n","/*!\n * Copyright (c) Microsoft Corporation.\n * All rights reserved. See LICENSE in the project root for license information.\n */\n\nimport { InvoicePaidStatus, OrderInvoice } from '@msdyn365-commerce/retail-proxy';\n\nexport interface IPayableInvoice {\n readonly id: string;\n readonly canBePaid: boolean;\n}\n\n/**\n * Represents invoice data model.\n */\nexport class InvoiceModel implements IPayableInvoice {\n public id: string;\n\n public invoiceDate?: Date;\n\n public customerAccountNumber?: string;\n\n public customerName?: string;\n\n public amount?: number;\n\n public dueDate?: Date;\n\n public amountDue?: number;\n\n public status?: InvoicePaidStatus;\n\n public currencyCode?: string;\n\n public isSelected: boolean;\n\n public constructor(invoice: OrderInvoice) {\n this.id = invoice.Id || '';\n this.invoiceDate = invoice.InvoiceDate ? new Date(invoice.InvoiceDate) : undefined;\n this.customerAccountNumber = invoice.CustomerAccountNumber;\n this.customerName = invoice.CustomerName;\n this.amount = invoice.TotalAmount;\n this.dueDate = invoice.InvoiceDueDate ? new Date(invoice.InvoiceDueDate) : undefined;\n this.amountDue = invoice.AmountBalance;\n this.status = invoice.InvoicePaidStatusValue;\n this.currencyCode = invoice.CurrencyCode;\n\n this.isSelected = false;\n }\n\n public get canBePaid(): boolean {\n return this.status !== InvoicePaidStatus.Paid;\n }\n}\n","/*!\n * Copyright (c) Microsoft Corporation.\n * All rights reserved. See LICENSE in the project root for license information.\n */\n\nimport { InvoicePaidStatus } from '@msdyn365-commerce/retail-proxy';\n\nexport interface IInvoiceStatusResources {\n invoiceStatusNone: string;\n invoiceStatusUnpaid: string;\n invoiceStatusPartiallyPaid: string;\n invoiceStatusPaid: string;\n}\n\nexport const getInvoiceStatusText = (resources: IInvoiceStatusResources, invoiceStatus: InvoicePaidStatus | undefined) => {\n switch (invoiceStatus) {\n case InvoicePaidStatus.None:\n return resources.invoiceStatusNone;\n\n case InvoicePaidStatus.Unpaid:\n return resources.invoiceStatusUnpaid;\n\n case InvoicePaidStatus.PartiallyPaid:\n return resources.invoiceStatusPartiallyPaid;\n\n case InvoicePaidStatus.Paid:\n return resources.invoiceStatusPaid;\n default:\n return '';\n }\n};\n","/*!\n * Copyright (c) Microsoft Corporation.\n * All rights reserved. See LICENSE in the project root for license information.\n */\n\nimport { Module, Node } from '@msdyn365-commerce-modules/utilities';\nimport * as React from 'react';\n\nimport { ITileListItemViewProps } from './tile-list-item';\n\n/**\n *\n * ITileListItemViewProps component.\n * @param props - Component props.\n * @returns TileListView.\n */\nconst renderTileListItemContent = (props: ITileListItemViewProps): JSX.Element => {\n const {\n heading,\n paragraph,\n backgroundImage,\n links,\n thumbnailImage,\n tileListContainer,\n tileListImageContainer,\n tileListThumbnailImageContainer,\n tileListHeadingContainer\n } = props;\n\n return (\n \n {backgroundImage}\n \n {heading}\n {paragraph}\n {links}\n \n {thumbnailImage}\n \n );\n};\n\n/**\n *\n * TileListItemViewProps component.\n * @param props - Component props.\n * @returns TileListView.\n */\nexport const TileListItemViewComponent: React.FC = props => {\n const { tileListItem } = props;\n return {renderTileListItemContent(props)};\n};\n\nexport default TileListItemViewComponent;\n","/*!\n * Copyright (c) Microsoft Corporation.\n * All rights reserved. See LICENSE in the project root for license information.\n */\n\nexport * from './invoice-data';\nexport * from './invoice-status';\nexport * from './pay-invoices';\n","/*!\r\n * Copyright (c) Microsoft Corporation.\r\n * All rights reserved. See LICENSE in the project root for license information.\r\n */\r\n\r\nimport * as React from 'react';\r\nimport { IOrderLevelCommentViewProps } from './order-level-comment';\r\nimport { Module, Node } from '@msdyn365-commerce-modules/utilities';\r\n\r\nexport default (props: IOrderLevelCommentViewProps) => {\r\n const { staticDisplay, orderLevelCommentModuleProps, modal } = props;\r\n return (\r\n \r\n {modal}\r\n \r\n \r\n );\r\n};\r\n","/*!\n * Copyright (c) Microsoft Corporation.\n * All rights reserved. See LICENSE in the project root for license information.\n */\n\nimport { IComponentProps, msdyn365Commerce } from '@msdyn365-commerce/core';\nimport { EnumExtensions } from '@msdyn365-commerce-modules/retail-actions';\nimport { Button, Popover } from '@msdyn365-commerce-modules/utilities';\nimport React from 'react';\n\nimport { IInvoiceStatusResources } from '../helpers/invoice-status';\n\nexport interface IInvoicesFilterResources extends IInvoiceStatusResources {\n invoicesFilterShowAll: string;\n}\n\nexport interface IInvoicesFilterProps extends IComponentProps {\n className: string;\n disabled: boolean;\n\n resources: IInvoicesFilterResources;\n\n currentState: InvoicesFilterState;\n onFilterStateChanged(state: InvoicesFilterState): void;\n}\n\nexport enum InvoicesFilterState {\n ShowAll,\n Paid,\n PartiallyPaid,\n Unpaid\n}\n\nconst getInvoicesFilterStateResources = (filterState: InvoicesFilterState, resources: IInvoicesFilterResources): string => {\n switch (filterState) {\n case InvoicesFilterState.ShowAll:\n return resources.invoicesFilterShowAll;\n case InvoicesFilterState.Paid:\n return resources.invoiceStatusPaid;\n case InvoicesFilterState.PartiallyPaid:\n return resources.invoiceStatusPartiallyPaid;\n case InvoicesFilterState.Unpaid:\n return resources.invoiceStatusUnpaid;\n default:\n throw new Error('Failed to retrieve resource description for unknown invoices filter state.');\n }\n};\n\n/**\n * On Click function.\n * @param onStateClick -Invoice filter state function.\n * @param filterState -Invoice filter state.\n * @returns Set Invoice filter state.\n */\nconst onClickHandler = (onStateClick: (filterState: InvoicesFilterState) => void, filterState: InvoicesFilterState) => () => {\n onStateClick(filterState);\n};\n\nconst renderFilterStateItem = (\n filterState: InvoicesFilterState,\n resources: IInvoicesFilterResources,\n className: string,\n onStateClick: (filterState: InvoicesFilterState) => void\n): React.ReactNode => {\n const stateStringRepresentation = InvoicesFilterState[filterState];\n\n return (\n \n );\n};\n\nconst InvoicesFilter: React.FC = (props: IInvoicesFilterProps) => {\n const popoverRef = React.createRef();\n const [popoverState, setPopoverState] = React.useState(false);\n const togglePopover = React.useCallback(() => {\n setPopoverState(!popoverState);\n }, [popoverState]);\n\n const [filterState, setFilterState] = React.useState(props.currentState);\n\n const onFilterStateClick = (clickedState: InvoicesFilterState) => {\n setFilterState(clickedState);\n setPopoverState(false);\n props.onFilterStateChanged(clickedState);\n };\n\n return (\n \n
\n
\n {EnumExtensions.getEnumValues(InvoicesFilterState).map(state => {\n return renderFilterStateItem(state, props.resources, props.className, onFilterStateClick);\n })}\n \n
\n );\n};\n\nexport const InvoicesFilterComponent: React.FunctionComponent = msdyn365Commerce.createComponent<\n // @ts-expect-error\n IInvoicesFilterProps\n>('InvoicesFilterComponent', { component: InvoicesFilter });\n","/*!\n * Copyright (c) Microsoft Corporation.\n * All rights reserved. See LICENSE in the project root for license information.\n */\n\nimport React from 'react';\nimport { KeyCodes } from '@msdyn365-commerce-modules/utilities';\n\nexport interface ISearchBarProps {\n className: string;\n searchPlaceholderLabel: string;\n onSearch(searchText: string): void;\n}\n\nexport interface ISearchBarState {\n searchText: string;\n}\n\nexport class SearchBar extends React.Component {\n constructor(props: ISearchBarProps) {\n super(props);\n this.state = {\n searchText: ''\n };\n }\n\n public render(): JSX.Element {\n const { className, searchPlaceholderLabel } = this.props;\n return (\n \n \n \n
\n );\n }\n\n private readonly onSearchTextChange = (ev: React.ChangeEvent): void => {\n this.setState({\n searchText: ev.target.value\n });\n };\n\n private readonly onSearch = (): void => {\n this.props.onSearch(this.state.searchText);\n };\n\n private readonly handleKeyUp = (event: React.KeyboardEvent): void => {\n if (event.keyCode === KeyCodes.Enter) {\n this.onSearch();\n }\n };\n}\n","/*!\n * Copyright (c) Microsoft Corporation.\n * All rights reserved. See LICENSE in the project root for license information.\n */\n\nimport { IComponentProps } from '@msdyn365-commerce/core';\nimport { CommerceException } from '@msdyn365-commerce/retail-proxy/dist/Entities/CommerceTypes.g';\nimport { ArrayExtensions } from '@msdyn365-commerce-modules/retail-actions';\nimport { Alert, Button } from '@msdyn365-commerce-modules/utilities';\nimport React from 'react';\n\nimport { moduleClassName } from '../business-partners';\nimport { IBusinessPartnersResources } from '../business-partners.props.autogenerated';\nimport { SearchBar } from './search-bar';\n\n/**\n * Props of DataGrid component.\n */\nexport interface IDataGridProps extends IComponentProps {\n className: string;\n pageSize?: number;\n searchPlaceholderLabel: string;\n defaultSorting?: ISortingInfo;\n resources: IBusinessPartnersResources;\n columns: IDataGridColumn[];\n loadData(): Promise;\n}\n\n/**\n * State of DataGrid component.\n */\nexport interface IDataGridState {\n data: Tdata[];\n paging: IPagingInfo;\n filter: IFilterInfo;\n sorting?: ISortingInfo;\n isLoading: boolean;\n errorMessage: string;\n}\n\nexport interface IPagingInfo {\n index: number;\n}\n\nexport interface IFilterInfo {\n searchText: string;\n}\n\nexport interface ISortingInfo {\n fieldName: string;\n ascending: boolean;\n}\n\n/**\n * Column definition of DataGrid component.\n */\nexport interface IDataGridColumn {\n /**\n * A unique key for identifying the column.\n */\n reactKey: string;\n\n /**\n * Name to render on the column header.\n */\n name: string;\n\n /**\n * Class name added to table cell.\n */\n className?: string;\n\n /**\n * The field to pull the text value from for the column.Can be unset if a custom `onRender` method is provided.\n */\n fieldName?: string;\n\n /**\n * Whether the user can search this column by text.\n */\n isSearchable?: boolean;\n\n /**\n * Custom renderer for cell content, instead of the default text rendering.\n */\n onRender?(item: Tdata): JSX.Element;\n\n /**\n * Event when the table cell is clicked.\n */\n onClick?(item: Tdata): void;\n}\n\nconst defaultPageSize = 10;\n\n/**\n * DataGrid component encapsulates common table functionalities.\n */\nexport class DataGrid extends React.Component, IDataGridState> {\n constructor(props: IDataGridProps) {\n super(props);\n this.state = {\n data: [],\n sorting: props.defaultSorting,\n paging: { index: 0 },\n filter: { searchText: '' },\n isLoading: true,\n errorMessage: ''\n };\n }\n\n public componentDidMount(): void {\n this.loadDataInternal();\n }\n\n public shouldComponentUpdate(nextProps: Readonly>, nextState: Readonly>): boolean {\n return (\n this.props.id !== nextProps.id ||\n this.props.columns !== nextProps.columns ||\n this.state.data !== nextState.data ||\n this.state.paging !== nextState.paging ||\n this.state.filter !== nextState.filter ||\n this.state.isLoading !== nextState.isLoading ||\n this.state.errorMessage !== nextState.errorMessage\n );\n }\n\n public render(): JSX.Element | null {\n const data = this.getDataToDisplay();\n const hasMore = this.hasMore();\n if (this.state.errorMessage) {\n return {this.state.errorMessage};\n }\n if (this.state.isLoading) {\n return {this.props.resources.loadingLabel};\n }\n if (!ArrayExtensions.hasElements(data)) {\n return (\n <>\n \n {this.props.resources.noDataMessage}
\n >\n );\n }\n return (\n <>\n \n \n {this.renderGridHeader()}\n {data.map((item, index) => this.renderGridRow(item, index))}\n
\n {hasMore && (\n \n \n
\n )}\n >\n );\n }\n\n /**\n * Renders the TH header in the table.\n * @returns - Header react node.\n */\n private readonly renderGridHeader = () => {\n const className = `${this.props.className}__header`;\n const rowClassName = `${className}__row`;\n return (\n \n \n {this.props.columns.map(column => (\n \n {column.name}\n | \n ))}\n
\n \n );\n };\n\n /**\n * Renders table row.\n * @param item - Data item for this row.\n * @param index - Index of the row.\n * @returns - Row react node.\n */\n private readonly renderGridRow = (item: Tdata, index: number) => {\n const rowClassName = `${this.props.className}__row`;\n return (\n \n {this.props.columns.map(column => {\n return (\n column.onClick?.(item)}>\n {column.fieldName ? (\n {((item as unknown) as { [key: string]: string })[column.fieldName]}\n ) : (\n column.onRender?.(item)\n )}\n | \n );\n })}\n
\n );\n };\n\n /**\n * Load data for the table.\n * @returns Promise.\n */\n private readonly loadDataInternal = async () => {\n if (!this.props.context.actionContext.requestContext.user.isAuthenticated) {\n this.setState({\n isLoading: false\n });\n return;\n }\n try {\n const result = await this.props.loadData();\n this.setState({\n isLoading: false,\n data: result\n });\n } catch (e) {\n this.setState({\n isLoading: false,\n errorMessage: (e as { data: CommerceException })?.data?.LocalizedMessage || this.props.resources.genericErrorMessage\n });\n }\n };\n\n private readonly onSearch = (searchText: string): void => {\n this.setState({\n paging: { index: 0 },\n filter: { searchText }\n });\n };\n\n private readonly loadMore = (): void => {\n this.setState({\n paging: { index: this.state.paging.index + 1 }\n });\n };\n\n private readonly sortData = (): Tdata[] => {\n const sorting = this.state.sorting;\n if (!sorting) {\n return this.state.data;\n }\n const { fieldName, ascending } = sorting;\n return this.state.data.sort((item1: Tdata, item2: Tdata) => {\n let value1 = ((item1 as unknown) as { [key: string]: string })[fieldName];\n let value2 = ((item2 as unknown) as { [key: string]: string })[fieldName];\n if (typeof value1 === 'string' && typeof value2 === 'string') {\n value1 = value1.toLowerCase();\n value2 = value2.toLowerCase();\n }\n const compareResult = value1 > value2 ? 1 : value1 < value2 ? -1 : 0;\n return ascending ? compareResult : compareResult * -1;\n });\n };\n\n private readonly filterData = (): Tdata[] => {\n const column = this.props.columns.find(c => c.isSearchable);\n const searchText = this.state.filter.searchText;\n return this.sortData().filter(item => {\n if (!column?.fieldName || !searchText) {\n return true;\n }\n const columnValue = ((item as unknown) as { [key: string]: string })[column.fieldName];\n return columnValue && columnValue.toLowerCase().indexOf(searchText.trim().toLowerCase()) >= 0;\n });\n };\n\n private readonly getDataToDisplay = (): Tdata[] => {\n const itemsCount = (this.props.pageSize || defaultPageSize) * (this.state.paging.index + 1);\n const filteredItems = this.filterData();\n return filteredItems.slice(0, itemsCount);\n };\n\n private readonly hasMore = (): boolean => {\n const itemsCount = (this.props.pageSize || defaultPageSize) * (this.state.paging.index + 1);\n const filteredItems = this.filterData();\n return filteredItems.length > itemsCount;\n };\n}\n","/*!\n * Copyright (c) Microsoft Corporation.\n * All rights reserved. See LICENSE in the project root for license information.\n */\n\nimport { IComponentProps } from '@msdyn365-commerce/core';\nimport { ObjectExtensions } from '@msdyn365-commerce-modules/retail-actions';\nimport React, { ReactNode } from 'react';\nimport { isMobile, Node, VariantType } from '@msdyn365-commerce-modules/utilities';\n\nimport { BusinessPartner, BusinessPartnerUser, BusinessPartnersDataActions } from '@msdyn365-commerce/retail-proxy';\nimport { IBusinessPartnersResources } from '../business-partners.props.autogenerated';\nimport { DataGrid, IDataGridColumn } from './data-grid';\n\n/**\n * View model of business partner user, used to display.\n */\nexport interface IBusinessPartnerUserViewModel extends BusinessPartnerUser {\n name: string;\n spendingLimit: ReactNode;\n}\n\n/**\n * Props of BusinessPartnerUsersTable.\n */\nexport interface IBusinessPartnerUsersTableProps extends IComponentProps {\n className: string;\n pageSize?: number;\n businessPartner: BusinessPartner;\n resources: IBusinessPartnersResources;\n onSelect(businessPartnerUser: BusinessPartnerUser): void;\n}\n\n/**\n * BusinessPartnerUsersTable component.\n */\nexport class BusinessPartnerUsersTable extends React.Component {\n public shouldComponentUpdate(nextProps: Readonly): boolean {\n return this.props.id !== nextProps.id;\n }\n\n public render(): JSX.Element {\n return (\n \n );\n }\n\n /**\n * Get column definition of the table.\n * @returns - Columns.\n */\n private readonly getColumns = (): IDataGridColumn[] => {\n const isMobileDevice = isMobile({ variant: VariantType.Viewport, context: this.props.context.request }) === 'xs';\n\n return [\n {\n reactKey: 'name',\n name: this.props.resources.businessUserTableNameHeadingText,\n isSearchable: true,\n onClick: this.props.onSelect,\n onRender: (data: IBusinessPartnerUserViewModel) => {data.name}\n },\n {\n reactKey: 'email',\n name: this.props.resources.businessUserTableEmailHeadingText,\n fieldName: 'Email',\n onClick: this.props.onSelect\n },\n {\n reactKey: 'spendingLimit',\n name: this.props.resources.businessUserTableSpendingLimitHeadingText,\n className: `${this.props.className}__row__column-with-number`,\n fieldName: 'spendingLimit',\n onClick: !isMobileDevice ? this.props.onSelect : undefined\n }\n ];\n };\n\n /**\n * Call retail proxy to get business partner users.\n * @returns - Business partner users view model.\n */\n private readonly getBusinessPartnerUsers = async (): Promise => {\n const businessPartners = await BusinessPartnersDataActions.getUsersAsync(\n {\n callerContext: this.props.context.actionContext,\n bypassCache: 'get',\n queryResultSettings: {}\n },\n this.props.businessPartner.BusinessPartnerId\n );\n return businessPartners\n .filter(user => user.StatusValue === 1)\n .map(user => {\n return {\n ...user,\n name: `${user.FirstName} ${user.LastName}`,\n spendingLimit: (\n \n {!ObjectExtensions.isNullOrUndefined(user.SpendingLimit)\n ? this.props.context.cultureFormatter.formatCurrency(user.SpendingLimit.toString())\n : ''}\n {!ObjectExtensions.isNullOrUndefined(user.SpendingLimit) && user.SpendingLimit === 0 ? (\n \n \n
\n ) : (\n ''\n )}\n \n )\n };\n });\n };\n}\n","/*!\n * Copyright (c) Microsoft Corporation.\n * All rights reserved. See LICENSE in the project root for license information.\n */\n\nimport { IComponentProps } from '@msdyn365-commerce/core';\nimport React from 'react';\n\nimport { BusinessPartner, BusinessPartnersDataActions } from '@msdyn365-commerce/retail-proxy';\nimport { IBusinessPartnersResources } from '../business-partners.props.autogenerated';\nimport { DataGrid, IDataGridColumn } from './data-grid';\n\n/**\n * Props of IBusinessPartnersTable.\n */\nexport interface IBusinessPartnersTableProps extends IComponentProps {\n className: string;\n pageSize?: number;\n resources: IBusinessPartnersResources;\n onSelect(businessPartner: BusinessPartner): void;\n}\n\n/**\n * BusinessPartnersTable component.\n */\nexport class BusinessPartnersTable extends React.Component {\n public shouldComponentUpdate(nextProps: Readonly): boolean {\n return this.props.id !== nextProps.id;\n }\n\n public render(): JSX.Element {\n return (\n \n );\n }\n\n /**\n * Get column definition of the table.\n * @returns - Columns.\n */\n private readonly getColumns = (): IDataGridColumn[] => {\n return [\n {\n reactKey: 'name',\n name: this.props.resources.businessPartnerName,\n isSearchable: true,\n onClick: this.props.onSelect,\n onRender: (data: BusinessPartner) => {data.Name}\n }\n ];\n };\n\n /**\n * Call retail proxy to get business partners.\n * @returns - Business partners.\n */\n private readonly getBusinessPartners = async () => {\n const businessPartners = await BusinessPartnersDataActions.getManagedBusinessPartnersAsync({\n callerContext: this.props.context.actionContext,\n bypassCache: 'get',\n queryResultSettings: {}\n });\n return businessPartners;\n };\n}\n","/*!\n * Copyright (c) Microsoft Corporation.\n * All rights reserved. See LICENSE in the project root for license information.\n */\n\nimport * as Msdyn365 from '@msdyn365-commerce/core';\nimport { Alert, IModuleProps } from '@msdyn365-commerce-modules/utilities';\nimport classname from 'classnames';\nimport * as React from 'react';\n\nimport { BusinessPartner, BusinessPartnerUser, CatalogsDataActions, CommerceException } from '@msdyn365-commerce/retail-proxy';\nimport { validateCatalogId } from '@msdyn365-commerce-modules/retail-actions';\nimport { IBusinessPartnersData } from './business-partners.data';\nimport { IBusinessPartnersProps } from './business-partners.props.autogenerated';\nimport { BusinessPartnerUsersTable } from './components/business-partner-users-table';\nimport { BusinessPartnersTable } from './components/business-partners-table';\n\n/**\n * Props for the module view.\n */\nexport interface IBusinessPartnersViewProps extends IBusinessPartnersProps {\n businessPartnersProps: IModuleProps;\n businessPartnersHeading?: React.ReactNode;\n businessPartnersTable?: React.ReactNode;\n businessPartnerUsersTable?: React.ReactNode;\n backToBusinessPartnersLink?: React.ReactNode;\n catalogLoadingIndicator?: React.ReactNode;\n catalogLoadingErrorMessage?: React.ReactNode;\n selectedBusinessPartnerUser?: BusinessPartnerUser;\n cookieConsentNotGivenErrorMessage?: React.ReactNode;\n}\n\n/**\n * State of BusinessPartners component.\n */\nexport interface IBusinessPartnersState {\n selectedBusinessPartner?: BusinessPartner;\n selectedBusinessPartnerUser?: BusinessPartnerUser;\n isLoadingCatalog?: boolean;\n loadCatalogErrorMessage?: string;\n}\n\n/**\n * The class name root for the module.\n */\nexport const moduleClassName: string = 'ms-business-partners';\n\n/**\n * BusinessPartners component.\n */\nclass BusinessPartners extends React.Component, IBusinessPartnersState> {\n constructor(props: IBusinessPartnersProps) {\n super(props);\n this.state = {};\n }\n\n public componentDidMount() {\n this.props.context.request.cookies.removeAccountSelectionCookie();\n }\n\n public shouldComponentUpdate(nextProps: IBusinessPartnersProps, nextState: IBusinessPartnersState): boolean {\n if (this.state === nextState && this.props.data === nextProps.data) {\n return false;\n }\n return true;\n }\n\n public render(): JSX.Element | null {\n const {\n config: { className, pageSize, businessPartnersHeading }\n } = this.props;\n const viewProps: IBusinessPartnersViewProps = {\n ...this.props,\n selectedBusinessPartnerUser: this.state.selectedBusinessPartnerUser,\n businessPartnersProps: {\n moduleProps: this.props,\n className: classname(moduleClassName, className)\n },\n businessPartnersHeading: businessPartnersHeading && businessPartnersHeading.text && (\n \n ),\n businessPartnersTable: !this.state.selectedBusinessPartner ? (\n \n ) : (\n undefined\n ),\n businessPartnerUsersTable: this.state.selectedBusinessPartner ? (\n \n ) : (\n undefined\n ),\n backToBusinessPartnersLink: this.state.selectedBusinessPartner ? (\n {\n this.onSelectBusinessPartner();\n }}\n >\n {this.props.resources.backToBusinessPartnersLink}\n \n ) : (\n undefined\n ),\n catalogLoadingIndicator: this.state.isLoadingCatalog ? (\n {this.props.resources.loadingLabel}\n ) : (\n undefined\n ),\n catalogLoadingErrorMessage: this.state.loadCatalogErrorMessage ? (\n {this.state.loadCatalogErrorMessage}\n ) : (\n undefined\n ),\n cookieConsentNotGivenErrorMessage: !this.props.context.request.cookies.isConsentGiven() ? (\n {this.props.resources.cookieConsentRequiredMessage}\n ) : (\n undefined\n )\n };\n return this.props.renderView(viewProps);\n }\n\n /**\n * Event handler of business partner selection.\n * @param businessPartner - Selected business partner.\n */\n private readonly onSelectBusinessPartner = (businessPartner?: BusinessPartner) => {\n this.setState({\n selectedBusinessPartner: businessPartner\n });\n };\n\n /**\n * Event handler of business partner user selection.\n * @param businessPartnerUser - Selected business partner user.\n */\n private readonly onSelectBusinessPartnerUser = async (businessPartnerUser: BusinessPartnerUser) => {\n if (businessPartnerUser.AccountNumber) {\n this.setState({\n selectedBusinessPartnerUser: businessPartnerUser\n });\n\n this.props.context.request.cookies.setAccountSelectionCookie(true, businessPartnerUser.AccountNumber);\n const catalogId = await this.getCustomerDefaultCatalogId();\n validateCatalogId(catalogId);\n const homeUrl = Msdyn365.getUrlSync('home', this.props.context.actionContext);\n if (homeUrl && Msdyn365.msdyn365Commerce.isBrowser && catalogId !== undefined) {\n const fullUrl = new URL(homeUrl, window.location.href);\n fullUrl.searchParams.set('catalogid', `${catalogId}`);\n window.location.href = fullUrl.href;\n } else {\n this.props.context.request.cookies.removeAccountSelectionCookie();\n }\n }\n };\n\n /**\n * Get the first catalog ID of the selected customer. Customer account number is passed in CAN header.\n */\n private readonly getCustomerDefaultCatalogId = async (): Promise => {\n this.setState({\n isLoadingCatalog: true\n });\n\n const requestContext = this.props.context.request;\n const channelId = requestContext.apiSettings.channelId ? Number(requestContext.apiSettings.channelId) : 0;\n\n try {\n const productCatalogs = await CatalogsDataActions.getCatalogsAsync(\n { callerContext: this.props.context.actionContext },\n channelId,\n true\n );\n this.setState({\n isLoadingCatalog: false\n });\n return productCatalogs[0]?.RecordId || 0;\n } catch (e) {\n this.setState({\n isLoadingCatalog: false,\n loadCatalogErrorMessage:\n (e as { data: CommerceException })?.data?.LocalizedMessage || this.props.resources.genericErrorMessage\n });\n return undefined;\n }\n };\n}\n\nexport default BusinessPartners;\n","/*--------------------------------------------------------------\r\n * Copyright (c) Microsoft Corporation. All rights reserved.\r\n * See License.txt in the project root for license information.\r\n *--------------------------------------------------------------*/\r\n\r\nimport { Module, Node } from '@msdyn365-commerce-modules/utilities';\r\nimport * as React from 'react';\r\n\r\nimport { IAddressAddItem, IAddressAddUpdateProps } from '@msdyn365-commerce-modules/address/src/modules/checkout-shipping-address/../../common/components/address-add';\r\nimport { IAddressSelectItem, IAddressSelectProps } from '@msdyn365-commerce-modules/address/src/modules/checkout-shipping-address/../../common/components/address-select';\r\nimport { IAddressShowItem, IAddressShowProps } from '@msdyn365-commerce-modules/address/src/modules/checkout-shipping-address/../../common/components/address-show';\r\nimport { ICheckoutShippingAddressViewProps } from '@msdyn365-commerce-modules/address/src/modules/checkout-shipping-address/./checkout-shipping-address';\r\nimport GetProductEligibilityAction, {\r\n GetProductEligibilityInput\r\n} from \"../../../actions/get-product-eligibility.action\";\r\nimport { CommercePropertyValueEnum } from \"../../../modules/allowable-freight/utils/CommercePropertyValueEnum\";\r\nimport { CommerceProperty } from \"@msdyn365-commerce/retail-proxy\";\r\n/**\r\n * Address show component.\r\n * @param param0 - Root param.\r\n * @param param0.AddressDetail - Address detail.\r\n * @param param0.items - IAddressShowItem[].\r\n * @returns - Address Node.\r\n */\r\nconst AddressShow: React.FC = ({\r\n AddressDetail,\r\n items\r\n}) => {\r\n\r\n return (\r\n \r\n {items.map((item: IAddressShowItem) => {\r\n return (\r\n <>\r\n {item.description}\r\n >\r\n );\r\n })}\r\n \r\n );\r\n};\r\n\r\n/**\r\n * Address Select Component.\r\n * @param param0 - Root param.\r\n * @param param0.SelectAddress - Select address.\r\n * @param param0.addButton - Add button.\r\n * @param param0.items - IAddressSelectItem[].\r\n * @param param0.isShowSaveButton - Boolean.\r\n * @param param0.saveButton - Save button.\r\n * @param param0.isShowCancelButton - Boolean.\r\n * @param param0.cancelButton - Cancel button.\r\n * @returns - SelectAddress Node.\r\n */\r\nconst AddressSelect: React.FC = ({\r\n SelectAddress,\r\n addButton,\r\n items,\r\n isShowSaveButton,\r\n saveButton,\r\n isShowCancelButton,\r\n cancelButton\r\n}) => {\r\n\r\n return (\r\n \r\n {addButton}\r\n {items.map((item: IAddressSelectItem) => {\r\n const SelectItem = item.SelectItem;\r\n return (\r\n {item.input}\r\n \r\n );\r\n })}\r\n {isShowSaveButton && saveButton}\r\n {isShowCancelButton && cancelButton}\r\n \r\n );\r\n};\r\n\r\n/**\r\n * Address Add Update Component.\r\n * @param param0 - Root param.\r\n * @param param0.AddressForm - Address form.\r\n * @param param0.heading - Address heading.\r\n * @param param0.items - IAddressAddItem[].\r\n * @param param0.hasError - Boolean.\r\n * @param param0.error - Error message.\r\n * @param param0.isShowSaveButton - Boolean.\r\n * @param param0.saveButton - Save button.\r\n * @param param0.isShowCancelButton - Boolean.\r\n * @param param0.cancelButton - Cancel button.\r\n * @returns Address add update component node.\r\n */\r\nconst AddressAddUpdate: React.FC = ({\r\n AddressForm,\r\n heading,\r\n items,\r\n hasError,\r\n error,\r\n isShowSaveButton,\r\n saveButton,\r\n isShowCancelButton,\r\n cancelButton\r\n}) => {\r\n\r\n return (\r\n \r\n {heading}\r\n {items.map((item: IAddressAddItem) => {\r\n const { AddressItem, key, label, alert, input } = item;\r\n return (\r\n {label}\r\n {alert}\r\n {input}\r\n );\r\n })}\r\n {hasError && \r\n {error.title}\r\n {error.message}\r\n }\r\n {isShowSaveButton && saveButton}\r\n {isShowCancelButton && cancelButton}\r\n \r\n );\r\n};\r\n\r\n/**\r\n * Checkout Shipping Address View Component.\r\n * @param props - Props.\r\n * @returns - CheckoutShippingAddress Module.\r\n */\r\nconst CheckoutShippingAddressView: React.FC = props => {\r\n const { CheckoutShippingAddress, viewState, showAddress, showAddressSelect, showAddOrUpdateAddress, cartLineImages } = props;\r\n const address = props.data.checkout.result?.shippingAddress;\r\n const [ nonEligibleItems, setnonEligibleItems ] = React.useState([]);\r\n React.useEffect(() => {\r\n void checkEligibilityForShippingAddress();\r\n }, [address])\r\n React.useEffect(() => {\r\n props.moduleState.setHasError(true);\r\n }, [])\r\n\r\n const _getExtensionPropertyValueByKey = (\r\n properties: CommerceProperty[] | undefined,\r\n key: string,\r\n valueType: CommercePropertyValueEnum\r\n ) => {\r\n const property = properties?.find(p => p.Key === key)?.Value;\r\n\r\n // eslint-disable-next-line security/detect-object-injection\r\n const val = property ? property[valueType] : null;\r\n return val;\r\n };\r\n\r\n\r\n const checkEligibilityForShippingAddress = async () => {\r\n const notEligibleList: string[] = [];\r\n const cart = props.data.checkout.result?.checkoutCart?.cart;\r\n if (cart && address) {\r\n const itemIds = cart.CartLines?.map(cl => cl.ItemId!) || [];\r\n const addressCountry = address.ThreeLetterISORegionName || null;\r\n const addressState = address.State || null;\r\n const addressRegion = address.County || null;\r\n const input = new GetProductEligibilityInput(itemIds, true, cart.Id || '', addressCountry, addressState, addressRegion);\r\n const eligibilityData = await GetProductEligibilityAction(input, props.context.actionContext);\r\n if (eligibilityData.length) {\r\n eligibilityData.forEach((i: any) => {\r\n const id = _getExtensionPropertyValueByKey(i.ExtensionProperties, 'ItemId', CommercePropertyValueEnum.string);\r\n if (!i.IsEligible) {\r\n notEligibleList.push(id as string);\r\n }\r\n });\r\n }\r\n }\r\n setnonEligibleItems(notEligibleList);\r\n props.moduleState.setHasError(notEligibleList.length > 0);\r\n }\r\n\r\n const renderCartError = () => {\r\n if (!nonEligibleItems.length) {\r\n return null;\r\n }\r\n return (\r\n \r\n
\r\n The following items are not eligible for your current shipping address. Please remove them from the cart or change your address:\r\n
\r\n {nonEligibleItems.map(i => {\r\n const item = getCartLineFromItemId(i);\r\n return
{item?.ItemId} - {item?.Description}
\r\n })}\r\n
\r\n
\r\n )\r\n }\r\n\r\n const removeIneligibleItems = async () => {\r\n const cartState = props.data.cart.result;\r\n const checkoutState = props.data.checkout.result;\r\n const badCartLines: string[] = [];\r\n cartState!.cart.CartLines!.forEach(cl => {\r\n if (nonEligibleItems.includes(cl.ItemId!)) {\r\n badCartLines.push(cl.LineId!);\r\n }\r\n })\r\n await checkoutState!.checkoutCart.removeCartLines({cartLineIds: badCartLines});\r\n await cartState!.removeCartLines({cartLineIds: badCartLines});\r\n window.location.reload();\r\n }\r\n\r\n const getCartLineFromItemId = (itemId: string) => props.data.checkout.result?.checkoutCart.cart.CartLines?.find(i => i.ItemId === itemId)\r\n return (\r\n \r\n {cartLineImages}\r\n {viewState.isShowAddress && }\r\n {viewState.isShowAddresList && }\r\n {viewState.isShowAddOrUpdateAddress && }\r\n {renderCartError()}\r\n \r\n );\r\n};\r\n\r\nexport default CheckoutShippingAddressView;\r\n","/*!\r\n * Copyright (c) Microsoft Corporation.\r\n * All rights reserved. See LICENSE in the project root for license information.\r\n */\r\n\r\nimport * as React from 'react';\r\n\r\nimport { IOrderLevelCommentData } from './order-level-comment.data';\r\nimport { IOrderLevelCommentProps } from './order-level-comment.props.autogenerated';\r\nimport { Button, IModuleProps, INodeProps, Modal, ModalFooter, ModalHeader } from '@msdyn365-commerce-modules/utilities';\r\nimport classnames from 'classnames';\r\n\r\nexport interface IOrderLevelCommentViewProps extends IOrderLevelCommentProps {\r\n staticDisplay: INodeProps;\r\n orderLevelCommentModuleProps: IModuleProps;\r\n modal: React.ReactNode;\r\n}\r\n\r\ninterface IOrderLevelCommentExtendedProps extends IOrderLevelCommentProps {\r\n comment: string;\r\n}\r\n\r\ninterface IOrderLevelCommentState {\r\n comment: string;\r\n isCommentAreaActive: boolean;\r\n}\r\n\r\n/**\r\n *\r\n * OrderLevelComment component\r\n * @extends {React.PureComponent>}\r\n */\r\nclass OrderLevelComment extends React.PureComponent {\r\n private _className = 'gar-order-comment';\r\n private _maxCommentLength = 60;\r\n constructor(props: IOrderLevelCommentExtendedProps) {\r\n super(props);\r\n this.state = {\r\n comment: props.data.cart.result?.cart.Comment || '',\r\n isCommentAreaActive: false\r\n };\r\n }\r\n\r\n /**\r\n * Saves order comment\r\n */\r\n\r\n private _updateOrderLevelComment = async (): Promise => {\r\n const cartState = this.props.data.cart.result;\r\n if (!cartState?.cart) {\r\n return;\r\n }\r\n const dummyCart = { ...cartState.cart };\r\n dummyCart.Comment = this.state.comment;\r\n await cartState.updateCart({ newCartObject: dummyCart });\r\n this.setState({ isCommentAreaActive: false });\r\n };\r\n\r\n /**\r\n * Updates the comment in state\r\n * @param event new comment from event\r\n */\r\n private _updateCommentState = (event: React.ChangeEvent): void => {\r\n this.setState({ comment: event.target.value });\r\n };\r\n /**\r\n * Resets Cart Comment\r\n */\r\n private _resetCommentToProps = (): void => {\r\n this.setState({ comment: this.props.comment, isCommentAreaActive: false });\r\n };\r\n\r\n /**\r\n * Toggles the isCommentAreaActive state property\r\n */\r\n private _toggleCommentAreaActive = () =>\r\n this.setState(prevState => {\r\n return {\r\n isCommentAreaActive: !prevState.isCommentAreaActive\r\n };\r\n });\r\n\r\n /**\r\n * Renders the static comment display for when the isCommentAreaActive state param is false\r\n * @return React.ReactNode\r\n */\r\n private _renderStaticCommentDisplay = (): React.ReactNode => {\r\n const {\r\n resources: { commentLinkText, editLinkText },\r\n config: { disclaimer }\r\n } = this.props;\r\n const currentComment = this.props.data.cart.result?.cart.Comment;\r\n if (!currentComment) {\r\n return (\r\n <>\r\n {this._renderCommentLink(commentLinkText)}\r\n {disclaimer}
\r\n >\r\n );\r\n } else {\r\n return (\r\n <>\r\n {this._getCommentPreview()}
\r\n {this._renderCommentLink(editLinkText)}\r\n {disclaimer ? {disclaimer}
: null}\r\n >\r\n );\r\n }\r\n };\r\n\r\n private _getCommentPreview = (): string => {\r\n const comment = this.props.data.cart.result?.cart.Comment || '';\r\n if (comment.length > 50) {\r\n return `${comment.substring(0, 50)}...`;\r\n } else {\r\n return comment;\r\n }\r\n };\r\n\r\n /**\r\n * Renders a comment link which toggles the active area state\r\n */\r\n private _renderCommentLink = (linkText: string): React.ReactNode => (\r\n \r\n {linkText}\r\n \r\n );\r\n\r\n /**\r\n * Renders a button to save comments to the cartlines\r\n */\r\n private _renderSaveButton = (): React.ReactNode => {\r\n const {\r\n resources: { saveButtonText }\r\n } = this.props;\r\n return (\r\n \r\n );\r\n };\r\n\r\n /**\r\n * Renders a button to cancel the comment entry and set the state to default\r\n */\r\n private _renderCancelButton = (): React.ReactNode => {\r\n const {\r\n resources: { cancelButtonText }\r\n } = this.props;\r\n return (\r\n \r\n );\r\n };\r\n\r\n /**\r\n * Renders the text area for inputting comments\r\n */\r\n private _renderTextArea = (): React.ReactNode => {\r\n const {\r\n resources: { textAreaLabel, charactersRemaining }\r\n } = this.props;\r\n const { comment } = this.state;\r\n return (\r\n \r\n
\r\n
\r\n
\r\n {`${this._maxCommentLength - (comment && comment.length ? comment.length : 0)} ${charactersRemaining}`}\r\n
\r\n
\r\n );\r\n };\r\n\r\n /**\r\n * Renders the button area\r\n */\r\n private _renderButtonArea = (): React.ReactNode => (\r\n \r\n {this._renderSaveButton()}\r\n {this._renderCancelButton()}\r\n
\r\n );\r\n\r\n /**\r\n * Renders the complete modal\r\n */\r\n private _renderModal = () => {\r\n return (\r\n \r\n {this._getModalTitle()}\r\n {this._renderTextArea()}\r\n {this._renderButtonArea()}\r\n \r\n );\r\n };\r\n\r\n /**\r\n * Gets ID for the text field\r\n */\r\n private _getFieldId = (): string => `orderLevelCommentInput`;\r\n\r\n /**\r\n * Gets the title for the comment modal\r\n */\r\n private _getModalTitle = (): string => {\r\n const {\r\n resources: { commentModalTitle }\r\n } = this.props;\r\n\r\n return commentModalTitle;\r\n };\r\n\r\n public render(): JSX.Element | null {\r\n const {\r\n config: { className }\r\n } = this.props;\r\n const viewProps: IOrderLevelCommentViewProps = {\r\n ...this.props,\r\n staticDisplay: {\r\n tag: 'div',\r\n className: `${this._className}__static-display`,\r\n children: this._renderStaticCommentDisplay()\r\n },\r\n orderLevelCommentModuleProps: {\r\n className: classnames(className, this._className),\r\n tag: 'div',\r\n moduleProps: { ...this.props }\r\n },\r\n modal: this._renderModal()\r\n };\r\n\r\n return this.props.renderView(viewProps);\r\n }\r\n}\r\n\r\nexport default OrderLevelComment;\r\n","/*!\n * Copyright (c) Microsoft Corporation.\n * All rights reserved. See LICENSE in the project root for license information.\n */\n\nimport * as Msdyn365 from '@msdyn365-commerce/core';\nimport { ArrayExtensions } from '@msdyn365-commerce-modules/retail-actions';\nimport { getPayloadObject, getTelemetryAttributes, ITelemetryContent, onTelemetryClick } from '@msdyn365-commerce-modules/utilities';\nimport * as React from 'react';\n\nimport { ILinksData } from '../tile-list-item.props.autogenerated';\n\n/**\n * Tile List links.\n */\nexport interface ITileListLinks {\n links: ILinksData[];\n requestContext: Msdyn365.IRequestContext;\n telemetryContent: ITelemetryContent;\n onTextChange?(index: number): (event: Msdyn365.ContentEditableEvent) => void;\n}\n\n/**\n *\n * ITileListLinks component.\n * @extends {React.PureComponent}\n */\nexport class TileListLinks extends React.PureComponent {\n public render(): JSX.Element {\n const editableLinks = this._mapEditableLinks(this.props.links);\n return (\n \n {editableLinks && ArrayExtensions.hasElements(editableLinks) ? (\n \n ) : null}\n
\n );\n }\n\n /**\n * ITileListLinks component.\n * @param linkdata - Link data.\n * @returns Editable links.\n */\n private readonly _mapEditableLinks = (linkdata: ILinksData[]): Msdyn365.ILinksData[] | null => {\n if (!ArrayExtensions.hasElements(linkdata)) {\n return null;\n }\n const editableLinks: Msdyn365.ILinksData[] = [];\n for (const link of linkdata) {\n // Construct telemetry attribute to render\n const payLoad = getPayloadObject('click', this.props.telemetryContent, '', '');\n const linkText = link.linkText ? link.linkText : '';\n payLoad.contentAction.etext = linkText;\n const attributes = getTelemetryAttributes(this.props.telemetryContent, payLoad);\n const editableLink: Msdyn365.ILinksData = {\n ariaLabel: link.ariaLabel,\n className: 'ms-tile-list__link',\n linkText: link.linkText,\n linkUrl: link.linkUrl.destinationUrl,\n openInNewTab: link.openInNewTab,\n role: 'button',\n additionalProperties: attributes,\n onClick: onTelemetryClick(this.props.telemetryContent, payLoad, linkText)\n };\n editableLinks.push(editableLink);\n }\n\n return editableLinks;\n };\n}\nexport default TileListLinks;\n","/*!\n * Copyright (c) Microsoft Corporation.\n * All rights reserved. See LICENSE in the project root for license information.\n */\n\nimport * as Msdyn365 from '@msdyn365-commerce/core';\nimport { ArrayExtensions, StringExtensions } from '@msdyn365-commerce-modules/retail-actions';\nimport { getTelemetryObject, IModuleProps, INodeProps, ITelemetryContent, NodeTag } from '@msdyn365-commerce-modules/utilities';\nimport * as React from 'react';\n\nimport { TileListLinks } from './components';\nimport { IHeadingData, ITileListItemProps } from './tile-list-item.props.autogenerated';\n\n/**\n * Tile-list-item view props.\n */\nexport interface ITileListItemViewProps extends ITileListItemProps<{}> {\n heading?: React.ReactNode;\n paragraph?: React.ReactNode;\n links?: React.ReactNode;\n backgroundImage?: React.ReactNode;\n thumbnailImage?: React.ReactNode;\n tileListItem: IModuleProps;\n tileListContainer: INodeProps;\n tileListImageContainer: INodeProps;\n tileListThumbnailImageContainer: INodeProps;\n tileListHeadingContainer: INodeProps;\n}\n\n/**\n *\n * TileListItem component.\n * @extends {React.PureComponent>}\n */\nexport class TileListItem extends React.PureComponent> {\n private readonly _telemetryContent: ITelemetryContent = getTelemetryObject(\n this.props.context.request.telemetryPageName!,\n this.props.friendlyName,\n this.props.telemetry\n );\n\n public constructor(props: ITileListItemProps<{}>) {\n super(props);\n }\n\n public render(): JSX.Element {\n const { heading, paragraph, backgroundImage, thumbnailImage, redirectionUrl, links } = this.props.config;\n const tileListLinks = links && ArrayExtensions.hasElements(links) && (\n \n );\n const tileListLinkContainer = {\n tag: 'a' as NodeTag,\n className: 'ms-tile-list-container',\n role: 'link',\n href: redirectionUrl?.linkUrl.destinationUrl,\n 'aria-label': redirectionUrl?.ariaLabel\n };\n\n const tileListButtonContainer = {\n tag: 'button' as NodeTag,\n className: 'ms-tile-list-container'\n };\n const viewProps: ITileListItemViewProps = {\n ...this.props,\n heading: this._createHeading(heading),\n paragraph: this._createParagraph(paragraph),\n backgroundImage: this._renderBackgroundImage(backgroundImage),\n thumbnailImage: this._renderThumbnailImage(thumbnailImage),\n links: tileListLinks,\n tileListItem: {\n moduleProps: this.props,\n className: ''\n },\n tileListContainer: redirectionUrl ? tileListLinkContainer : tileListButtonContainer,\n tileListImageContainer: {\n className: 'ms-tile-list__image'\n },\n tileListThumbnailImageContainer: {\n className: 'ms-tile-list__thumbnail_image',\n 'aria-hidden': 'true'\n },\n tileListHeadingContainer: {\n className: 'ms-tile-list__heading'\n }\n };\n\n return this.props.renderView(viewProps) as React.ReactElement;\n }\n\n /**\n * Handles heading content change event.\n * @param event - Content editable event.\n */\n public handleHeadingChange = (event: Msdyn365.ContentEditableEvent): void => {\n this.props.config.heading!.text = event.target.value;\n };\n\n /**\n * Handles paragraph content change event.\n * @param event - Content editable event.\n */\n public handleParagraphChange = (event: Msdyn365.ContentEditableEvent): void => {\n this.props.config.paragraph = event.target.value;\n };\n\n /**\n * Handles paragraph content change event.\n * @param linkIndex - Index of link in linkarray.\n * @returns Void.\n */\n public handleLinkTextChange = (linkIndex: number) => (event: Msdyn365.ContentEditableEvent): void => {\n if (this.props.config.links?.[Number(linkIndex)]) {\n this.props.config.links[Number(linkIndex)].linkText = event.target.value;\n }\n };\n\n private _createParagraph(text?: Msdyn365.RichText): React.ReactNode | null {\n if (!text) {\n return null;\n }\n return (\n \n );\n }\n\n private _createHeading(heading?: IHeadingData): React.ReactNode | null {\n if (!heading) {\n return null;\n }\n if (StringExtensions.isNullOrWhitespace(heading.text)) {\n return null;\n }\n\n return (\n \n );\n }\n\n private _renderBackgroundImage(item?: Msdyn365.IImageData, isAriaHiddenAttribute?: boolean): React.ReactNode | null {\n // If image is null, we want to render the placeholder SVG - by passing image data with empty src\n\n const defaultImageSettings: Msdyn365.IImageSettings = {\n viewports: {\n xs: { q: 'w=328&h=412&m=6', w: 328, h: 412 },\n sm: { q: 'w=328&h=412&m=6', w: 328, h: 412 },\n md: { q: 'w=315&h=412&m=6', w: 315, h: 412 },\n lg: { q: 'w=315&h=412&m=6', w: 315, h: 412 },\n xl: { q: 'w=315&h=412&m=6', w: 315, h: 412 }\n },\n lazyload: true\n };\n const verifiedImageSettings = item?.imageSettings?.viewports ? item.imageSettings : defaultImageSettings;\n\n return (\n \n );\n }\n\n private _renderThumbnailImage(\n item?: Msdyn365.IImageData,\n itemImageSettings?: Msdyn365.IImageSettings,\n isAriaHiddenAttribute?: boolean\n ): React.ReactNode | null {\n if (!item) {\n return null;\n }\n if (StringExtensions.isNullOrWhitespace(item.src)) {\n return null;\n }\n\n return (\n \n );\n }\n}\n\nexport default TileListItem;\n","/*--------------------------------------------------------------\r\n * Copyright (c) Microsoft Corporation. All rights reserved.\r\n * See License.txt in the project root for license information.\r\n *--------------------------------------------------------------*/\r\n\r\nimport { Module, Node } from '@msdyn365-commerce-modules/utilities';\r\nimport * as React from 'react';\r\n\r\nimport {\r\n ICheckoutViewProps,\r\n ILineItem,\r\n ILineItemDeliveryGroup,\r\n ILineItems,\r\n IOrderSummary,\r\n IPickUpAtStore\r\n} from '@msdyn365-commerce-modules/checkout/src/modules/checkout/./checkout';\r\nimport { IEmailDelivery } from '@msdyn365-commerce-modules/checkout/src/modules/checkout/./components';\r\nimport { IInvoicePaymentSummary } from '@msdyn365-commerce-modules/checkout/src/modules/checkout/./components/get-invoice-payment-summary';\r\nimport { IChargeLinesBreakdownData, ICheckoutProps as ICheckoutExtensionProps } from '../definition-extensions/checkout.ext.props.autogenerated';\r\nimport { AttributeBooleanValue, Cart } from '@msdyn365-commerce/retail-proxy';\r\n\r\nexport const PickUpAtStoreComponent: React.FC = ({ PickUpAtStore, label, location }) => (\r\n \r\n {label}\r\n {location}\r\n \r\n);\r\n\r\nexport interface IOrderSummaryExtended extends IOrderSummary {\r\n otherChargesLines: React.ReactNode[];\r\n}\r\n\r\n\r\nexport const EmailDeliveryComponent: React.FC = ({ EmailDelivery, label }) => {label};\r\n\r\nexport const LineItemComponent: React.FC = ({ LineItem, item, pickUpAtStore, emailDelivery }) => (\r\n \r\n {item}\r\n {pickUpAtStore && }\r\n {emailDelivery && }\r\n \r\n);\r\n\r\nexport const LineItemGroupComponent: React.FC = ({ LineItemDeliveryGroup, LineItemList, heading, lineItems }) => (\r\n \r\n {heading}\r\n \r\n {lineItems.map(lineItem => (\r\n \r\n ))}\r\n \r\n \r\n);\r\n\r\nexport const LineItemGroupComponentWithMultiplePickUp: React.FC = ({\r\n LineItemDeliveryGroup,\r\n LineItemList,\r\n heading,\r\n lineItems,\r\n lineItemWraper,\r\n lineItemWraperIcon\r\n}) => (\r\n \r\n {lineItemWraperIcon}\r\n {lineItemWraper}\r\n {heading}\r\n \r\n {lineItems.map(lineItem => (\r\n \r\n ))}\r\n \r\n \r\n);\r\n\r\nexport const LineItemComponentWithMultiplePickUp: React.FC = ({ LineItem, item, pickUpAtStore, emailDelivery }) => (\r\n \r\n {item}\r\n {emailDelivery && }\r\n \r\n);\r\n\r\nexport const PickUpAtStoreComponentWithMultiplePickUp: React.FC = ({ PickUpAtStore, label, location }) => (\r\n \r\n {label}\r\n {location}\r\n \r\n);\r\n\r\nexport const LineItemsComponent: React.FC = ({\r\n LineItems,\r\n Header,\r\n heading,\r\n editLink,\r\n itemsForPickup,\r\n itemsForShip,\r\n itemsForEmail,\r\n itemsGroupWithMulitplePickupMode\r\n}) => (\r\n \r\n \r\n {heading}\r\n {editLink}\r\n \r\n {itemsGroupWithMulitplePickupMode === undefined && itemsForPickup && }\r\n {itemsGroupWithMulitplePickupMode === undefined && itemsForEmail && }\r\n {itemsGroupWithMulitplePickupMode === undefined && itemsForShip && }\r\n {itemsGroupWithMulitplePickupMode !== undefined\r\n ? itemsGroupWithMulitplePickupMode.map((item, index) => {\r\n return ;\r\n })\r\n : null}\r\n \r\n);\r\n\r\nconst OrderSummaryComponent: React.FC = ({ heading, lines, otherChargesLines }) => (\r\n \r\n {heading}\r\n
\r\n {lines && (\r\n <>\r\n {lines.subtotal}\r\n {lines.shipping}\r\n {lines.otherCharge}\r\n {otherChargesLines &&
{otherChargesLines}
}\r\n {lines.tax}\r\n {lines.totalDiscounts}\r\n {lines.loyalty}\r\n {lines.giftCard}\r\n {lines.orderTotal}\r\n >\r\n )}\r\n
\r\n
\r\n);\r\n\r\nconst PaymentSummaryComponent: React.FC = ({ heading, lines }) => (\r\n \r\n {heading}\r\n
\r\n {lines && (\r\n <>\r\n {lines.invoices}\r\n {lines.giftCard}\r\n {lines.loyalty}\r\n {lines.orderTotal}\r\n >\r\n )}\r\n
\r\n
\r\n);\r\n\r\n\r\nconst otherChargesLines = (cart: Cart, chargeCodes: IChargeLinesBreakdownData[] | undefined, currencyFormatter: (price: string | number, currencyCode?: string | undefined) => string): React.ReactNode[] => {\r\n\r\n let lines = cart.ChargeLines;\r\n let linesNode: React.ReactNode[] = [];\r\n chargeCodes?.map(c => {\r\n const node = lines?.filter(\r\n line => line.ChargeCode === c.chargeCode).map(\r\n r => {c.chargeLineLabel} {currencyFormatter(r.CalculatedAmount?.toString() ?? \"\")}
\r\n );\r\n linesNode.push(node);\r\n })\r\n return linesNode;\r\n}\r\n\r\n\r\nconst _renderShipOnCheckout = (\r\n shipCompleteOnCheckout: boolean,\r\n handleShipCompleteOnCheckoutChange: (e: React.MouseEvent) => void,\r\n shipCompleteOnCheckoutLabel: string\r\n): React.ReactNode => (\r\n \r\n
\r\n
\r\n);\r\n\r\nconst _renderOrderComment = (commentHeading: string, commentString: string): React.ReactNode => {\r\n const emptyStringClassName = commentString == '';\r\n return (\r\n \r\n
\r\n {commentHeading}\r\n
\r\n
\r\n {commentString}\r\n
\r\n
\r\n );\r\n}\r\n\r\nconst CheckoutView: React.FC> = props => {\r\n\r\n\r\n const attributeArr = props.data.checkout.result?.checkoutCart.cart.AttributeValues || [];\r\n\r\n const getShipCompleteIndex = (): number => {\r\n return props.data.checkout.result?.checkoutCart.cart.AttributeValues?.findIndex(attribute => attribute.Name === 'ShipComplete') || -1;\r\n }\r\n // Get the text in the order level comment, if it doesn't exist, assign an empty string\r\n const getCommentText = (): string => {\r\n return props.data.checkout.result?.checkoutCart.cart.Comment || '';\r\n }\r\n // Get the comment heading text from the resources\r\n const getCommentHeadingText = (): string => {\r\n return props.resources.commentHeadingText;\r\n }\r\n const getShipCompleteValue = (): boolean => {\r\n const shipCompleteIndex = getShipCompleteIndex();\r\n if (shipCompleteIndex === -1) {\r\n return false;\r\n } else {\r\n const attr = attributeArr[shipCompleteIndex] as AttributeBooleanValue;\r\n return !!attr?.Value;\r\n }\r\n };\r\n const handleShipCompleteOnCheckoutChange = () => {\r\n const shipComplete = {\r\n '@odata.type': '#Microsoft.Dynamics.Commerce.Runtime.DataModel.AttributeBooleanValue',\r\n Name: 'ShipComplete',\r\n Value: !getShipCompleteValue()\r\n };\r\n const idx = getShipCompleteIndex();\r\n idx === -1 ? attributeArr.push(shipComplete) : attributeArr.splice(idx, 1, shipComplete);\r\n const cart: Cart = Object.assign({}, props.data.checkout.result?.checkoutCart.cart);\r\n if (cart) {\r\n cart.AttributeValues = attributeArr;\r\n saveShipCompleteToCart(cart);\r\n }\r\n };\r\n const saveShipCompleteToCart = async (cart: Cart) => {\r\n if (cart) {\r\n const checkoutState = props.data.checkout.result;\r\n if (!checkoutState) {\r\n return;\r\n }\r\n\r\n await checkoutState.checkoutCart.updateCart({ newCartObject: cart }).then(() => {\r\n setShipCompleteOnCheckout(getShipCompleteValue());\r\n });\r\n }\r\n };\r\n const [shipCompleteOnCheckout, setShipCompleteOnCheckout] = React.useState(getShipCompleteValue());\r\n const shipCompleteOnCheckoutToggle = _renderShipOnCheckout(\r\n shipCompleteOnCheckout,\r\n handleShipCompleteOnCheckoutChange,\r\n props.resources.shipCompleteOnCheckoutLabel\r\n );\r\n const {\r\n canShow,\r\n checkoutProps,\r\n headerProps,\r\n hasSalesOrder,\r\n hasInvoiceLine,\r\n bodyProps,\r\n mainProps,\r\n mainControlProps,\r\n sideProps,\r\n sideControlFirstProps,\r\n sideControlSecondProps,\r\n termsAndConditionsProps,\r\n orderConfirmation,\r\n loading,\r\n alert,\r\n title,\r\n guidedForm,\r\n orderSummary,\r\n invoicePaymentSummary,\r\n lineItems,\r\n placeOrderButton,\r\n termsAndConditions,\r\n keepShoppingButton\r\n } = props;\r\n const cart: Cart = Object.assign({}, props.data.checkout.result?.checkoutCart.cart);\r\n\r\n let chargelines = otherChargesLines(cart, props.config.chargeLinesBreakdown, props.context.cultureFormatter.formatCurrency);\r\n const orderSummaryExtended: IOrderSummaryExtended = { ...orderSummary!, otherChargesLines: chargelines };\r\n\r\n return (\r\n \r\n {!hasSalesOrder && {title}}\r\n {!hasSalesOrder && (\r\n \r\n {loading}\r\n {alert}\r\n {canShow && (\r\n <>\r\n \r\n {guidedForm}\r\n {termsAndConditions}\r\n {props.config.shipCompleteOnCheckout && shipCompleteOnCheckoutToggle}\r\n \r\n {placeOrderButton}\r\n {keepShoppingButton}\r\n \r\n \r\n \r\n {!hasInvoiceLine\r\n ? orderSummary && \r\n : invoicePaymentSummary && }\r\n \r\n {termsAndConditions}\r\n {placeOrderButton}\r\n {keepShoppingButton}\r\n \r\n {lineItems && }\r\n {_renderOrderComment(getCommentHeadingText(), getCommentText())}\r\n \r\n {termsAndConditions}\r\n {placeOrderButton}\r\n {keepShoppingButton}\r\n \r\n \r\n >\r\n )}\r\n \r\n )}\r\n {hasSalesOrder && orderConfirmation}\r\n \r\n );\r\n};\r\n\r\nexport default CheckoutView;\r\n","/*--------------------------------------------------------------\r\n * Copyright (c) Microsoft Corporation. All rights reserved.\r\n * See License.txt in the project root for license information.\r\n *--------------------------------------------------------------*/\r\n\r\nimport { getUrlSync } from '@msdyn365-commerce/core';\r\nimport { IB2bRequestsStatusViewProps } from '@msdyn365-commerce-modules/account-management';\r\nimport { Button, Module, Node } from '@msdyn365-commerce-modules/utilities';\r\nimport React from 'react';\r\n\r\nimport { IB2bRequestsStatusProps, IB2bRequestsStatusResources } from '../definition-extensions/b2b-requests-status.ext.props.autogenerated';\r\n\r\n/**\r\n * B2b Request status View.\r\n * @param props - The view props.\r\n * @returns The JSX Element.\r\n */\r\n// eslint-disable-next-line @typescript-eslint/naming-convention -- Dependency from module file.\r\nconst B2bRequestsStatusView: React.FC> = props => {\r\n const { b2bRequestsStatus, heading, table } = props;\r\n const { continueShoppingButtonTitle } = props.resources;\r\n const initialCount = 0;\r\n\r\n if (props.data.operationRequests.result?.length === initialCount) {\r\n return (\r\n \r\n {heading}\r\n \r\n \r\n
\r\n {props.resources.headingForEmptyRequestStatus}\r\n
\r\n
\r\n {props.resources.textForEmptyRequestStatus}\r\n
\r\n
\r\n \r\n \r\n
\r\n \r\n \r\n );\r\n }\r\n return (\r\n \r\n {heading}\r\n {table}\r\n \r\n );\r\n};\r\n\r\nexport default B2bRequestsStatusView;\r\n","/*!\r\n * Copyright (c) Microsoft Corporation.\r\n * All rights reserved. See LICENSE in the project root for license information.\r\n */\r\n\r\nimport * as React from 'react';\r\n\r\nimport { ICartLineCommentData } from './cart-line-comment.data';\r\nimport { ICartLineCommentProps } from './cart-line-comment.props.autogenerated';\r\nimport { Button, IModuleProps, INodeProps, Modal, ModalFooter, ModalHeader } from '@msdyn365-commerce-modules/utilities';\r\nimport classnames from 'classnames';\r\n\r\nexport interface ICartLineCommentViewProps extends ICartLineCommentProps {\r\n staticDisplay: INodeProps;\r\n cartLineCommentModuleProps: IModuleProps;\r\n modal: React.ReactNode;\r\n}\r\n\r\ninterface ICartLineCommentExtendedProps extends ICartLineCommentProps {\r\n comment: string;\r\n cartLineId: string;\r\n cartProductTitle: string;\r\n}\r\n\r\ninterface ICartLineCommentState {\r\n comment: string;\r\n isCommentAreaActive: boolean;\r\n}\r\n/**\r\n *\r\n * CartLineComment component\r\n * @extends {React.PureComponent>}\r\n */\r\nclass CartLineComment extends React.PureComponent {\r\n private _className = 'gar-cart-comment';\r\n constructor(props: ICartLineCommentExtendedProps) {\r\n super(props);\r\n this.state = {\r\n comment: props.comment,\r\n isCommentAreaActive: false\r\n };\r\n }\r\n\r\n /**\r\n * Saves cart comment\r\n * Method must call updateCartLineQuantity on the cart state because\r\n * updateCart does not update the cart line comment. Calling the other method with an additional\r\n * parameter for the comment successfully updates the comment\r\n */\r\n private _updateCartLineComment = async (): Promise => {\r\n const cartState = this.props.data.cart.result;\r\n if (!cartState) {\r\n return;\r\n }\r\n const dummyCart = { ...cartState.cart };\r\n const cartLineToUpdate = dummyCart.CartLines?.find(cl => cl.LineId === this.props.cartLineId);\r\n if (cartLineToUpdate) {\r\n cartLineToUpdate.Comment = this.state.comment;\r\n }\r\n await cartState.updateCartLineQuantity({\r\n cartLineId: this.props.cartLineId,\r\n newQuantity: cartLineToUpdate?.Quantity!,\r\n additionalProperties: { Comment: this.state.comment }\r\n });\r\n\r\n this.setState({ isCommentAreaActive: false });\r\n };\r\n\r\n /**\r\n * Updates the comment in state\r\n * @param event new comment from event\r\n */\r\n private _updateCommentState = (event: React.ChangeEvent): void => {\r\n this.setState({ comment: event.target.value });\r\n };\r\n /**\r\n * Resets Cart Comment\r\n */\r\n private _resetCommentToProps = (): void => {\r\n this.setState({ comment: this.props.comment, isCommentAreaActive: false });\r\n };\r\n\r\n /**\r\n * Toggles the isCommentAreaActive state property\r\n */\r\n private _toggleCommentAreaActive = () =>\r\n this.setState(prevState => {\r\n return {\r\n isCommentAreaActive: !prevState.isCommentAreaActive\r\n };\r\n });\r\n\r\n /**\r\n * Renders the static comment display for when the isCommentAreaActive state param is false\r\n * @return React.ReactNode\r\n */\r\n private _renderStaticCommentDisplay = (): React.ReactNode => {\r\n const {\r\n resources: { commentLinkText, editLinkText }\r\n } = this.props;\r\n if (!this.props.comment) {\r\n return this._renderCommentLink(commentLinkText);\r\n } else {\r\n return (\r\n \r\n {this._getCommentPreview()}
\r\n {this._renderCommentLink(editLinkText)}\r\n \r\n );\r\n }\r\n };\r\n\r\n private _getCommentPreview = (): string => {\r\n const { comment } = this.props;\r\n if (comment.length > 50) {\r\n return `${comment.substring(0, 50)}...`;\r\n } else {\r\n return comment;\r\n }\r\n };\r\n\r\n /**\r\n * Renders a comment link which toggles the active area state\r\n * @param linkText text for the link\r\n */\r\n private _renderCommentLink = (linkText: string): React.ReactNode => (\r\n \r\n {linkText}\r\n \r\n );\r\n\r\n /**\r\n * Renders a button to save comments to the cartlines\r\n * @return React.ReactNode\r\n */\r\n private _renderSaveButton = (): React.ReactNode => {\r\n const {\r\n resources: { saveButtonText }\r\n } = this.props;\r\n return (\r\n \r\n );\r\n };\r\n\r\n /**\r\n * Renders a button to cancel the comment entry and set the state to default\r\n * @return React.ReactNode\r\n */\r\n private _renderCancelButton = (): React.ReactNode => {\r\n const {\r\n resources: { cancelButtonText }\r\n } = this.props;\r\n return (\r\n \r\n );\r\n };\r\n\r\n /**\r\n * Renders the text area for inputting comments\r\n */\r\n private _renderTextArea = (): React.ReactNode => {\r\n const {\r\n resources: { textAreaLabel }\r\n } = this.props;\r\n const { comment } = this.state;\r\n return (\r\n \r\n \r\n \r\n
\r\n );\r\n };\r\n /**\r\n * Renders the button area\r\n */\r\n private _renderButtonArea = (): React.ReactNode => (\r\n \r\n {this._renderSaveButton()}\r\n {this._renderCancelButton()}\r\n
\r\n );\r\n\r\n private _renderModal = () => {\r\n return (\r\n \r\n {this._getModalTitle()}\r\n {this._renderTextArea()}\r\n {this._renderButtonArea()}\r\n \r\n );\r\n };\r\n\r\n /**\r\n * Gets ID for the text field\r\n */\r\n private _getFieldId = (): string => `${this.props.cartLineId}__commentInput`;\r\n\r\n /**\r\n * Gets the title for the comment modal\r\n */\r\n private _getModalTitle = (): string => {\r\n const {\r\n resources: { commentModalTitle }\r\n } = this.props;\r\n\r\n return commentModalTitle.replace('{0}', this.props.cartProductTitle);\r\n };\r\n\r\n public render(): JSX.Element | null {\r\n const {\r\n config: { className }\r\n } = this.props;\r\n const viewProps: ICartLineCommentViewProps = {\r\n ...this.props,\r\n staticDisplay: {\r\n tag: 'div',\r\n className: `${this._className}__static-display`,\r\n children: this._renderStaticCommentDisplay()\r\n },\r\n cartLineCommentModuleProps: {\r\n className: classnames(className, this._className),\r\n tag: 'div',\r\n moduleProps: { ...this.props }\r\n },\r\n modal: this._renderModal()\r\n };\r\n return this.props.renderView(viewProps);\r\n }\r\n}\r\n\r\nexport default CartLineComment;\r\n","/*!\n * Copyright (c) Microsoft Corporation.\n * All rights reserved. See LICENSE in the project root for license information.\n */\n\nimport { ArrayExtensions, StringExtensions } from '@msdyn365-commerce-modules/retail-actions';\nimport { Modal, ModalBody, ModalFooter, ModalHeader } from '@msdyn365-commerce-modules/utilities';\n\n/* ---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\nimport classnames from 'classnames';\nimport * as React from 'react';\n\nimport { InvoiceModel } from '../helpers/invoice-data';\nimport { IInvoicesListResources } from '../invoices-list.props.autogenerated';\n\ninterface IRequestInvoiceModalProps {\n className: string;\n email: string;\n isModalOpen: boolean;\n submitRequestLabel: string;\n modalHeaderLabel: string;\n sendToEmailLabel: string;\n cancelLabel: string;\n searchPlaceholderLabel: string;\n invoices: InvoiceModel[];\n invoicesFoundString: string;\n singleInvoiceFoundString: string;\n searchButtonAltText: string;\n resources: IInvoicesListResources;\n toggle(): void;\n onSubmitRequest(invoiceId: string): void;\n}\n\n/**\n * On submit request click function.\n * @param selectedInvoiceIndex -Check is busy.\n * @param onClosing -On closing function.\n * @param filteredInvoices -Invoice modal.\n * @param onSubmitRequest -On submit request function.\n * @returns Call on submit request.\n */\nconst onSubmitRequestClick = (\n onClosing: () => void,\n filteredInvoices: InvoiceModel[],\n onSubmitRequest: (invoiceId: string) => void,\n checkboxRef: React.MutableRefObject\n) => () => {\n const defaultIndex = 0;\n let closingFlag: number = 0;\n\n filteredInvoices.map((invoice, index) => {\n const inputInvoice = checkboxRef.current[Number(index)] as React.RefObject;\n if (inputInvoice.current?.checked === true && index >= defaultIndex) {\n onSubmitRequest(invoice.id);\n closingFlag = closingFlag + 1;\n }\n });\n if (closingFlag <= filteredInvoices.length) {\n onClosing();\n }\n};\n\n/**\n * On search input change function.\n * @param setSearchInput -Set search input.\n * @returns Set search input function.\n */\nconst searchInputChangedHandler = (setSearchInput: React.Dispatch>) => (\n event: React.ChangeEvent\n) => {\n setSearchInput(event.target.value);\n};\n\n/**\n * Search entered handler.\n * @param invoices -InvoiceModel.\n * @param searchInput -String.\n * @param setFilteredInvoices -InvoiceModel action.\n * @param filteredInvoices -Filtered Invoices.\n * @param setSelectedInvoiceIndex -Selected Invoice Index.\n * @param setShouldShowSearchResults -Show search result action.\n * @param setInvoiceOptions -Invoice options action.\n * @param selectedInvoiceIndex -Selected invoice index.\n * @param className -String.\n * @returns Set invoice options.\n */\nconst searchEnteredHandler = (\n invoices: InvoiceModel[],\n searchInput: string,\n setFilteredInvoices: React.Dispatch>,\n filteredInvoices: InvoiceModel[],\n setSelectedInvoiceIndex: React.Dispatch>,\n setShouldShowSearchResults: React.Dispatch>,\n setInvoiceOptions: React.Dispatch>,\n selectedInvoiceIndex: number,\n className: string,\n checkboxRef: React.MutableRefObject\n) => () => {\n let updatedInvoices = filteredInvoices;\n if (!StringExtensions.isNullOrEmpty(searchInput)) {\n updatedInvoices = invoices.filter(invoice => invoice.id.toLocaleUpperCase().includes(searchInput.toLocaleUpperCase()));\n } else {\n updatedInvoices = invoices;\n }\n\n if (ArrayExtensions.hasElements(updatedInvoices)) {\n setFilteredInvoices(updatedInvoices);\n setSelectedInvoiceIndex(0);\n } else {\n setFilteredInvoices(updatedInvoices);\n setSelectedInvoiceIndex(-1);\n }\n setShouldShowSearchResults(true);\n\n setInvoiceOptions(\n renderInvoiceOptions(\n StringExtensions.isNullOrEmpty(searchInput) ? invoices : updatedInvoices,\n setSelectedInvoiceIndex,\n className,\n checkboxRef\n )\n );\n};\n\n/**\n * On cancel function.\n * @param onClosing -Closing function.\n * @param toggle -Toggle Function.\n * @returns Toggle function.\n */\nconst onCancelHandler = (onClosing: () => void, toggle: () => void) => () => {\n onClosing();\n toggle();\n};\n\n/**\n * On invoice select.\n * @param setSelectedInvoiceIndex -Set Selected Invoice Index.\n * @param index -Number.\n * @returns Set Selected Invoice Index.\n */\nconst onInvoiceSelectHandler = (setSelectedInvoiceIndex: (newIndex: number) => void, index: number) => () => {\n setSelectedInvoiceIndex(index);\n};\n\nconst renderInvoiceOptions = (\n filteredInvoices: InvoiceModel[],\n setSelectedInvoiceIndex: (newIndex: number) => void,\n className: string,\n checkboxRef: React.MutableRefObject\n) => {\n checkboxRef.current = filteredInvoices.map((_invoice, i) => checkboxRef.current[Number(i)] ?? React.createRef());\n return (\n \n {filteredInvoices.map((invoice, index) => {\n const onInvoiceSelect = onInvoiceSelectHandler(setSelectedInvoiceIndex, index);\n\n const checkboxId = `invoice__${invoice.id}`;\n const checkboxName = `invoiceSelect__${invoice.id}`;\n\n return (\n
\n \n \n
\n );\n })}\n
\n );\n};\n\nexport const RequestInvoiceModal: React.FC = ({\n className,\n email,\n isModalOpen,\n toggle,\n modalHeaderLabel,\n sendToEmailLabel,\n submitRequestLabel,\n cancelLabel,\n onSubmitRequest,\n invoices,\n searchPlaceholderLabel,\n invoicesFoundString,\n singleInvoiceFoundString,\n searchButtonAltText,\n resources\n}) => {\n const [selectedInvoiceIndex, setSelectedInvoiceIndex] = React.useState(-1);\n const [filteredInvoices, setFilteredInvoices] = React.useState(invoices);\n const [searchInput, setSearchInput] = React.useState('');\n const [shouldShowSearchResults, setShouldShowSearchResults] = React.useState(false);\n const focusDelay = 110;\n const checkboxRef = React.useRef([]);\n const invoiceOptionsData = renderInvoiceOptions(filteredInvoices, setSelectedInvoiceIndex, className, checkboxRef);\n const [invoiceOptions, setInvoiceOptions] = React.useState(invoiceOptionsData);\n const focusElement = React.useRef(null);\n React.useEffect(() => {\n if (isModalOpen && focusElement.current) {\n setTimeout(() => {\n focusElement.current?.focus();\n }, focusDelay);\n }\n }, [isModalOpen]);\n\n /**\n * Sets the filtered invoices to the ones that were selected on the main screen.\n */\n const setFilteredInvoicesToSelectedInvoices = () => {\n setSelectedInvoiceIndex(-1);\n const selectedInvoices = invoices.filter(invoice => invoice.isSelected);\n if (ArrayExtensions.hasElements(selectedInvoices)) {\n setFilteredInvoices(selectedInvoices);\n\n // If only one invoice selected, auto select it\n if (selectedInvoices.length === 1) {\n setSelectedInvoiceIndex(0);\n }\n\n setInvoiceOptions(renderInvoiceOptions(selectedInvoices, setSelectedInvoiceIndex, className, checkboxRef));\n } else {\n setFilteredInvoices(invoices);\n setInvoiceOptions(renderInvoiceOptions(invoices, setSelectedInvoiceIndex, className, checkboxRef));\n }\n };\n\n const searchEntered = searchEnteredHandler(\n invoices,\n searchInput,\n setFilteredInvoices,\n filteredInvoices,\n setSelectedInvoiceIndex,\n setShouldShowSearchResults,\n setInvoiceOptions,\n selectedInvoiceIndex,\n className,\n checkboxRef\n );\n\n const onClosing = () => {\n setSelectedInvoiceIndex(-1);\n setFilteredInvoices(invoices);\n setSearchInput('');\n setShouldShowSearchResults(false);\n };\n\n /**\n * Sets the invoices that are possible to select when the dialog is opened.\n */\n const onOpened = () => {\n setSearchInput('');\n setFilteredInvoicesToSelectedInvoices();\n };\n\n const searchResultString = filteredInvoices.length === 1 ? singleInvoiceFoundString : invoicesFoundString;\n const invoicesFoundReplaceIndex = searchResultString.indexOf('{0}');\n\n return (\n \n \n {modalHeaderLabel}\n \n \n \n
\n
\n {email}\n
\n
\n {shouldShowSearchResults && (\n
\n {searchResultString.substring(0, invoicesFoundReplaceIndex)}\n {filteredInvoices.length}\n {searchResultString.substring(invoicesFoundReplaceIndex + 3)}\n
\n )}\n
\n \n \n
\n {invoiceOptions}\n
\n
\n \n \n \n \n \n \n );\n};\n","/*!\n * Copyright (c) Microsoft Corporation.\n * All rights reserved. See LICENSE in the project root for license information.\n */\n\nimport { getUrlSync, isOboRequest } from '@msdyn365-commerce/core';\nimport {\n BusinessPartnerOperationDeliveryType,\n BusinessPartnerUsersDataActions,\n InvoicePaidStatus,\n InvoiceSearchCriteria,\n IQueryResultSettings,\n OrderInvoice\n} from '@msdyn365-commerce/retail-proxy';\nimport { getCurrentAsync } from '@msdyn365-commerce/retail-proxy/dist/DataActions/BusinessPartnersDataActions.g';\nimport {\n ArrayExtensions,\n getCustomer,\n GetCustomerInput,\n getInvoices,\n GetInvoicesInput,\n IQueryResultSettingsModuleConfig,\n QueryResultSettingsProxy,\n StringExtensions\n} from '@msdyn365-commerce-modules/retail-actions';\nimport { Alert, Button, Heading, IModuleProps } from '@msdyn365-commerce-modules/utilities';\nimport classnames from 'classnames';\nimport * as React from 'react';\nimport { reaction } from 'mobx';\nimport { observer } from 'mobx-react';\n\nimport { InvoiceErrorComponent } from './components/invoice-error-component';\nimport { InvoicesFilterComponent, InvoicesFilterState } from './components/invoices-filter-component';\nimport { InvoicesTableComponent } from './components/invoices-table/invoices-table-component';\nimport { IMakePaymentResources, MakePaymentComponent } from './components/make-payment-component';\nimport { RequestInvoiceButton } from './components/request-invoice-button';\nimport { RequestInvoiceModal } from './components/request-invoice-modal';\nimport { InvoiceModel } from './helpers/invoice-data';\nimport { IAddInvoicesToCartFailureResult } from './helpers/pay-invoices';\nimport { IInvoicesListData } from './invoices-list.data';\nimport { IInvoicesListProps } from './invoices-list.props.autogenerated';\n\nexport type InvoicesListStatus = 'LOADING' | 'SUCCESS' | 'FAILED' | 'EMPTY';\nexport type InvoiceErrorHost = 'ADDINVOICETOCART';\n\nexport interface IInvoicesListViewProps extends IInvoicesListProps {\n moduleProps: IModuleProps;\n\n header: JSX.Element;\n filter: JSX.Element;\n content?: JSX.Element;\n invoiceRequestModal?: JSX.Element;\n pagination?: JSX.Element;\n}\n\nexport interface IInvoiceErrorState {\n errorHost?: InvoiceErrorHost;\n errorReason?: IAddInvoicesToCartFailureResult;\n errorMessage: string;\n otherError?: string;\n errorCode?: string;\n}\n\nexport interface IInvoicesListState {\n invoices: InvoiceModel[];\n invoicesFilterState: InvoicesFilterState;\n errorState: IInvoiceErrorState;\n isInvoiceRequestModalOpen: boolean;\n isLoading: boolean;\n isOpen: boolean;\n isPageLoaded: boolean;\n payInvoiceErrorModalOpen: boolean;\n selectedButtonElement?: HTMLButtonElement | null;\n}\n\n/**\n * InvoicesList component.\n * @extends {React.Component, IInvoicesListState>}\n */\n@observer\nclass InvoicesList extends React.Component, IInvoicesListState> {\n private readonly _className: string = 'msc-invoices-list';\n\n private readonly payInvoiceRequestReference: React.RefObject;\n\n private readonly makePaymentReference: React.RefObject;\n\n private buttonChildReference: React.RefObject[] = [];\n\n private buttonChildPayButtonReference: React.RefObject[] = [];\n\n private readonly _queryResultSettings: IQueryResultSettings;\n\n private invoiceIds: string[] = [];\n\n constructor(props: IInvoicesListProps