= ({\n templateId,\n readonlyCalendar,\n nylasCalendar,\n onDelete,\n onCreateReadonlyCalendar,\n onCreateNylasLink\n}) => {\n const [showReadonlyDeleteModal, setShowReadonlyDeleteModal] = useState(false);\n const [showNylasDeleteModal, setShowNylasDeleteModal] = useState(false);\n const [showGenerateReadonlyModal, setShowGenerateReadonlyModal] = useState(\n false\n );\n const readonlyLinkExists = readonlyCalendar !== undefined;\n const nylaslinkExists = nylasCalendar !== undefined;\n // @ts-ignore\n return (\n <>\n \n {!nylaslinkExists && (\n \n )}\n {nylaslinkExists && (\n \n )}\n {!readonlyLinkExists && (\n \n )}\n {readonlyLinkExists && (\n \n )}\n \n {readonlyCalendar && (\n setShowReadonlyDeleteModal(false)}\n title={strings.calendars.card.deleteTitle}\n style={{ zIndex: 9999 }}\n footer={\n \n \n \n
\n }\n >\n {strings.calendars.card.deleteReadonlyConfirmation}
\n \n )}\n {nylasCalendar && (\n setShowNylasDeleteModal(false)}\n title={strings.calendars.card.deleteTitle}\n style={{ zIndex: 9999 }}\n footer={\n \n \n \n
\n }\n >\n {strings.calendars.card.deleteHcssConnectConfirmation}
\n \n )}\n setShowGenerateReadonlyModal(false)}\n title={strings.calendars.card.createLink}\n style={{ zIndex: 9999 }}\n footer={\n \n \n \n
\n }\n >\n {strings.calendars.card.deleteReadonlyWarning}
\n {strings.calendars.card.deleteNewReadonlyLink}
\n \n >\n );\n};\n\nconst OptionsButton = styled(DropdownButton)`\n font-size: 1.15rem;\n font-weight: 800;\n text-transform: uppercase;\n letter-spacing: 0.06rem;\n box-shadow: none;\n padding: 5px 20px;\n color: ${gray600};\n\n &:hover:not(:disabled) {\n box-shadow: none;\n }\n`;\n","import React from \"react\";\nimport Clipboard from \"react-clipboard.js\";\nimport styled from \"styled-components\";\nimport config from \"config\";\nimport moment from \"moment\";\nimport { Calendar } from \"api\";\nimport { WithId } from \"core\";\nimport { strings } from \"localization\";\n\nimport {\n FormControl,\n Icon,\n InputGroup,\n notify,\n OverlayTrigger,\n Tooltip\n} from \"hcss-components\";\n\nexport interface ReadOnlyCalendarContentProps {\n templateName: string;\n readonlyCalendar?: WithId;\n}\n\nexport const ReadOnlyCalendarContent: React.FC = ({\n templateName,\n readonlyCalendar\n}) => {\n const link = readonlyCalendar\n ? `${config.endpoints.PRECON}/api/cal/${readonlyCalendar.token}`\n : \"\";\n\n const handleCopy = () => {\n notify(\n \"info\",\n strings.calendars.card.copy.title,\n strings.formatString(\n strings.calendars.card.copy.message,\n templateName\n ) as string\n );\n };\n\n const handleFocus = (event: any) => event.target.select();\n\n return (\n <>\n \n \n {strings.calendars.card.readlonlyLinkLabel}\n
\n \n \n\n \n \n \n \n \n {readonlyCalendar && (\n \n {strings.calendars.card.syncWarning}\n \n }\n placement=\"left\"\n >\n \n \n {strings.formatString(\n strings.calendars.card.lastSync,\n readonlyCalendar.lastPing\n ? moment(readonlyCalendar.lastPing).format(\"l LT\")\n : strings.calendars.card.none\n )}\n \n \n )}\n >\n );\n};\n\nconst ClipboardButton = styled(Clipboard)<{ disabled?: boolean }>`\n cursor: ${props => (props.disabled ? \"not-allowed\" : \"pointer\")};\n`;\n\nconst LinkFormControl = styled(FormControl)`\n background-color: #fff;\n`;\n\nconst LastSync = styled.div`\n margin-top: 0;\n margin-left: 5px;\n`;\n\nconst ContentWrapper = styled.div`\n margin: 15px 0 10px 5px;\n\n div.fieldTitle {\n margin: 5px 0 5px 0;\n font-weight: bold;\n font-size: 14px;\n }\n`;\n","import React, { useCallback, useState } from \"react\";\nimport AsyncSelect from \"react-select\";\nimport { Props as ReactSelectProps } from \"react-select\";\nimport { WithId } from \"../../../core/models\";\nimport { Calendar } from \"../../../api\";\nimport styled from \"styled-components\";\nimport { useDispatch } from \"react-redux\";\nimport { actions } from \"../state\";\nimport ConfirmDelete from \"../../../core/components/modals/ConfirmDelete\";\nimport { Icon, Button } from \"hcss-components\";\nimport { strings } from \"localization\";\nimport { useHcssConnectContext } from \"core/services/hcss-connect\";\nimport { debounce } from \"lodash-es\";\nimport { ListLoadingOverlay } from \"core\";\nimport { track } from \"logging\";\nimport { NylasCalendar } from \"core/services/hcss-connect/models/NylasCalendar\";\n\nexport interface HcssConnectSelectProps {\n viewId: string;\n calendars: NylasCalendar[];\n selectedCalendar?: WithId;\n}\n\n// @ts-ignore\nconst formatGroupLabel = data => (\n \n {data.label}\n {data.options.length}\n
\n);\n\nexport const NylasSelect = ({\n viewId,\n calendars,\n selectedCalendar\n}: HcssConnectSelectProps) => {\n const {\n loadNylasCalendars,\n nylasAccounts,\n loading\n } = useHcssConnectContext();\n const [showReplaceModal, setShowReplaceModal] = useState(false);\n const [newCalId, setNewCalId] = useState();\n const options: GroupedOptions[] = [];\n const dispatch = useDispatch();\n\n calendars\n .filter(\n c =>\n c.name.toLowerCase() !== \"birthdays\" &&\n c.name.toLowerCase() !== \"united states holidays\" &&\n !c.read_only\n )\n .forEach(c => {\n const accountForCal = nylasAccounts.find(\n nylasAccount => nylasAccount.id === c.account_id\n );\n const groupLabel = `${accountForCal?.serviceName}: ${accountForCal?.emailAddress}`;\n let group = options.find(o => o.label === groupLabel);\n if (!group) {\n group = { label: groupLabel, options: [] };\n options.push(group);\n }\n group.options.push({ value: c.id, label: c.name ?? \"(no name)\" });\n });\n\n const selected = calendars.find(\n cal => cal.id === selectedCalendar?.calendarId\n );\n\n const selectedOption: ReactSelectProps[\"value\"] = selected\n ? { value: selected.id, label: selected.name }\n : undefined;\n\n const handleChange = (e: any) => {\n const calId = e?.value;\n if (selectedCalendar?.calendarId === calId) return;\n if (selectedCalendar?.calendarId) {\n setNewCalId(calId);\n setShowReplaceModal(true);\n } else {\n saveCalendar(calId);\n }\n };\n\n const saveCalendar = (id: string) => {\n const cal = calendars.find(c => c.id === id);\n if (cal) {\n dispatch(\n actions.saveCalendar.request({\n viewId,\n accountId: cal.account_id,\n calendarId: cal.id,\n isNylas: true\n })\n );\n track(\"CALENDAR CONNECTED\", {\n module: \"projects\",\n redux: false,\n type: \"Nylas\"\n });\n }\n };\n\n const refreshCalendars = useCallback(\n debounce(loadNylasCalendars, 5000, { leading: true }),\n []\n );\n\n return (\n <>\n \n setShowReplaceModal(false)}\n title={strings.calendars.card.createLink}\n style={{ zIndex: 9999 }}\n footer={\n \n \n \n
\n }\n >\n {strings.calendars.card.deleteHcssConnectWarning}
\n {strings.calendars.card.deleteNewHcssConnectLink}
\n \n >\n );\n};\n\nconst LoadingIndicator = () => {\n return ;\n};\n\ninterface GroupedOptions {\n label: string;\n options: SelectOption[];\n}\ninterface SelectOption {\n label: string;\n value: string;\n}\n\nconst groupStyles = {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n marginLeft: 0,\n paddingLeft: 0\n};\nconst groupBadgeStyles = {\n backgroundColor: \"#EBECF0\",\n borderRadius: \"2em\",\n color: \"#172B4D\",\n display: \"inline-block\",\n fontSize: 12,\n lineHeight: \"1\",\n minWidth: 1,\n padding: \"0.16666666666667em 0.5em\"\n};\nconst StyledSelect = styled(AsyncSelect)`\n div.react-select__menu {\n z-index: 11;\n }\n`;\n","import React from \"react\";\nimport { Calendar } from \"api\";\nimport { Badge, WithId } from \"core\";\nimport { InputGroup } from \"hcss-components\";\nimport styled from \"styled-components\";\nimport { NylasSelect } from \"./NylasSelect\";\nimport { strings } from \"../../../localization\";\nimport { NylasCalendar } from \"core/services/hcss-connect/models/NylasCalendar\";\n\nexport interface NylasCalendarContentProps {\n viewId: string;\n nylasCalendar?: WithId;\n nylasCalendars: NylasCalendar[];\n}\n\nexport const NylasCalendarContent: React.FC = ({\n viewId,\n nylasCalendar,\n nylasCalendars\n}) => {\n return (\n <>\n \n \n {strings.calendars.card.nylasLinkLabel}\n \n {strings.featureStatus.beta}\n \n
\n \n \n \n \n >\n );\n};\n\nconst ContentWrapper = styled.div`\n margin: 5px 0 10px 5px;\n\n div.fieldTitle {\n display: flex;\n justify-content: space-between;\n margin: 5px 0 5px 0;\n font-weight: bold;\n font-size: 14px;\n }\n`;\n","import React, { useMemo } from \"react\";\nimport { useDispatch, useSelector } from \"react-redux\";\nimport styled from \"styled-components\";\nimport { View } from \"../../../api\";\nimport { selectors, actions } from \"../state\";\nimport { ModalPanel, ModalPanelProps } from \"core/components/modals/SideModal\";\nimport { ContentLoadingOverlay } from \"core/components/loaders\";\nimport { strings } from \"localization\";\nimport { CalendarSubscriptionCard } from \"./CalendarSubscriptionCard\";\nimport { InactiveOptionsButton } from \"./InactiveOptionsButton\";\nimport { ActiveOptionsButton } from \"./ActiveOptionsButton\";\nimport { ReadOnlyCalendarContent } from \"./ReadonlyCalendarContent\";\nimport { useHcssConnectContext } from \"core/services/hcss-connect\";\nimport { track } from \"logging\";\nimport { NylasCalendarContent } from \"./NylasCalendarContent\";\nimport { actions as settingsActions } from \"modules/settings\";\n\ninterface CalendarSubscribePanelProps extends ModalPanelProps {\n views?: View[];\n}\nexport const CalendarSubscribePanel = ({\n isActive,\n onHide,\n views\n}: CalendarSubscribePanelProps) => {\n const { nylasCalendars } = useHcssConnectContext();\n const dispatch = useDispatch();\n const handleDelete = (calendarId: string) => {\n dispatch(actions.deleteCalendar.request(calendarId));\n };\n\n const handleCreateReadonlyLink = (viewId: string) => {\n track(\"CALENDAR CONNECTED\", {\n module: \"projects\",\n redux: false,\n type: \"iCal\"\n });\n dispatch(\n actions.saveCalendar.request({\n viewId,\n isNylas: false\n })\n );\n };\n\n const handleCreateNylasLink = (viewId: string) => {\n dispatch(\n actions.saveCalendar.request({\n viewId,\n isNylas: true\n })\n );\n };\n\n const calendars = useSelector(selectors.getCalendarsByViewId);\n\n const loaded = useSelector(selectors.getLoaded);\n\n return (\n \n \n \n \n {strings.calendars.subscribePanel.links}\n \n {strings.calendars.subscribePanel.visible}\n \n \n \n dispatch(settingsActions.toggleCalendarPanel())}\n >\n {strings.calendars.subscribePanel.openAccountSettingsButton}\n \n {strings.calendars.subscribePanel.openAccountSettingsAfter}\n \n \n {!loaded || !views ? (\n \n ) : (\n \n {views.map((view: View) => {\n const calendarsForView = calendars[view.id];\n const readOnlyCalendar = calendarsForView?.iCal;\n const nylasCalendar = calendarsForView?.nylas;\n return (\n \n ) : (\n \n )\n }\n CardContent={\n <>\n {nylasCalendar && (\n \n )}\n {readOnlyCalendar && (\n \n )}\n >\n }\n />\n );\n })}\n \n )}\n \n \n
\n );\n};\n\nconst StyledPanel = styled(ModalPanel)`\n z-index: 2002;\n\n .modal-panel-content {\n .modal-panel-body {\n display: flex;\n flex-wrap: nowrap;\n flex-direction: column;\n overflow: hidden;\n padding: 0;\n\n .panel-instructions {\n position: relative;\n margin: 10px 0;\n padding: 0 20px;\n margin-bottom: 20px;\n font-size: 14px;\n line-height: 22px;\n color: #1e1e1e;\n }\n }\n }\n`;\n\nconst TitleLabel = styled.div`\n padding-left: 30px;\n margin-top: 16px;\n`;\nconst OpenAccountSettingsButton = styled.button`\n background: none;\n border: none;\n padding: 0;\n color: #069;\n cursor: pointer;\n`;\n\nconst StyledDivWrapper = styled.div`\n padding-top: 20px;\n margin-left: 10px;\n margin-right: 10px;\n flex-grow: 1;\n overflow-y: auto;\n overflow-x: hidden;\n`;\n","import React, { useEffect } from \"react\";\nimport { CalendarSubscribePanel } from \"modules/calendars/components/CalendarSubscribePanel\";\nimport { selectors as viewSelectors } from \"modules/views\";\nimport { useSelector, useDispatch } from \"react-redux\";\nimport * as calendarModule from \"modules/calendars\";\n\nexport const ProjectsCalendarSubscribeModal = () => {\n const dispatch = useDispatch();\n const views = useSelector(viewSelectors.getProjectViewsSorted);\n const isActive = useSelector(calendarModule.selectors.getCalendarPanelActive);\n\n useEffect(() => {\n if (isActive) {\n dispatch(calendarModule.actions.loadCalendars.request());\n }\n }, [dispatch, isActive]);\n\n return (\n dispatch(calendarModule.actions.toggleSideModal(false))}\n views={views}\n />\n );\n};\n","import React, { useEffect, useState, ReactElement } from \"react\";\nimport styled from \"styled-components\";\nimport { useDispatch, useSelector } from \"react-redux\";\nimport { actions, selectors } from \"../state\";\nimport {\n Button,\n ConcreteColors,\n Icon,\n Link,\n OverlayTrigger,\n Popover\n} from \"hcss-components\";\nimport { Box } from \"hcss-core\";\nimport moment from \"moment\";\nimport { strings } from \"../../../localization\";\nimport { ModalPanel } from \"../../../core/components/modals/SideModal\";\nimport {\n Badge,\n ContentLoadingOverlay,\n TextOverflow\n} from \"../../../core/components\";\nimport ConfirmDelete from \"../../../core/components/modals/ConfirmDelete\";\nimport {\n useHcssConnectContext,\n UsableAccount\n} from \"core/services/hcss-connect\";\nimport { selectors as calendarSelectors } from \"modules/calendars\";\n\nconst { gray600 } = ConcreteColors;\n\nexport const HcssConnectConfigurationPanel = () => {\n const {\n nylasAccounts,\n loading: calendarLoading,\n deleteNylasAccount,\n nylasCalendars,\n connectAccount\n } = useHcssConnectContext();\n const dispatch = useDispatch();\n const calendarsLoaded = useSelector(calendarSelectors.getLoaded);\n const loading = calendarLoading && !calendarsLoaded;\n const isActive = useSelector(selectors.getCalendarPanelActive);\n const redirectQueryArg = \"kloudlessRedirect=true\";\n const calendarsByAccountId = useSelector(\n calendarSelectors.getCalendarsByAccountId\n );\n\n const onHide = () => dispatch(actions.toggleCalendarPanel());\n\n useEffect(() => {\n if (window.location.href.includes(redirectQueryArg)) {\n dispatch(actions.toggleCalendarPanel(true));\n }\n }, [dispatch]);\n\n return (\n \n
\n } />\n \n \n \n {strings.hcssConnect.subscribePanel.connectAccountButton}\n \n
\n {nylasAccounts.length > 0 && (\n \n {strings.hcssConnect.subscribePanel.linkedAccounts}\n
\n )}\n \n {!loading && (\n \n {nylasAccounts\n .sort(account => (account.isBad ? 1 : -1))\n .map(account => {\n const accountCalendars = calendarsByAccountId[account.id];\n return (\n \n nylasCalendars?.find(nc => nc.id === c.calendarId)\n ).length > 0\n }\n isBad={account.isBad}\n />\n );\n })}\n \n )}\n {loading && }\n \n \n
\n );\n};\n\nconst ModalHeader = () => {\n return (\n \n
{strings.hcssConnect.subscribePanel.title}
\n
\n {strings.featureStatus.beta}\n \n
\n );\n};\n\ninterface CalendarAccountCardProps {\n account: UsableAccount;\n onDelete: (accountId: string) => void;\n isBad?: boolean;\n hasExistingCalendars?: boolean;\n}\n\nexport interface CalendarAccountCardWrapperProps {\n hasExistingCalendars?: boolean;\n children: ReactElement;\n}\n\nconst CalendarAccountCardWrapper = ({\n hasExistingCalendars,\n children\n}: CalendarAccountCardWrapperProps) => {\n if (!hasExistingCalendars) return children;\n return (\n \n \n {strings.hcssConnect.card.deletePopover}\n
\n \n }\n >\n {children}\n \n );\n};\n\nconst CalendarAccountCard = ({\n account,\n onDelete,\n isBad,\n hasExistingCalendars\n}: CalendarAccountCardProps) => {\n const [showDeleteModal, setShowDeleteModal] = useState(false);\n const CardContainer = isBad\n ? BadLinkContainer\n : moment().diff(account.created, \"minutes\") <= 1\n ? NewLinkContainer\n : LinkContainer;\n\n return (\n \n \n \n \n \n {\n setShowDeleteModal(true);\n }}\n >\n \n \n \n \n\n setShowDeleteModal(false)}\n title={strings.hcssConnect.card.deleteTitle}\n style={{ zIndex: 9999 }}\n footer={\n \n \n \n
\n }\n >\n {strings.hcssConnect.card.deleteConfirmation}
\n \n \n {account.emailAddress}\n \n );\n};\n\nconst HelpTrigger = ({ serviceName }: { serviceName: string }) => {\n return (\n \n \n {strings.formatString(\n strings.hcssConnect.card.helper.error,\n serviceName\n )}\n
\n
\n {strings.hcssConnect.card.helper.recommendDelete}\n \n }\n >\n \n \n \n \n );\n};\n\nconst StyledPanel = styled(ModalPanel)`\n z-index: 2002;\n\n .modal-panel-content {\n .modal-panel-body {\n display: flex;\n flex-wrap: nowrap;\n flex-direction: column;\n overflow: hidden;\n padding: 0;\n\n .panel-instructions {\n position: relative;\n margin: 10px 0;\n padding: 0 20px;\n margin-bottom: 20px;\n font-size: 14px;\n line-height: 22px;\n color: #1e1e1e;\n }\n }\n }\n`;\n\nconst TitleLabel = styled.label`\n padding-left: 30px;\n margin-top: 16px;\n\n .warning {\n padding-left: 0px;\n }\n`;\nconst StyledDivWrapper = styled.div`\n margin-left: 10px;\n margin-right: 10px;\n flex-grow: 1;\n overflow-y: auto;\n overflow-x: hidden;\n`;\nconst NewAccountButton = styled(Link)`\n background: none;\n border: none;\n padding: 0;\n color: #069;\n cursor: pointer;\n`;\nconst LinkContainer = styled.div`\n padding: 24px;\n margin: 20px 12px;\n background-color: #f6f6f6;\n border-radius: 12px;\n`;\n\nconst NewLinkContainer = styled.div`\n padding: 24px;\n margin: 20px 12px;\n background-color: #e0ffe0;\n border-radius: 12px;\n`;\n\nconst BadLinkContainer = styled.div`\n padding: 24px;\n margin: 20px 12px;\n background-color: #f8d7da;\n border-radius: 12px;\n`;\n\nconst LinkHeader = styled.div`\n .template-wrap {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n width: 270px;\n }\n\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n`;\n\nconst DeleteButton = styled(Button)`\n font-size: 1.15rem;\n font-weight: 800;\n text-transform: uppercase;\n letter-spacing: 0.06rem;\n box-shadow: none;\n padding: 5px 20px;\n color: ${gray600};\n\n &:hover:not(:disabled) {\n box-shadow: none;\n }\n`;\n","import { BusinessUnit } from \"api/GeneratedClients/precon\";\nimport { DataColumnType, TypedDataColumn } from \"hcss-tables\";\n\nexport interface BUSelect extends BusinessUnit {\n selected: boolean;\n home: boolean;\n}\ninterface CompareValue {\n home: boolean;\n code: string;\n}\n\nconst CodeColumn: TypedDataColumn = {\n name: \"code\",\n title: \"Code\",\n type: DataColumnType.Custom,\n getCellValue: (row: BUSelect) => {\n return { home: row.home, code: row.code };\n },\n config: {\n sortingFunction: (a: CompareValue, b: CompareValue) => {\n const priorityA = a.code.toLowerCase();\n const priorityB = b.code.toLowerCase();\n if (priorityA === priorityB) return 0;\n return priorityA < priorityB ? -1 : 1;\n },\n valueAsString: (value: CompareValue) => value.code\n }\n};\nconst DescriptionColumn: TypedDataColumn = {\n name: \"description\",\n title: \"Description\",\n type: DataColumnType.ShortText,\n getCellValue: (row: BUSelect) => row.description ?? \"\"\n};\n\nexport const businessUnitColumns: TypedDataColumn[] = [\n CodeColumn,\n DescriptionColumn\n];\n","import {\n DataTypeProvider,\n IntegratedSelection,\n TableColumnWidthInfo\n} from \"@devexpress/dx-react-grid\";\nimport {\n Grid,\n SearchPanel,\n Table,\n TableColumnResizing,\n TableHeaderRow,\n TableSelection,\n Toolbar\n} from \"@devexpress/dx-react-grid-bootstrap3\";\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport { BusinessUnit } from \"api/GeneratedClients/precon\";\nimport { isNullOrWhitespace } from \"core\";\nimport { ConfirmSubmitModal } from \"core/components/modals/confirm-submit\";\nimport { useLocalStorage } from \"hcss-hooks\";\nimport {\n BooleanFormatProvider,\n getRowId,\n IntegratedFiltering,\n Root,\n SearchState,\n SelectionState,\n SortingState,\n TableContainer,\n TableWrapper,\n // TableHeaderRow,\n IntegratedSorting\n} from \"hcss-tables\";\nimport { selectors as accountSelectors } from \"modules/account\";\nimport { StyledSelectionRow } from \"modules/tables/selection-table\";\nimport React, { useMemo, useState } from \"react\";\nimport { useDispatch, useSelector } from \"react-redux\";\nimport styled from \"styled-components\";\nimport { ModalPanel } from \"../../../core/components/modals/SideModal\";\nimport { actions, selectors } from \"../state\";\nimport { businessUnitColumns } from \"./BusinessUnitColumns\";\n\nexport const BusinessUnitPanel = () => {\n const dispatch = useDispatch();\n const isActive = useSelector(selectors.getBUPanelActive);\n const BUs = useSelector(accountSelectors.getAccessibleBusinessUnits);\n const homeBU = useSelector(accountSelectors.getHomeBusinessUnit);\n const selectedBU = useSelector(accountSelectors.getSelectedBusinessUnit);\n const hcssUser = useSelector(accountSelectors.getHcssUser);\n\n const [showModal, setShowModal] = useState(false);\n const [chosenRowId, setChosenRowId] = useState(\"\");\n\n const [_, setLocalStorageBU] = useLocalStorage<{\n id: string | undefined;\n code: string | undefined;\n }>(`user.business.unit-${hcssUser?.hcssUserId}`, homeBU);\n\n const businessUnits = useMemo(() => {\n return (\n BUs?.map(bu => {\n if (isNullOrWhitespace(selectedBU.id) && bu.code === \"MANAGER\")\n return { ...bu, selected: true };\n\n if (bu.id === selectedBU.id) return { ...bu, selected: true };\n\n return { ...bu, selected: false };\n }).map(bu => {\n if (bu.id === homeBU.id) return { ...bu, home: true };\n return { ...bu, home: false };\n }) ?? []\n );\n }, [BUs, homeBU.id, selectedBU.id]);\n\n const onHide = () => {\n setShowModal(false);\n dispatch(actions.toggleBUPanel());\n };\n\n const handleSelection = (change: (string | number)[]) => {\n const selectedRowId = change[change.length - 1];\n if (change.length > 0) {\n setChosenRowId(selectedRowId);\n setShowModal(true);\n }\n };\n\n const handleConfirm = () => {\n const selectedBU = businessUnits.find(bu => bu.id === chosenRowId);\n const businessUnit = {\n id: selectedBU?.id ?? \"\",\n code: selectedBU?.code ?? \"\"\n };\n setLocalStorageBU(businessUnit);\n setShowModal(false);\n window.location.reload();\n };\n\n const handleClose = () => {\n setShowModal(false);\n };\n\n return (\n \n \n } />\n \n \n \n \n \n The site will reload with your newly selected business unit. Any unsaved\n work will be lost.\n \n
\n );\n};\n\nconst ModalHeader = () => {\n return Change Business Unit
;\n};\n\ninterface BUTableProps {\n businessUnits: BusinessUnit[] | undefined;\n selection: (string | number)[] | undefined;\n handleSelection: (change: (string | number)[]) => void;\n}\n\nconst BUTable = ({\n businessUnits,\n selection,\n handleSelection\n}: BUTableProps) => {\n const [columnWidths, setColumnWidths] = useState(\n defaultWidths\n );\n\n return (\n \n
\n \n \n \n \n \n\n \n \n \n\n \n \n\n \n \n \n \n \n \n \n \n \n
\n );\n};\n\nconst HomeBUFormatter = ({ value }: { value: any }) => {\n return (\n \n {value.code}\n
\n {value.home && }\n
\n
\n );\n};\n\nconst HomeBUProvider = (props: any) => (\n \n);\n\nconst defaultWidths = [\n { columnName: \"code\", width: 150 },\n { columnName: \"description\", width: 315 }\n];\n\nconst StyledPanel = styled(ModalPanel)`\n z-index: 2002;\n\n .modal-panel-content {\n .modal-panel-body {\n display: flex;\n flex-wrap: nowrap;\n flex-direction: column;\n overflow: hidden;\n padding: 0;\n\n .panel-instructions {\n position: relative;\n margin: 10px 0;\n padding: 0 20px;\n margin-bottom: 20px;\n font-size: 14px;\n line-height: 22px;\n color: #1e1e1e;\n }\n }\n }\n`;\n","import React, { ReactNode } from \"react\";\nimport {\n HeaderSection,\n Item,\n MenuSection\n} from \"core/components/hcss-navigation\";\nimport { ConcreteColors, Icon, Link } from \"hcss-components\";\nimport styled from \"styled-components\";\nimport { strings } from \"localization\";\n\ninterface HeaderSectionWithBackButtonProps {\n backPath: string;\n children?: ReactNode;\n}\nexport const HeaderSectionWithBackButton = ({\n backPath,\n children\n}: HeaderSectionWithBackButtonProps) => {\n return (\n \n {headerStyle => {\n return (\n \n \n \n {strings.core.buttons.back}\n \n {children}\n \n
\n );\n }}\n \n );\n};\n\nconst HeaderContainer = styled.div`\n padding-bottom: 26px;\n padding-left: 12px;\n padding-top: 8px;\n font-size: 16px;\n font-weight: 700;\n color: ${ConcreteColors.gray600};\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n`;\n\nconst BackLink = styled(Link)`\n font-size: 12px;\n font-weight: 400;\n color: ${ConcreteColors.gray600};\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n`;\n","import { Badge } from \"@mui/material\";\nimport { FeatureFlag } from \"core\";\nimport { Item, MenuSection } from \"core/components/hcss-navigation\";\nimport { ConcreteColors, Icon, OverlayTrigger, Tooltip } from \"hcss-components\";\nimport { strings } from \"localization\";\nimport { usePermissions, useUserAndCompanyLicense } from \"modules/account\";\nimport { useFeatureFlags } from \"modules/configurationSettings/use-feature-flags\";\nimport { SubscriptionStatus, selectors } from \"modules/email-subscriptions\";\nimport { default as React, useMemo } from \"react\";\nimport { useSelector } from \"react-redux\";\nimport { useLocation } from \"react-router-dom\";\nimport styled, { createGlobalStyle } from \"styled-components\";\nimport { paths } from \"../../../../projects/views/paths\";\nimport { HeaderSectionWithBackButton } from \"../../common/header-section-with-back-button\";\n\nexport const FailedSubscriptionsBadge = () => {\n const subscriptions = useSelector(selectors.getSubscriptions);\n\n const stopedCount = useMemo(\n () =>\n subscriptions.reduce(\n (acc, curr) => acc + +(curr.status === SubscriptionStatus.STOPPED),\n 0\n ),\n [subscriptions]\n );\n\n return (\n \n \n {\n strings.layout.menu.projectTracking.subscriptions\n .stoppedSubscriptions\n }\n \n }\n >\n \n \n );\n};\n\nexport const CustomizeSetupContext = () => {\n const path = useLocation().pathname;\n const { companyLicense } = useUserAndCompanyLicense();\n const isLimitedCompany = companyLicense.isLimitedLicense;\n const permissions = usePermissions();\n const {\n isFeatureEnabled: isFrontEndEmailSubscriptionEnabled\n } = useFeatureFlags(FeatureFlag.FrontEndEmailSubscription);\n const { isFeatureEnabled: isQuickPricingPhase1Enabled } = useFeatureFlags(\n FeatureFlag.QuickPricingPhase1\n );\n\n return (\n \n
\n {strings.layout.menu.projectTracking.customize}
\n \n
\n {menuStyle => (\n \n \n \n }\n />\n {isFrontEndEmailSubscriptionEnabled && (\n }\n to={paths.subscriptionSetup}\n />\n )}\n {isQuickPricingPhase1Enabled && permissions.contactWrite && (\n \n )}\n \n )}\n \n
\n );\n};\n\nconst ItemContainer = styled.div`\n font-size: 14px;\n margin-right: 14px;\n color: ${props => props.theme.colors.neutral[0]};\n`;\n\nconst TooltipStyling = createGlobalStyle`\n .tooltip {\n background-color: ${ConcreteColors.gray700};\n margin: 0;\n border-radius: 4px;\n pointer-events:none;\n .tooltip-arrow {\n display: none;\n }\n \n .tooltip-inner {\n background-color: transparent;\n font-weight: 500;\n font-size: 13px;\n }\n }\n`;\n","import { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport { Badge } from \"@mui/material\";\nimport { FeatureFlag } from \"core\";\nimport {\n HeaderSection,\n Item,\n MenuSection\n} from \"core/components/hcss-navigation/\";\nimport { ConcreteColors, OverlayTrigger, Tooltip } from \"hcss-components\";\nimport { strings } from \"localization\";\nimport { useAuthorization, usePermissions } from \"modules/account\";\nimport { useFeatureFlags } from \"modules/configurationSettings/use-feature-flags\";\nimport { actions } from \"modules/email-subscriptions\";\nimport { selectors as estimateSelectors } from \"modules/estimates\";\nimport React, { useEffect } from \"react\";\nimport { useDispatch, useSelector } from \"react-redux\";\nimport { useHistory, useLocation } from \"react-router-dom\";\nimport { createGlobalStyle } from \"styled-components\";\nimport { FailedSubscriptionsBadge } from \"../context/customize-setup\";\nimport { HeaderContainer, ItemContainer, ModuleTitle } from \"./styles\";\n\nexport const ProjectTrackingModule = () => {\n const {\n isFeatureEnabled: isFrontEndEmailSubscriptionEnabled\n } = useFeatureFlags(FeatureFlag.FrontEndEmailSubscription);\n const dispatch = useDispatch();\n const path = useLocation().pathname;\n const history = useHistory();\n const permissions = usePermissions();\n const auth = useAuthorization();\n\n const unlinkedEstimateCount = useSelector(\n estimateSelectors.getUnlinkedAndUnhiddenEstimatesCount\n );\n\n const canImport =\n auth.canAccessLimitedPrecon && (permissions.admin || permissions.write);\n\n const isFreeCompany =\n auth.canAccessLimitedPrecon && !auth.canAccessFullPrecon;\n\n const canCustomize = isFreeCompany || permissions.admin;\n const headerText = strings.layout.menu.projectTracking.customize;\n\n useEffect(() => {\n if (!isFrontEndEmailSubscriptionEnabled) {\n return;\n }\n\n dispatch(actions.loadSubscriptions.request());\n }, [dispatch, isFrontEndEmailSubscriptionEnabled]);\n\n return (\n \n
\n {headerStyle => {\n return (\n \n \n \n {strings.layout.menu.projectTracking.title}\n \n \n
\n );\n }}\n \n
\n {menuStyle => (\n \n }\n text={strings.layout.menu.projectTracking.list}\n isSelected={path === \"/projects\"}\n onClick={() => history.push(\"/projects\")}\n data-testid=\"nav-projects-list\"\n />\n\n }\n text={strings.layout.menu.projectTracking.estimateMapping.title}\n isSelected={path === \"/projects/estimate-mapping\"}\n onClick={() => history.push(\"/projects/estimate-mapping\")}\n data-testid=\"nav-projects-estimate-mapping\"\n after={\n \n \n {\n strings.layout.menu.projectTracking.estimateMapping\n .hover\n }\n \n }\n >\n \n \n }\n />\n {canImport && (\n \n }\n text={strings.layout.menu.projectTracking.import}\n isSelected={path === \"/projects/import-projects\"}\n onClick={() => history.push(\"/projects/import-projects\")}\n data-testid=\"nav-projects-import\"\n />\n )}\n {canCustomize && (\n \n }\n text={headerText}\n isSelected={path === \"/projects/setup\"}\n onClick={() => history.push(\"/projects/setup\")}\n after={\n isFrontEndEmailSubscriptionEnabled && (\n \n )\n }\n data-testid=\"nav-projects-customize\"\n />\n )}\n \n )}\n \n
\n );\n};\n\nconst TooltipStyling = createGlobalStyle`\n .tooltip {\n background-color: ${ConcreteColors.gray700};\n margin: 0;\n border-radius: 4px;\n pointer-events:none;\n .tooltip-arrow {\n display: none;\n }\n \n .tooltip-inner {\n background-color: transparent;\n font-weight: 500;\n font-size: 13px;\n }\n }\n`;\n","import * as React from \"react\";\nimport { useRecentEstimatesContext } from \"./use-recent-estimates\";\nimport { useMemo } from \"react\";\nimport { Link } from \"hcss-components\";\nimport styled from \"styled-components\";\nimport { Box } from \"hcss-core\";\nimport { getPathForEstimate } from \"modules/estimates/views/paths\";\nimport { track } from \"logging\";\nimport { SectionHeading } from \"core/components/hcss-navigation\";\nimport { TextOverflow } from \"core\";\nimport { strings } from \"localization\";\n\nexport const RecentEstimateList = () => {\n const { recentEstimates } = useRecentEstimatesContext();\n const options = useMemo(() => {\n return recentEstimates.slice(0, 5).map(e => {\n return {\n key: e.id ?? \"\",\n mainText: e.values.code ?? \"(no code)\",\n subText: e.values.name ?? \"(no name)\"\n };\n });\n }, [recentEstimates]);\n if (recentEstimates.length === 0) return null;\n return (\n \n
\n {strings.layout.menu.estimating.recentEstimates}\n \n
\n {options.map(option => (\n \n track(\"RECENT ESTIMATE CLICK\")}\n >\n {option.mainText}\n {option.subText}\n \n \n ))}\n
\n
\n );\n};\n\nconst EstimateLink = styled(Box)`\n color: ${props => props.theme.colors.neutral[1]};\n text-decoration: none;\n min-width: 100%;\n font-size: 12px;\n transition: all 0.2s ease;\n display: flex;\n flex-direction: column;\n max-width: 300px;\n\n &:hover {\n color: #0370f5;\n text-decoration: none;\n }\n`;\n","import React from \"react\";\nimport { useLocation, useHistory } from \"react-router-dom\";\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport { strings } from \"localization\";\nimport { HeaderContainer, ModuleTitle, ItemContainer } from \"./styles\";\nimport { faBooks } from \"@fortawesome/pro-light-svg-icons\";\nimport {\n HeaderSection,\n MenuSection,\n Item\n} from \"core/components/hcss-navigation/\";\nimport { RecentEstimateList } from \"modules/estimates/components/recent-estimates/recent-estimate-list\";\nimport { useAuthorization, usePermissions } from \"modules/account\";\nimport { DisableInSandbox, HideInSandbox } from \"core/util/sandbox\";\nimport { useFeatureFlags } from \"modules/configurationSettings/use-feature-flags\";\n\nexport const EstimatingModule = () => {\n const path = useLocation().pathname;\n const history = useHistory();\n const permissions = usePermissions();\n const auth = useAuthorization();\n const enableCustomizeReporting =\n permissions.admin && auth.canAccessFullPrecon;\n const { isFeatureEnabled: isCodebooksFeatureEnabled } = useFeatureFlags(\n \"Codebooks\"\n );\n\n const iconStyle = { width: \"10px\" };\n return (\n \n
\n {headerStyle => {\n return (\n \n \n \n {strings.layout.menu.estimating.title}\n \n \n
\n );\n }}\n \n
\n {menuStyle => (\n \n }\n text={strings.layout.menu.estimating.list}\n isSelected={path === \"/estimates\"}\n onClick={() => history.push(\"/estimates\")}\n data-testid=\"navitem-estimating-list\"\n beforeWrapperStyle={iconStyle}\n />\n {isCodebooksFeatureEnabled && (\n \n }\n text={strings.layout.menu.estimating.codebooks}\n isSelected={path === \"/estimates/codebooks\"}\n onClick={() => history.push(\"/estimates/codebooks\")}\n data-testid=\"navitem-estimating-codebooks\"\n beforeWrapperStyle={iconStyle}\n />\n \n )}\n \n \n }\n text={strings.layout.menu.estimating.edit}\n isSelected={path === \"/estimates/edit\"}\n onClick={() => history.push(\"/estimates/edit\")}\n data-testid=\"navitem-estimating-edit\"\n beforeWrapperStyle={iconStyle}\n />\n \n {enableCustomizeReporting && (\n \n }\n text={strings.layout.menu.estimating.customize}\n isSelected={path === \"/estimates/customize\"}\n onClick={() => history.push(\"/estimates/customize\")}\n data-testid=\"navitem-customize-reporting\"\n beforeWrapperStyle={iconStyle}\n />\n )}\n \n \n )}\n \n
\n );\n};\n","import { TextOverflow } from \"core\";\nimport { Flex } from \"hcss-core\";\nimport React from \"react\";\nimport { Link } from \"react-router-dom\";\nimport { actions } from \"modules/contacts\";\nimport { useDispatch } from \"react-redux\";\nimport { CompanyAvatar } from \"../../../../core/components/avatar\";\n\ninterface CompanyCardProps {\n companyId: string;\n companyName: string;\n companyCode: string;\n}\nexport const CompanyCard = ({\n companyId,\n companyName,\n companyCode\n}: CompanyCardProps) => {\n const dispatch = useDispatch();\n return (\n \n \n \n \n dispatch(actions.setShowSearchModal(false))}\n >\n {companyName}\n \n \n\n \n {companyCode}\n \n \n \n );\n};\n","import { Avatar, List, ListItem, Box } from \"@mui/material\";\nimport React, { ReactNode } from \"react\";\nimport { EmailField, PhoneField, NoDataPlaceholder, TextOverflow } from \"core\";\nimport { Flex } from \"hcss-core\";\nimport { LocalPhone, PhoneAndroid, Email } from \"@mui/icons-material\";\nimport { blue, green } from \"@mui/material/colors\";\nimport { CompanyCard } from \"./company-card\";\nimport { IGetContactSearchDto } from \"api/GeneratedClients/ContactsClient\";\nimport { strings } from \"localization\";\nexport interface ContactListProps {\n contacts: IGetContactSearchDto[];\n}\n\nexport const ContactList = ({ contacts }: ContactListProps) => {\n return (\n \n <>\n {contacts.length === 0 && (\n \n )}\n {contacts.length > 0 &&\n contacts.map(contact => (\n \n \n \n ))}\n >\n
\n );\n};\n\nexport interface ContactListItemProps {\n contact: IGetContactSearchDto;\n}\n\nconst ContactCard = ({ contact }: ContactListItemProps) => {\n return (\n \n \n \n \n \n {contact.firstName || \"--\"} {contact.lastName || \"--\"}\n \n \n\n \n \n \n \n ) : (\n \"--\"\n )\n }\n icon={}\n iconColor={green[400]}\n />\n \n ) : (\n \"--\"\n )\n }\n icon={}\n iconColor={green[600]}\n />\n \n ) : (\n \"--\"\n )\n }\n icon={}\n iconColor={blue[400]}\n />\n \n \n \n );\n};\n\ninterface ContactInfoFieldProps {\n fieldInfo: ReactNode;\n icon: ReactNode;\n iconColor: string;\n}\n\nconst ContactInfoField = ({\n fieldInfo,\n icon,\n iconColor\n}: ContactInfoFieldProps) => {\n return (\n \n \n {fieldInfo}\n \n \n {icon}\n \n \n );\n};\n","import { List, ListItem, Box } from \"@mui/material\";\nimport React from \"react\";\nimport { NoDataPlaceholder } from \"core\";\nimport { Flex } from \"hcss-core\";\nimport { CompanyData } from \"./modal\";\nimport { CompanyCard } from \"./company-card\";\nimport { strings } from \"localization\";\nexport interface TempContactDto {\n id?: string;\n companyId?: string;\n companyCode?: string;\n companyName?: string;\n firstName?: string | undefined;\n lastName?: string | undefined;\n title?: string | undefined;\n phoneNumber?: string | undefined;\n cellPhoneNumber?: string | undefined;\n emailAddress?: string | undefined;\n}\n\nexport interface CompanyListProps {\n companies: CompanyData[];\n}\n\nexport const CompanyList = ({ companies }: CompanyListProps) => {\n return (\n \n <>\n {companies.length === 0 && (\n \n )}\n {companies.map(company => (\n \n \n \n ))}\n >\n
\n );\n};\n\nexport interface ContactListItemProps {\n company: CompanyData;\n}\n\nconst CompanyItem = ({ company }: ContactListItemProps) => {\n return (\n \n \n \n \n \n );\n};\n","const NUM_ITEMS_PER_PAGE = 4;\n\nexport const calculateNumPages = (\n results: any[],\n numItemsPerPage = NUM_ITEMS_PER_PAGE\n) => {\n return Math.ceil(results.length / numItemsPerPage);\n};\n\nexport const getCurrentPage = (\n currentPage: number,\n results: any[],\n numItemsPerPage = NUM_ITEMS_PER_PAGE\n) => {\n if (results.length === 0) return [];\n if (results.length <= numItemsPerPage) return results;\n const firstItemInCurrentPageIndex =\n numItemsPerPage * currentPage - results.length;\n if (firstItemInCurrentPageIndex > 0) {\n return results.slice(-(numItemsPerPage - firstItemInCurrentPageIndex));\n }\n return results.slice(\n (currentPage - 1) * numItemsPerPage,\n numItemsPerPage * currentPage\n );\n};\n","import { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport React, {\n forwardRef,\n ReactElement,\n Ref,\n useEffect,\n useMemo,\n useState\n} from \"react\";\nimport {\n Badge,\n Box,\n DialogContent,\n DialogTitle,\n InputBase,\n Slide,\n Tab,\n Tabs,\n Dialog,\n BadgeProps,\n Pagination,\n DialogActions,\n CircularProgress\n} from \"@mui/material\";\nimport { TransitionProps } from \"@mui/material/transitions\";\nimport { ContactList } from \"./contact-list\";\nimport { AccountCircle, Business } from \"@mui/icons-material\";\nimport { styled } from \"@mui/material/styles\";\nimport { useDebounce } from \"core\";\nimport { useDispatch, useSelector } from \"react-redux\";\nimport { selectors, actions } from \"modules/contacts\";\nimport { CompanyList } from \"./company-list\";\nimport { Flex } from \"hcss-core\";\nimport { IGetContactSearchDto } from \"api/GeneratedClients/ContactsClient\";\nimport { strings } from \"localization\";\nimport { ThemeProvider } from \"@hcss/theme\";\nimport { calculateNumPages, getCurrentPage } from \"core/util/pagination\";\nimport { isSandboxEnabled } from \"core/util/sandbox\";\n\nexport const SearchContactsModal = () => {\n const dispatch = useDispatch();\n const showSearchModal = useSelector(selectors.getShowSearchModal);\n const isSearching = useSelector(selectors.getIsSearching);\n const [currentTab, setCurrentTab] = useState(0);\n const [currentPage, setCurrentPage] = useState(0);\n\n const [searchValue, setSearchValue] = useState(\"\");\n const debouncedSearch = useDebounce(searchValue, 1000).toLocaleLowerCase();\n\n const results = useSelector(selectors.getSearchResultsSorted);\n\n const uniqueCompanies = useMemo(() => {\n const companyDict: Record = {};\n results.forEach(contact => {\n if (contact?.companyId && !companyDict[contact.companyId])\n companyDict[contact?.companyId] = {\n id: contact.companyId,\n companyName: contact?.companyName ?? \"\",\n companyCode: contact?.companyCode ?? \"\"\n };\n });\n return Object.values(companyDict);\n }, [results]);\n\n const numPagesContacts = calculateNumPages(results);\n const numPagesCompanies = calculateNumPages(uniqueCompanies, 6);\n const numPages = currentTab === 1 ? numPagesCompanies : numPagesContacts;\n\n const pagedResults: IGetContactSearchDto[] = useMemo(() => {\n return getCurrentPage(currentPage, results);\n }, [currentPage, results]);\n\n const pagedUniqueCompanies: CompanyData[] = useMemo(() => {\n return getCurrentPage(currentPage, uniqueCompanies, 6);\n }, [currentPage, uniqueCompanies]);\n\n useEffect(() => {\n dispatch(actions.searchContacts.request(debouncedSearch));\n }, [debouncedSearch, dispatch]);\n\n useEffect(() => {\n setCurrentPage(1);\n }, [currentTab]);\n\n const toggleModal = () =>\n dispatch(actions.setShowSearchModal(!showSearchModal));\n\n return (\n \n \n \n );\n};\n\nconst Transition = forwardRef(function Transition(\n props: TransitionProps & {\n children: ReactElement;\n },\n ref: Ref\n) {\n return ;\n});\n\nconst StyledBadge = styled(Badge)(({ theme }) => ({\n \"& .MuiBadge-badge\": {\n right: -3,\n top: 13,\n border: `2px solid ${theme.palette.background.paper}`,\n padding: \"0 4px\"\n }\n}));\n\nconst warningIconStyle: object = {\n \"--fa-primary-color\": \"#2c4369\"\n};\n\nconst Banner = styled(\"div\")({\n backgroundColor: \"#ffc400\",\n borderRadius: \"3px\",\n boxSizing: \"border-box\",\n boxShadow:\n \"0 0 1px rgba(9, 30, 66, 0.31), 0 20px 32px -8px rgba(9, 30, 66, 0.25)\",\n color: \"#2c4369\",\n padding: \"5px\",\n transition: \"background-color 200ms\",\n width: \"100%\",\n zIndex: \"600\",\n marginTop: \"10px\"\n});\n\nconst MainInfo = styled(\"div\")({\n display: \"flex\",\n alignItems: \"center\",\n height: \"32px\",\n marginLeft: \"10px\"\n});\n\nconst WarningIconContainer = styled(\"div\")({\n flex: \"0 0 auto\",\n width: \"40px\",\n height: \"24px\"\n});\n\nconst Title = styled(\"span\")({\n fontWeight: \"600\",\n fontSize: \"14px\",\n flex: \"1\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\"\n});\n\nexport interface CompanyData {\n id: string;\n companyName: string;\n companyCode: string;\n}\n","import React from \"react\";\nimport { useLocation, useHistory } from \"react-router-dom\";\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport { strings } from \"localization\";\nimport { HeaderContainer, ModuleTitle, ItemContainer } from \"./styles\";\n\nimport {\n HeaderSection,\n MenuSection,\n Item\n} from \"core/components/hcss-navigation/\";\nimport { Badge } from \"core\";\nimport { selectors, actions } from \"modules/contacts\";\nimport { useSelector, useDispatch } from \"react-redux\";\nimport { SearchContactsModal } from \"modules/contacts/components/search-contacts-modal\";\nimport { useAuthorization, usePermissions } from \"modules/account\";\n\nexport const ContactsModule = () => {\n const path = useLocation().pathname;\n const history = useHistory();\n const showSearchModal = useSelector(selectors.getShowSearchModal);\n const dispatch = useDispatch();\n const permissions = usePermissions();\n const authorization = useAuthorization();\n\n return (\n \n
\n {headerStyle => {\n return (\n \n \n {strings.common.modules.contacts}\n \n
\n );\n }}\n \n
\n {menuStyle => (\n \n }\n text={strings.layout.menu.contacts.list}\n isSelected={path === \"/contacts\"}\n onClick={() => history.push(\"/contacts\")}\n data-testid=\"navitem-company-list\"\n />\n \n {authorization.canAccessLimitedPrecon &&\n permissions.contactWrite && (\n \n }\n text={strings.layout.menu.contacts.import}\n isSelected={path === \"/contacts/import\"}\n onClick={() => history.push(\"/contacts/import\")}\n data-testid=\"navitem-contacts-import\"\n />\n )}\n {authorization.canAccessLimitedPrecon &&\n permissions.contactWrite && (\n \n }\n text={strings.layout.menu.contacts.setup}\n isSelected={path === \"/contacts/setup\"}\n onClick={() => history.push(\"/contacts/setup\")}\n data-testid=\"navitem-contacts-setup\"\n />\n )}\n \n )}\n \n
\n );\n};\n","import React, { useState, ReactNode } from \"react\";\nimport styled from \"styled-components\";\nimport { MenuItem } from \"react-bootstrap\";\nimport { Box } from \"hcss-core\";\nimport { Icon, Link } from \"hcss-components\";\nimport { GLOBAL_SIDE_NAV_WIDTH } from \"core/components/hcss-navigation/common/constants\";\n\ninterface Option {\n key: string;\n mainText: string;\n subText: string;\n}\n\ninterface Dropdown {\n options: Option[];\n optionHeader: string;\n singlePath: (param: string) => string;\n allPath: string;\n}\n\ninterface QuickSelectProps {\n children: ReactNode;\n dropDown?: Dropdown;\n}\n\nexport const QuickSelect = ({ children, dropDown }: QuickSelectProps) => {\n const [show, setShow] = useState(false);\n\n return (\n \n setShow(prev => !prev)}>\n {children}\n {dropDown && (\n \n \n \n )}\n \n\n {show && dropDown && (\n setShow(false)}>\n \n \n {dropDown.options.map(option => (\n \n \n \n {option.mainText}\n \n \n {option.subText}\n \n \n \n ))}\n \n \n View All\n \n \n \n )}\n \n );\n};\n\nconst EstimateLink = styled(Box)`\n color: ${props => props.theme.colors.neutral[0]};\n text-decoration: none;\n padding: 4px 20px;\n\n &:hover {\n color: ${props => props.theme.colors.neutral[0]};\n text-decoration: none;\n background-color: ${props => props.theme.colors.neutral[6]};\n }\n`;\n\nconst HoverContainer = styled(Box)`\n margin-right: 14px;\n background-color: ${props => props.theme.colors.neutral[6]};\n cursor: pointer;\n border-radius: 4px;\n display: flex;\n height: inherit;\n flex-direction: column;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n`;\n\nconst Cover = styled(Box)`\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n position: fixed;\n z-index: 100;\n`;\n","import React, { Fragment, useMemo } from \"react\";\nimport styled from \"styled-components\";\nimport { Link, useLocation, useRouteMatch } from \"react-router-dom\";\nimport { strings } from \"localization\";\nimport { QuickSelect } from \"core/components/sidenavigation/quick-select\";\nimport { useCurrentEstimateContext } from \"modules/estimates/context/CurrentEstimateContext\";\nimport { useSolicitationContext } from \"modules/quote-management/context/solicitation-context\";\nimport { useAuthorization } from \"modules/account\";\nimport { Badge, ConcreteColors } from \"hcss-components\";\nimport { Dropdown } from \"react-bootstrap\";\n\nimport {\n SectionHeading,\n Item,\n MenuSection,\n PlaceholderDynamicMenu\n} from \"core/components/hcss-navigation\";\nimport { useRecentEstimatesContext } from \"modules/estimates/components/recent-estimates/use-recent-estimates\";\nimport { useSelector } from \"react-redux\";\nimport { selectors } from \"modules/estimates\";\nimport { getPathForEstimate, paths } from \"modules/estimates/views/paths\";\nimport { HeaderSectionWithBackButton } from \"../../common/header-section-with-back-button\";\nimport { SolicitationMoreOptions } from \"../create-solicitation-menu-item\";\nimport { FeatureFlag } from \"core\";\nimport { useFeatureFlags } from \"modules/configurationSettings/use-feature-flags\";\n\nexport const EstimateDetailsContext = () => {\n const path = useLocation().pathname;\n const auth = useAuthorization();\n const { currentEstimate } = useCurrentEstimateContext();\n const { recentEstimateIds } = useRecentEstimatesContext();\n const { outboundNewSolicitationCount } = useSolicitationContext();\n const workingCopy = useSelector(selectors.getWorkingCopy);\n const { currentEstimateId } = useCurrentEstimateContext();\n const { isFeatureEnabled: newQuoteManagementEnabled } = useFeatureFlags(\n FeatureFlag.NewQuoteManagement\n );\n const materialReportEnabled = useFeatureFlags(\"MaterialReport\")\n .isFeatureEnabled;\n\n const estimateDetailsMatch = useRouteMatch<{ estimateId: string }>({\n path: \"/estimates/:estimateId\"\n })!;\n\n const options = useMemo(() => {\n const rawRecentEstimates = recentEstimateIds\n .map(estimateId => workingCopy[estimateId])\n .filter(estimate => estimate)\n .slice(1, 6);\n return rawRecentEstimates.map(e => {\n return {\n key: e.id ?? \"\",\n mainText: e.values.code ?? \"(no code)\",\n subText: e.values.name ?? \"(no name)\"\n };\n });\n }, [recentEstimateIds, workingCopy]);\n\n const estimateId = estimateDetailsMatch.params.estimateId;\n const estimateDetailsPath = `/estimates/${estimateId}`;\n const keyIndicatorsPath = `/estimates/${estimateId}/indicators`;\n const quotesSummaryPath = `/estimates/${estimateId}/quotes/summary`;\n const estimateDashboard = `/estimates/${estimateId}/dashboard`;\n const quotesInboxPath = `/estimates/${estimateId}/quotes/inbox`;\n const newSolicitationPath = `/estimates/${estimateId}/solicitation/details/`;\n const reportsPath = `/estimates/${estimateId}/reports`;\n const materialsPath = `/estimates/${estimateId}/materials`;\n const vendorCommunicationsPath = `/estimates/${estimateId}/communication`;\n const estimateCode = currentEstimate?.code ?? \"\";\n const estimateName = currentEstimate?.name ?? \"\";\n const drafts = `/estimates/${currentEstimateId!}/solicitation/drafts`;\n const sent = `/estimates/${currentEstimateId!}/solicitation/sent`;\n\n const dropDown = {\n options: [...options],\n singlePath: getPathForEstimate,\n allPath: paths.base,\n key: estimateId,\n optionHeader: strings.layout.menu.estimating.recentEstimates\n };\n\n if (!currentEstimate) {\n return ;\n }\n\n return (\n \n
\n \n \n {estimateCode}\n {estimateName}\n \n
\n \n
\n {menuStyle => (\n \n \n {strings.estimates.estimateAnalysis}\n \n \n \n \n {materialReportEnabled && (\n \n )}\n {newQuoteManagementEnabled && currentEstimate.isActiveEstimate && (\n \n \n {strings.estimates.quoteManagement}\n \n \n \n )}\n \n )}\n \n
\n );\n};\n\nconst HeaderContainer = styled.div`\n height: 75px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n\n .quick-select-container {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n`;\n\nconst ItemContainer = styled.div`\n font-size: 14px;\n margin-right: 14px;\n color: ${props => props.theme.colors.neutral[0]};\n`;\n\nconst ContextTitle = styled.span`\n font-size: 14px;\n font-weight: 700;\n text-transform: uppercase;\n color: #1e1e1e;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n`;\n\nconst ContextSubtitle = styled.span`\n font-size: 12px;\n display: inline-block;\n position: relative;\n width: 220px;\n color: ${props => props.theme.colors.neutral[1]};\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n`;\n\nconst InboxBadge = styled(Badge)`\n color: white;\n background-color: ${ConcreteColors.blue200};\n`;\n\ninterface ContextDropdownProps {\n isSelected: boolean;\n isHovering: boolean;\n}\nexport const ContextDropdown = styled(Dropdown)`\n margin-right: -8px;\n\n &&&& > button {\n border: 0;\n border-radius: 8px;\n box-shadow: none;\n background-color: transparent;\n color: ${p =>\n p.isSelected ? ConcreteColors.blue200 : ConcreteColors.gray600};\n\n & > span.caret {\n display: none;\n visibility: hidden;\n }\n }\n\n &&&& > ul.dropdown-menu {\n left: -155px;\n min-width: 195px;\n padding-top: 8px;\n padding-bottom: 8px;\n\n > li {\n padding-top: 5px;\n }\n }\n`;\n\nconst BackLink = styled(Link)`\n font-size: 12px;\n font-weight: 400;\n color: ${ConcreteColors.gray600};\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n`;\n","import React from \"react\";\nimport styled from \"styled-components\";\nimport { QuickSelect } from \"core/components/sidenavigation/quick-select\";\nimport { strings } from \"localization\";\nimport {\n HeaderSection,\n Item,\n MenuSection,\n SectionHeading\n} from \"core/components/hcss-navigation\";\nimport { useLocation } from \"react-router-dom\";\nimport { useGetSelectedVendor } from \"modules/contacts/hooks/use-get-selected-vendor\";\n\nexport const VendorDetailsContext = (vendorId: string) => {\n const path = useLocation().pathname;\n const currentSelectedVendor = useGetSelectedVendor();\n\n return (\n \n
\n {headerStyle => {\n return (\n \n \n \n {currentSelectedVendor && (\n <>\n {currentSelectedVendor.code}\n \n {currentSelectedVendor.name}\n \n >\n )}\n \n \n
\n );\n }}\n \n
\n {menuStyle => (\n \n \n {strings.layout.menu.contacts.title}\n \n \n \n )}\n \n
\n );\n};\n\nconst HeaderContainer = styled.div`\n height: 55px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n\n .quick-select-container {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n`;\n\nconst ItemContainer = styled.div`\n font-size: 14px;\n margin-right: 14px;\n color: ${props => props.theme.colors.neutral[0]};\n`;\n\nconst ContextTitle = styled.span`\n font-size: 14px;\n font-weight: 700;\n text-transform: uppercase;\n color: #1e1e1e;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n`;\n\nconst ContextSubtitle = styled.span`\n font-size: 12px;\n display: inline-block;\n position: relative;\n width: 220px;\n color: ${props => props.theme.colors.neutral[1]};\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n`;\n","import React from \"react\";\nimport styled from \"styled-components\";\nimport {\n HeaderSection,\n Item,\n MenuSection\n} from \"core/components/hcss-navigation\";\nimport { ConcreteColors } from \"hcss-components\";\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport { useLocation } from \"react-router-dom\";\nimport { strings } from \"localization\";\n\nexport const ProfileDetailsContext = () => {\n const path = useLocation().pathname;\n\n return (\n <>\n \n {headerStyle => {\n return (\n \n Company Profile\n
\n );\n }}\n \n \n {menuStyle => (\n \n }\n key=\"main-details\"\n text={strings.layout.menu.contacts.mainDetails}\n isSelected={path === `/settings/profile`}\n to={`/settings/profile`}\n />\n \n )}\n \n >\n );\n};\n\nconst HeaderContainer = styled.div`\n padding-bottom: 26px;\n padding-left: 12px;\n padding-top: 8px;\n font-size: 16px;\n font-weight: 700;\n color: ${ConcreteColors.gray600};\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n`;\n\nconst ItemContainer = styled.div`\n font-size: 14px;\n margin-right: 14px;\n color: ${ConcreteColors.gray600};\n`;\n","import React from \"react\";\nimport styled from \"styled-components\";\nimport {\n HeaderSection,\n Item,\n MenuSection\n} from \"core/components/hcss-navigation\";\nimport { ConcreteColors, Icon } from \"hcss-components\";\nimport { Link, useLocation } from \"react-router-dom\";\nimport { useAccount } from \"modules/account\";\nimport * as schemas from \"modules/schemas\";\nimport { useSelector } from \"react-redux\";\nimport { HeaderSectionWithBackButton } from \"../../common/header-section-with-back-button\";\nimport { DisableInSandbox } from \"core/util/sandbox\";\nimport { strings } from \"localization\";\n\nexport const Imports = () => {\n const path = useLocation().pathname;\n const account = useAccount();\n const isBidResultsAdminOnly = useSelector(\n schemas.selectors.getAllProjectSchemaFields\n )?.find(x => x.id === \"bidResults\")?.adminOnly;\n\n const canImportBidResults = () => {\n if (\n isBidResultsAdminOnly &&\n account.permissions &&\n !account.permissions.admin\n ) {\n return false;\n }\n\n return true;\n };\n\n return (\n <>\n \n {strings.layout.menu.projects.import.label}
\n \n \n {menuStyle => (\n \n \n {canImportBidResults() && (\n \n \n \n )}\n \n )}\n \n >\n );\n};\n\nconst HeaderContainer = styled.div`\n padding-bottom: 26px;\n padding-left: 12px;\n padding-top: 8px;\n font-size: 16px;\n font-weight: 700;\n color: ${ConcreteColors.gray600};\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n`;\n\nconst BackToProjectsLink = styled(Link)`\n font-size: 12px;\n font-weight: 400;\n color: ${ConcreteColors.gray600};\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n`;\n\nconst ItemContainer = styled.div`\n font-size: 14px;\n margin-right: 14px;\n color: ${ConcreteColors.gray600};\n`;\n","import { Badge } from \"core\";\nimport { Item, MenuSection } from \"core/components/hcss-navigation\";\nimport React from \"react\";\nimport { useLocation } from \"react-router-dom\";\nimport styled from \"styled-components\";\nimport { paths } from \"../../../../estimates/views/paths\";\nimport { HeaderSectionWithBackButton } from \"../../common/header-section-with-back-button\";\nimport { strings } from \"localization\";\n\nexport const CustomizeReportingContext = () => {\n const path = useLocation().pathname;\n\n return (\n \n
\n {strings.layout.menu.estimating.customize}
\n \n
\n {menuStyle => (\n \n - \n {strings.featureStatus.beta}\n \n }\n />\n
\n )}\n \n
\n );\n};\n\nconst ItemContainer = styled.div`\n font-size: 14px;\n margin-right: 14px;\n color: ${props => props.theme.colors.neutral[0]};\n`;\n","import { useRouteMatch, useLocation } from \"react-router-dom\";\nimport { EstimateDetailsContext } from \"./estimate-details\";\nimport { VendorDetailsContext } from \"./vendor-details\";\nimport { useAuthorization, usePermissions } from \"modules/account\";\nimport { ProfileDetailsContext } from \"./profile-details\";\nimport { Imports } from \"./imports\";\nimport { CustomizeSetupContext } from \"./customize-setup\";\nimport { CustomizeReportingContext } from \"./customize-reporting\";\nimport { paths } from \"modules/estimates/views/paths\";\n\nexport const useContextNavigation = () => {\n const path = useLocation().pathname;\n const auth = useAuthorization();\n const permissions = usePermissions();\n\n const enableCustomizeReporting =\n auth.canAccessFullPrecon && permissions.admin;\n\n const estimateDetailsMatch = useRouteMatch({\n path: \"/estimates/:estimateId\"\n });\n const estimateCodebooksMatch = useRouteMatch({\n path: \"/estimates/codebooks\"\n });\n\n const customizeReportingMatch = useRouteMatch({\n path: \"/estimates/customize\"\n });\n\n const vendorDetailsMatch = useRouteMatch({\n path: \"/contacts/:vendorId\"\n });\n\n const profileDetailsMatch = useRouteMatch({\n path: \"/settings/profile\"\n });\n\n const importProjectsMatch = useRouteMatch({\n path: \"/projects/import-projects\"\n });\n\n const importBidResultsMatch = useRouteMatch({\n path: \"/projects/import-bid-results\"\n });\n\n const customizeSetupMatch = useRouteMatch({\n path: \"/projects/setup\"\n });\n\n if (customizeReportingMatch && enableCustomizeReporting) {\n return { context: CustomizeReportingContext };\n }\n\n if (\n estimateDetailsMatch &&\n path !== paths.base &&\n path !== paths.edit &&\n !estimateCodebooksMatch\n ) {\n return { context: EstimateDetailsContext };\n }\n\n if (\n vendorDetailsMatch &&\n path !== \"/contacts/import\" &&\n path !== \"/contacts/setup\"\n ) {\n return {\n context: () => VendorDetailsContext(vendorDetailsMatch.params?.vendorId)\n };\n }\n\n if (profileDetailsMatch) {\n return { context: ProfileDetailsContext };\n }\n\n if (importProjectsMatch || importBidResultsMatch) {\n return { context: Imports };\n }\n\n if (customizeSetupMatch) {\n return { context: CustomizeSetupContext };\n }\n\n return { context: undefined };\n};\n\ninterface VendorDetailsMatchParams {\n vendorId: string;\n}\n","import React, { Fragment } from \"react\";\nimport styled from \"styled-components\";\nimport { OAuthConfigurationPanel } from \"modules/settings/components/OAuthConfigurationPanel\";\nimport { ProjectsCalendarSubscribeModal } from \"modules/projects/components/ProjectsCalendarSubscribeModal\";\nimport { HcssConnectConfigurationPanel } from \"../../settings/components/HcssConnectConfigurationPanel\";\nimport { BusinessUnitPanel } from \"../../settings/components/BusinessUnitPanel\";\nimport { HcssNavigation } from \"core/components/hcss-navigation\";\nimport { TopNavigation } from \"./navigation/top-navigation\";\nimport { SideNavigation } from \"./navigation/side-navigation\";\nimport { useModuleNavigation } from \"./navigation/module\";\nimport { useContextNavigation } from \"./navigation/context\";\nimport { useHcssUser } from \"modules/account\";\nimport { UserType } from \"core\";\n\ninterface SiteLayoutProps {\n children?: React.ReactNode;\n}\nexport const SiteLayout = ({ children }: SiteLayoutProps) => {\n const user = useHcssUser();\n const { module: ModuleNavigation } = useModuleNavigation();\n const { context: ContextNavigation } = useContextNavigation();\n\n const hasModule = ModuleNavigation ? true : false;\n const hasContext = ContextNavigation ? true : false;\n const hasDynamicNavigation = hasModule || hasContext;\n const isGuestUser = user.type === UserType.Guest;\n const globalSideNavigation = isGuestUser ? undefined : SideNavigation;\n const useDynamicNavigation = isGuestUser ? false : hasDynamicNavigation;\n\n return (\n \n \n \n {children}\n \n \n \n \n \n \n \n );\n};\n\nconst NoModule = () => null;\n\nconst FullHeightContainer = styled.div`\n height: 100vh;\n margin-top: 0;\n`;\n\nconst ContentWrapper = styled.div`\n width: 100%;\n height: 100%;\n`;\n\nexport default SiteLayout;\n","import { useRouteMatch } from \"react-router-dom\";\nimport { ProjectTrackingModule } from \"./project-tracking\";\nimport { EstimatingModule } from \"./estimating\";\nimport { ContactsModule } from \"./contacts\";\nimport { useAuthorization } from \"modules/account\";\n\nexport const useModuleNavigation = () => {\n const auth = useAuthorization();\n const projectsModuleMatch = useRouteMatch({ path: \"/projects\" });\n const estimatesModuleMatch = useRouteMatch({ path: \"/estimates\" });\n const contactsModuleMatch = useRouteMatch({ path: \"/contacts\" });\n\n if (projectsModuleMatch) {\n return { module: ProjectTrackingModule };\n }\n\n if (estimatesModuleMatch) {\n return { module: EstimatingModule };\n }\n\n if (contactsModuleMatch) {\n return { module: ContactsModule };\n }\n\n return { module: undefined };\n};\n","import * as React from \"react\";\nimport { ResponsiveBar } from \"@nivo/bar\";\n\nexport const CheckListChart: React.FunctionComponent<{ data: any }> = ({\n data /* see data tab */\n}) => (\n \n ((d.value as number) / (data[0].checked + data[0].notChecked)).toString()\n }\n labelFormat={\".0%\"}\n animate={true}\n motionStiffness={90}\n motionDamping={15}\n isInteractive={false}\n />\n);\n","import {\n ChangeSet,\n DataTypeProvider,\n EditingState\n} from \"@devexpress/dx-react-grid\";\nimport {\n TableEditColumn,\n TableEditRow,\n TableHeaderRow\n} from \"@devexpress/dx-react-grid-bootstrap3\";\nimport { notify } from \"hcss-components\";\nimport {\n BooleanFormatProvider,\n CommandComponent,\n DataColumnType,\n Grid,\n IntegratedSorting,\n SortingState,\n TableContainer,\n TypedDataColumn,\n useTableContext\n} from \"hcss-tables\";\nimport produce from \"immer\";\nimport { cloneDeep } from \"lodash-es\";\nimport * as React from \"react\";\nimport { NoDataPlaceholder } from \"../../../../core/components/containers/no-data-placeholder\";\n\nimport { Flex } from \"hcss-core\";\nimport {\n selectors as accountSelectors,\n StateSlice as AccountStateSlice\n} from \"modules/account\";\nimport {\n CustomEditCellComponent,\n CustomEditCellHeaderComponent\n} from \"modules/tables/cells/custom-cells-components\";\nimport { connect } from \"react-redux\";\nimport * as configuratingSettings from \"../../../configurationSettings\";\nimport { selectors as configSelectors } from \"../../../configurationSettings/state\";\nimport { TableProps } from \"../SchemaFormArrayField\";\nimport { CheckListChart } from \"./CheckListChart\";\nimport LegacyVirtualTable from \"core/components/bundle-fixes/LegacyVirtualTable\";\n\nexport interface TableState {\n editingRowIds: any[];\n addedRows: any[];\n rowChanges: any;\n}\n\nexport interface ChecklistTableProps extends TableProps {\n maxDescriptionLength?: number;\n}\n\nexport class CheckList extends React.PureComponent<\n ChecklistTableProps,\n TableState\n> {\n state = {\n editingRowIds: [] as any[],\n addedRows: [] as any[],\n rowChanges: {}\n };\n\n getColumns() {\n const { field } = this.props;\n const adminFailure =\n !this.props.permissions?.admin && this.props.field?.adminOnly;\n const checkCol = {\n id: \"checked\",\n name: \"checked\",\n title: \"Checked\",\n type: DataColumnType.Boolean,\n config: field.config,\n readOnly: field.readOnly || adminFailure,\n required: field.required\n };\n const desCol = {\n id: \"description\",\n name: \"description\",\n title: \"Description\",\n type: DataColumnType.LongText,\n config: field.config,\n readOnly: field.readOnly || adminFailure,\n required: field.required\n };\n // TODO: add customized TypeDataColumn\n const columns: TypedDataColumn[] = [\n checkCol as TypedDataColumn,\n desCol as TypedDataColumn\n ];\n return columns;\n }\n\n commitChanges = ({ added, changed, deleted }: ChangeSet) => {\n if (added && this.props.onChange) {\n const newObject = added[0];\n\n if (newObject.checked === undefined) {\n newObject.checked = false;\n }\n\n const { rows = [] } = this.props;\n const newRows = produce(rows, draft => {\n if (!draft) {\n draft = [];\n }\n draft.push(newObject);\n });\n this.props.onChange(newRows);\n }\n if (changed && this.props.onChange) {\n const changedIndex = parseInt(Object.keys(changed)[0], 10);\n const mergedObject = {\n ...this.props.rows[changedIndex],\n ...changed[changedIndex]\n };\n\n const newRows = produce(this.props.rows, draft => {\n draft[changedIndex] = mergedObject;\n });\n\n this.props.onChange(newRows);\n }\n if (deleted && this.props.onDelete) {\n this.props.onDelete(deleted[0]);\n }\n };\n\n changeAddedRows = (addedRows: any[]) => {\n this.setState({ addedRows });\n };\n\n changeEditingRowIds = (editingRowIds: any[]) => {\n this.setState({ editingRowIds });\n };\n\n changeRowChanges = (rowChanges: any) => {\n this.setState({ rowChanges });\n };\n\n validateEditedRow = (tableRow: any) => {\n const tempTableRow = cloneDeep(tableRow);\n const editedRowId = tableRow.rowId;\n const rowChanges: any = this.state.rowChanges;\n if (rowChanges[editedRowId]) {\n // when edited an existing row\n tempTableRow.row.checked =\n rowChanges[editedRowId].checked !== undefined\n ? rowChanges[editedRowId].checked\n : tempTableRow.row.checked;\n tempTableRow.row.description =\n rowChanges[editedRowId].description !== undefined\n ? rowChanges[editedRowId].description\n : tempTableRow.row.description;\n }\n\n if (\n tempTableRow.row.description === undefined ||\n tempTableRow.row.description === \"\"\n ) {\n notify(\"danger\", `Error Saving Row`, \"Description cannot be blank\");\n return false;\n }\n\n if (\n this.props.maxDescriptionLength &&\n tempTableRow.row.description.length > this.props.maxDescriptionLength\n ) {\n notify(\n \"danger\",\n `Error Saving Row`,\n `Description exceeded ${this.props.maxDescriptionLength} character length limit`\n );\n return false;\n }\n\n return true;\n };\n\n render() {\n const { rows = [] } = this.props;\n const adminFailure =\n !this.props.permissions?.admin && this.props.field?.adminOnly;\n const isAddEditDisplay = this.props.permissions?.write;\n const columns = this.getColumns();\n const { editingRowIds, rowChanges, addedRows } = this.state;\n const isDisabled = !isAddEditDisplay || adminFailure || this.props.isLocked;\n return (\n \n
\n \n {rows.length <= 0 && this.state.addedRows.length <= 0 && (\n \n )}\n {(rows.length > 0 || this.state.addedRows.length > 0) && (\n \n \n ({\n columnName: column.name,\n editingEnabled: !column.readOnly\n }))}\n />\n \n \n \n \n \n {(rows.length > 0 || this.state.addedRows.length > 0) && (\n \n TableCommandComponent({\n tableEditColumnProps: props,\n validationFunction: this.validateEditedRow,\n isDisabled\n })\n }\n cellComponent={CustomEditCellComponent}\n headerCellComponent={CustomEditCellHeaderComponent}\n />\n )}\n \n )}\n \n \n
\n );\n }\n\n EmptyCell = () => {\n const adminFailure =\n !this.props.permissions?.admin && this.props.field?.adminOnly;\n const isAddEditDisplay = this.props.permissions?.write;\n return this.state.addedRows.length === 0 ? (\n this.changeAddedRows([{}])}\n isAddEditDisplay={\n !this.props.isLocked && isAddEditDisplay && !adminFailure\n }\n />\n ) : null;\n };\n}\n\ninterface TableCommandComponentProps {\n tableEditColumnProps: TableEditColumn.CommandProps;\n validationFunction: (tableRow: any) => boolean;\n isDisabled: boolean | undefined;\n}\nconst TableCommandComponent = ({\n tableEditColumnProps,\n validationFunction,\n isDisabled\n}: TableCommandComponentProps) => {\n return (\n \n );\n};\n\nconst CheckListFormatter = ({\n row,\n value,\n column\n}: DataTypeProvider.ValueFormatterProps) => {\n if (!row || !value || value.length <= 0) {\n return ;\n }\n const data = checklistReducer(value);\n return (\n \n \n
\n );\n};\n\nexport const CheckListProvider = () => {\n const { columns } = useTableContext();\n return React.useMemo(\n () => (\n column.type === DataColumnType.CheckList)\n .map(column => column.name)}\n />\n ),\n [columns]\n );\n};\n\nexport const checklistReducer = (\n value: any[]\n): { checked: number; notChecked: number } => {\n const defaultValue = { checked: 0, notChecked: 0 };\n if (!value) {\n return defaultValue;\n }\n return value.reduce(\n (acc: any, curr: any) => ({\n checked: acc.checked + (curr.checked ? 1 : 0),\n notChecked: acc.notChecked + (curr.checked ? 0 : 1)\n }),\n defaultValue\n );\n};\n\nconst mapStateToProps = (\n state: configuratingSettings.StateSlice & AccountStateSlice,\n props: TableProps\n) => ({\n permissions: accountSelectors.getPermissions(state),\n configuratingSettings: configSelectors.getConfigurationSettings(state),\n ...props\n});\n\nexport default connect(mapStateToProps, {})(CheckList);\n","import React from \"react\";\nimport { ConcreteColors } from \"hcss-components\";\nimport styled from \"styled-components\";\n\nexport interface RoadblockProps {\n as?: React.ElementType;\n}\n\nexport const Roadblock = (props: RoadblockProps) => (\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n);\n\nconst StyledRoadblockImage = styled.svg`\n & #bg,\n & #l_sh {\n fill: ${ConcreteColors.blue200};\n opacity: 0.2;\n }\n & #l_m,\n & #sh-1,\n & #sh-2 {\n fill: ${ConcreteColors.blue200};\n opacity: 0.3;\n }\n & #bg-highlight {\n fill: #ffffff;\n opacity: 0.5;\n }\n & #c-1,\n & #c-2 {\n fill: #ffffff;\n }\n & #stripes-1,\n & #stripes-2 {\n fill: #ff7861;\n }\n`;\n","import React from \"react\";\nimport styled from \"styled-components\";\nimport { ExpandableContentStatus } from \"../../models/enums\";\n\ninterface HeaderProps {\n title: string;\n subtitle?: string;\n onPanelToggleRequested: (event: any) => void;\n alignRight?: boolean;\n activeWrapper?: React.ComponentType;\n panelStatus?: ExpandableContentStatus;\n}\n\nexport class SideNavigationHeader extends React.PureComponent {\n renderCollapsedPanelTitle = (): JSX.Element => {\n return (\n \n {this.props.title}\n {this.props.subtitle && (\n <>\n • \n {this.props.subtitle}\n >\n )}\n \n );\n };\n\n isExpanded = () => {\n if (this.props.panelStatus) {\n return (\n this.props.panelStatus === ExpandableContentStatus.expanding ||\n this.props.panelStatus === ExpandableContentStatus.expanded\n );\n }\n return true;\n };\n\n render() {\n const TitleWrapper = this.props.activeWrapper || DefaultTitleWrapper;\n\n return (\n \n {this.isExpanded() && (\n \n {this.props.title}\n {this.props.children}\n \n )}\n\n {this.renderCollapsedPanelTitle()}\n \n \n
\n \n );\n }\n}\n\nconst DefaultTitleWrapper = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n`;\n\nexport const BaseHeader = styled.header`\n width: 100%;\n height: auto;\n overflow: visible;\n padding-left: 14px;\n padding-right: 14px\n min-height: 43px;\n\n .panel-title,\n .panel-desc,\n .panel-collapsed-title {\n transition: 0.2s;\n }\n\n & .panel-title {\n position: relative;\n }\n\n & .panel-collapsed-title {\n position: absolute;\n }\n\n & .panel-title,\n & .panel-collapsed-title {\n float: left;\n /* display: inline-block; */\n font-size: 12px;\n font-weight: 700;\n text-transform: uppercase;\n color: #1e1e1e;\n white-space: nowrap;\n display: flex;\n justify-content: flex-end;\n align-items: center;\n }\n\n & .panel-display-toggle-button {\n display: inline-block;\n position: absolute;\n bottom: 10px;\n right: 10px;\n cursor: pointer;\n z-index: 5;\n color: #1e1e1e;\n transition: 0.7s;\n\n & > i {\n font-size: 14px;\n transition: 0.2s;\n height: 14px;\n }\n }\n\n & .panel-display-toggle-button:hover,\n & .panel-display-toggle-button:active {\n color: #404040;\n & > i {\n color: #0370f5;\n }\n }\n`;\n\nconst StyledHeader = styled(BaseHeader)`\n margin-bottom: 6px;\n\n & .panel-title {\n font-size: 14px;\n position: relative;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n & .panel-desc {\n font-size: 12px;\n display: inline-block;\n position: relative;\n width: 200px;\n color: ${props => props.theme.colors.neutral[1]};\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n & .panel-collapsed-title {\n width: 248px;\n left: -107px;\n top: 126px;\n text-align: right;\n transform: rotate(-90deg);\n overflow: hidden;\n text-overflow: ellipsis;\n text-transform: none;\n }\n`;\n","import { useState, useEffect, useCallback } from \"react\";\nimport { ExpandableContentStatus } from \"../../models/enums\";\n\nexport interface UseSideNavigationOptions {\n onExpanded?: () => void;\n onExpanding?: () => void;\n onCollapsed?: () => void;\n onCollapsing?: () => void;\n}\n\nexport function useSideNavigation(\n startExpanded: boolean,\n options: UseSideNavigationOptions = {}\n) {\n const [panelStatus, setPanelStatus] = useState(\n startExpanded\n ? ExpandableContentStatus.expanded\n : ExpandableContentStatus.collapsed\n );\n\n useEffect(() => {\n if (panelStatus === ExpandableContentStatus.collapsing) {\n setTimeout(() => {\n setPanelStatus(ExpandableContentStatus.collapsed);\n if (options.onCollapsed) {\n options.onCollapsed();\n }\n }, 425);\n } else if (panelStatus === ExpandableContentStatus.expanding) {\n setTimeout(() => {\n setPanelStatus(ExpandableContentStatus.expanded);\n if (options.onExpanded) {\n options.onExpanded();\n }\n }, 425);\n }\n }, [panelStatus, options]);\n\n const toggle = useCallback(() => {\n let status: ExpandableContentStatus;\n\n if (panelStatus === ExpandableContentStatus.expanded) {\n status = ExpandableContentStatus.collapsing;\n if (options.onCollapsing) {\n options.onCollapsing();\n }\n } else {\n status = ExpandableContentStatus.expanding;\n if (options.onExpanding) {\n options.onExpanding();\n }\n }\n\n setPanelStatus(status);\n }, [panelStatus, options]);\n\n return { status: panelStatus, toggle };\n}\n","import React from \"react\";\nimport styled from \"styled-components\";\nimport { SideNavigationHeader } from \"./Header\";\nimport { ExpandableContentStatus } from \"../../models/enums\";\n\nexport * from \"./use-side-navigation\";\n\ninterface Props {\n panelStatus: ExpandableContentStatus;\n panelActive: boolean;\n className?: string;\n onClick?: (event: any) => void;\n alignRight?: boolean;\n fullWidth?: boolean;\n style?: React.CSSProperties;\n}\nexport class SideNavigation extends React.PureComponent {\n static Header = SideNavigationHeader;\n // static Toolbar = SideNavigationToolbar;\n // static ToolbarMenu = SideNavigationToolbarMenu;\n // static Content = SideNavigationContent;\n\n render() {\n return (\n \n {this.props.children}\n \n );\n }\n}\n\nconst BaseContainer = styled.div<{ alignRight?: boolean }>`\n position: relative;\n box-sizing: border-box;\n height: 100%;\n padding: 12px 14px 16px 0px;\n background: #ffffff;\n border-right: 2px solid #ececec;\n transition: 0.5s;\n\n &[data-panel-status=\"expanded\"] {\n overflow: visible;\n &:after {\n z-index: -2;\n }\n }\n &[data-panel-status=\"expanding\"] {\n overflow: hidden;\n }\n &[data-panel-status=\"collapsed\"] {\n overflow: hidden;\n }\n &[data-panel-status=\"collapsing\"] {\n overflow: hidden;\n }\n\n &[data-panel-status=\"expanded\"],\n &[data-panel-status=\"expanding\"],\n &[data-panel-status=\"collapsing\"] {\n & > header {\n .panel-collapsed-title {\n color: transparent;\n z-index: -2;\n }\n }\n\n &:after {\n opacity: 0;\n }\n }\n\n &[data-panel-status=\"expanding\"],\n &[data-panel-status=\"expanded\"] {\n .panel-display-toggle-button {\n transform: rotate(180deg);\n bottom: 9px;\n }\n }\n\n &[data-panel-status=\"collapsing\"],\n &[data-panel-status=\"collapsed\"] {\n padding: 12px 0 16px 0;\n\n & > header {\n .panel-title {\n color: transparent;\n }\n .panel-desc {\n color: transparent;\n }\n .panel-collapsed-title {\n cursor: pointer;\n transition: 0.4s;\n z-index: 5;\n }\n .panel-display-toggle-button {\n }\n }\n\n &:after {\n opacity: 1;\n }\n }\n\n &:after {\n content: \"\";\n position: absolute;\n background: #ffffff;\n top: 0;\n ${props => (props.alignRight ? `right` : `left`)}: 0;\n left: 0;\n width: 100%;\n height: 100%;\n transition: 0.5s;\n z-index: 4;\n }\n`;\n\nconst Container = styled(BaseContainer)<{ fullWidth?: boolean }>`\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n ${props => props.fullWidth && \"width: 100%;\"}\n\n &[data-panel-active=\"false\"] {\n width: 0px;\n margin-left: -40px;\n opacity: 0.5;\n }\n\n &[data-panel-status=\"collapsed\"],\n &[data-panel-status=\"collapsing\"] {\n max-width: 34px;\n }\n &[data-panel-status=\"expanded\"],\n &[data-panel-status=\"expanding\"] {\n ${props => !props.fullWidth && \"max-width: 272px;\"};\n }\n\n /* Compatibility fixes for IE 11 */\n body.ie-compatibility-mode &[data-panel-status=\"expanded\"],\n body.ie-compatibility-mode &[data-panel-status=\"expanding\"] {\n ${props => !props.fullWidth && \"max-width: 272px;\"};\n }\n\n body.ie-compatibility-mode &[data-panel-status=\"collapsed\"],\n body.ie-compatibility-mode &[data-panel-status=\"collapsing\"] {\n max-width: 34px;\n }\n`;\n\nexport const SideNavigationWrapper = styled.div`\n display: flex;\n position: relative;\n flex-direction: row;\n flex-wrap: nowrap;\n width: auto;\n background: #ffffff;\n height: calc(100vh - 45px);\n\n @media print {\n display: none;\n }\n\n /* Compatibility fixes for IE 11 */\n body.ie-compatibility-mode & {\n flex-shrink: 0;\n }\n`;\n","import { Button, Icon } from \"hcss-components\";\nimport * as React from \"react\";\nimport { strings } from \"localization\";\n\nexport default function BackButton({ buttonLabel, ...props }: any) {\n return (\n \n );\n}\n","import {\n Table,\n TableCell,\n TableHead,\n TableRow,\n Tooltip,\n Typography\n} from \"@mui/material\";\nimport { ConcreteColors } from \"hcss-components\";\nimport React, { useEffect, useRef, useState } from \"react\";\nimport styled from \"styled-components\";\n\nexport const StyledTableCell = styled(TableCell)`\n padding: 0 16px;\n color: #555;\n font-family: Inter;\n font-size: 16px;\n font-style: normal;\n font-weight: 400;\n line-height: normal;\n border-bottom: 1px solid rgba(217, 217, 217, 0.6);\n box-sizing: border-box;\n\n &.sticky-left {\n position: sticky;\n left: 0;\n border-right: 1px solid rgba(217, 217, 217, 0.6);\n zindex: 9999 !important;\n }\n\n &.sticky-right {\n position: sticky;\n right: 0;\n border-left: 1px solid rgba(217, 217, 217, 0.6);\n zindex: 9999 !important;\n }\n`;\nexport const TableHeaderLabel = ({\n children,\n fontSize\n}: {\n children: string;\n fontSize?: number;\n}) => (\n \n {children}\n \n);\n\nexport const StyledTableRow = styled(TableRow)`\n background: #fff;\n height: 48px;\n border: none;\n`;\n\nexport const StyledTableHead = styled(TableHead)`\n height: 48px;\n`;\n\nexport const StyledFixedTable = styled(Table)`\n table-layout: fixed;\n overflow: auto;\n height: 1px;\n`;\n\nexport const CellContainer = ({\n children,\n tootlip,\n style\n}: {\n children: React.ReactNode | React.ReactNode[];\n tootlip?: React.ReactNode | React.ReactNode[];\n style?: React.CSSProperties;\n}) => {\n const [isOverflow, setIsOverflow] = useState(false);\n const textElementRef = useRef(null);\n\n useEffect(() => {\n if (!textElementRef.current) {\n return;\n }\n\n setIsOverflow(\n textElementRef.current.scrollWidth > textElementRef.current.clientWidth\n );\n }, []);\n\n return (\n \n \n {children}\n \n \n );\n};\n","import React, { useEffect, useRef, useState } from \"react\";\nimport { SchemaFormFieldInputProps } from \"./common\";\nimport { preconIdToString, parsePreConId, PreConId } from \"modules/projects\";\nimport { useAuthorization, usePermissions } from \"modules/account\";\n\nexport interface PreConIdInputBaseProps\n extends Omit<\n React.DetailedHTMLProps<\n React.InputHTMLAttributes,\n HTMLInputElement\n >,\n \"value\" | \"onChange\" | \"disabled\"\n > {\n value: PreConId | null;\n onChange: (value: PreConId | null) => void;\n disabled?: boolean;\n}\n\nexport const PreConIdInputBase = ({\n value,\n onChange,\n disabled,\n ...other\n}: PreConIdInputBaseProps) => {\n const [cursor, setCursor] = useState(null);\n const ref = useRef(null);\n\n //const handleChange = (stringValue: string, uppercase: boolean) => {\n // const normalizedValue = uppercase ? stringValue.toUpperCase() : stringValue;\n // const parsedId = parsePreConId(normalizedValue);\n // onChange(parsedId);\n //};\n\n useEffect(() => {\n const input = ref.current;\n if (input) input.setSelectionRange(cursor, cursor);\n }, [ref, cursor, value]);\n\n const handleChange = (e: any) => {\n setCursor(e.target.selectionStart);\n const parsedId = parsePreConId(e.target.value);\n onChange(parsedId);\n };\n\n return (\n \n );\n};\n\nexport const PreConIdInput = (props: SchemaFormFieldInputProps) => {\n const {\n form: { setFieldValue },\n field,\n schemaField: { adminOnly },\n isLocked\n } = props;\n\n const permissions = usePermissions();\n const authorization = useAuthorization();\n const adminFailure = !permissions.admin && adminOnly;\n const fieldIsEditable =\n !isLocked &&\n permissions.write &&\n authorization.canAccessLimitedPrecon &&\n !adminFailure;\n\n return (\n \n setFieldValue(\"preconId\", preconId)\n }\n />\n );\n};\n","import {\n collection,\n deleteDoc,\n doc,\n getDoc,\n getDocs,\n getFirestore,\n query,\n setDoc,\n where\n} from \"firebase/firestore\";\nimport axios from \"axios\";\nimport config from \"../../../config\";\nimport { firebaseApp } from \"../../../firebase-app\";\nimport { isUndefined, omitBy, omit } from \"lodash-es\";\nimport { MasterSolicitation, MasterSolicitationEntity } from \"core\";\n\nconst db = getFirestore(firebaseApp);\nconst collectionId = \"masterSolicitations\";\n\nexport const MasterSolicitationRepo = {\n generateId: () => {\n return doc(collection(db, collectionId)).id;\n },\n\n getById: async (id: string) => {\n const ref = doc(collection(db, collectionId), id);\n\n try {\n const snap = await getDoc(ref);\n const data = snap.data() as MasterSolicitation;\n const entity: MasterSolicitationEntity = { ...data, id: ref.id };\n return entity;\n } catch (error) {\n console.error(error);\n return undefined;\n }\n },\n\n getAll: async (\n companyId: string,\n estimateId?: string,\n projectId?: string\n ) => {\n const solicitations: MasterSolicitationEntity[] = [];\n\n const whereClauses = [];\n whereClauses.push(where(\"companyId\", \"==\", companyId));\n\n if (estimateId) {\n whereClauses.push(where(\"estimateId\", \"==\", estimateId));\n }\n\n if (projectId) {\n whereClauses.push(where(\"projectId\", \"==\", projectId));\n }\n\n try {\n const q = query(collection(db, collectionId), ...whereClauses);\n const snap = await getDocs(q);\n for (const doc of snap.docs) {\n const data = doc.data() as MasterSolicitation;\n solicitations.push({ ...data, id: doc.id });\n }\n } catch (error) {\n console.error(error);\n }\n\n return solicitations;\n },\n\n subscribe: (companyId: string, estimateId?: string, projectId?: string) => {\n const whereClauses = [];\n whereClauses.push(where(\"companyId\", \"==\", companyId));\n\n if (estimateId) {\n whereClauses.push(where(\"estimateId\", \"==\", estimateId));\n }\n\n if (projectId) {\n whereClauses.push(where(\"projectId\", \"==\", projectId));\n }\n\n const q = query(collection(db, collectionId), ...whereClauses);\n return q;\n },\n\n save: async (solicitation: MasterSolicitationEntity) => {\n const id = solicitation.id;\n const ref = doc(collection(db, collectionId), id);\n\n try {\n let doc = omitBy(solicitation, isUndefined);\n doc = omit(doc, [\"id\"]);\n await setDoc(ref, doc);\n return true;\n } catch (error) {\n console.error(error);\n return false;\n }\n },\n\n delete: async (solicitation: MasterSolicitationEntity, token: string) => {\n const deleteFile = async (fileId: string) => {\n await axios.delete(config.endpoints.SMARTDRIVE + \"Files\", {\n headers: {\n Authorization: \"Bearer \" + token\n },\n params: {\n appId: config.keys.SMARTDRIVEAPPID,\n Id: fileId\n }\n });\n };\n\n const ref = doc(collection(db, collectionId), solicitation.id);\n\n try {\n const promises = solicitation.attachments.map(a => deleteFile(a.id));\n promises.push(deleteDoc(ref));\n await Promise.all(promises);\n return true;\n } catch (error) {\n console.error(error);\n return false;\n }\n }\n};\n","import React, { useState, useEffect } from \"react\";\nimport numeral from \"numeral\";\nimport MaskedInput from \"react-text-mask\";\nimport createNumberMask from \"text-mask-addons/dist/createNumberMask\";\nimport { formatCurrency, formatNumber } from \"core\";\n\ninterface NumericInputProps {\n decimal: number;\n value: number;\n isCurrency: boolean;\n onChange: (value: number) => any;\n isReadOnly?: boolean;\n className?: string;\n}\n\nexport const NumericInput = ({\n decimal,\n isCurrency,\n value,\n onChange,\n isReadOnly,\n className\n}: NumericInputProps) => {\n const format = isCurrency ? formatCurrency : formatNumber;\n const formattedValue = format(value, decimal);\n\n const [fieldValue, setFieldValue] = useState(formattedValue);\n\n useEffect(() => {\n setFieldValue(formattedValue);\n }, [formattedValue]);\n\n const handleChange = (e: React.ChangeEvent) => {\n e.persist();\n const value: string = e.target.value;\n const numberValue = numeral(value).value();\n if (!isNaN(numberValue)) {\n setFieldValue(value);\n }\n };\n\n const handleBlur = () => {\n const updateVal = fieldValue === \"\" ? 0 : numeral(fieldValue).value();\n setFieldValue(format(updateVal, decimal));\n onChange(updateVal);\n };\n\n const handleFocus = () => {\n if (numeral(fieldValue).value() === 0) {\n setFieldValue(\"\");\n }\n };\n\n const numberMask = createNumberMask({\n allowDecimal: true,\n allowNegative: true,\n prefix: isCurrency ? \"$\" : \"\",\n decimalLimit: isCurrency ? 4 : 2,\n integerLimit: 10,\n allowLeadingZeroes: true\n });\n\n return (\n \n );\n};\n\nexport default NumericInput;\n","import { isEqual } from \"lodash-es\";\nimport { useDispatch, useSelector } from \"react-redux\";\nimport { useParams } from \"react-router-dom\";\nimport { PreConId, ProjectsApi, actions, selectors } from \"modules/projects\";\nimport { selectors as schemaSelectors } from \"modules/schemas\";\nimport {\n selectors as accountSelectors,\n useSelectedBusinessUnitId\n} from \"modules/account\";\nimport { FieldMetadata } from \"api\";\nimport { useCallback, useMemo, useState } from \"react\";\n\nexport function useGetCurrentProject() {\n const { id: projectId } = useParams<{ id: string }>();\n const projectLookup = useSelector(selectors.getProjectHash);\n\n return projectLookup[projectId];\n}\n\nexport const useProjectFieldsMetadata = (projectId: string) => {\n const dispatch = useDispatch();\n const projectsLookup = useSelector(selectors.getProjectHash);\n const projectsLookupPristine = useSelector(selectors.getProjectHashPristine);\n const projectSchemaLookup = useSelector(schemaSelectors.getProjectSchema)\n ?.fields;\n\n const projectFieldsMetadata = useMemo(\n () => projectsLookup[projectId]?.fieldsMetadata ?? {},\n [projectId, projectsLookup]\n );\n const projectFieldsMetadataPristine = useMemo(\n () => projectsLookupPristine[projectId]?.fieldsMetadata ?? {},\n [projectId, projectsLookupPristine]\n );\n\n const hasChanged = (() => {\n if (!projectId) return false;\n const oldFieldMetadata = Object.keys(projectFieldsMetadata)\n .filter(key => Object.keys(projectFieldsMetadataPristine).includes(key))\n .reduce((filteredObj, key) => {\n filteredObj[key] = projectFieldsMetadata[key];\n return filteredObj;\n }, {} as { [key: string]: any });\n const hasChangedInOldMetadata = !isEqual(\n oldFieldMetadata,\n projectFieldsMetadataPristine\n );\n\n const newFieldMetadataKeys = Object.keys(projectFieldsMetadata).filter(\n key => !Object.keys(projectFieldsMetadataPristine).includes(key)\n );\n const hasChangedInNewMetadata = newFieldMetadataKeys.some(\n key => projectFieldsMetadata[key]?.isLocked === true\n );\n return hasChangedInNewMetadata || hasChangedInOldMetadata;\n })();\n\n const checkIfFieldIsLocked = useMemo(\n () => (fieldId: string) => {\n if (!projectSchemaLookup || !projectFieldsMetadata[fieldId] || !projectId)\n return false;\n const fieldSchema = projectSchemaLookup[fieldId];\n return (\n fieldSchema.isLockable &&\n projectFieldsMetadata[fieldId].isLocked === true\n );\n },\n [projectSchemaLookup, projectFieldsMetadata, projectId]\n );\n\n const updateProjectFieldMetadata = useCallback(\n (fieldId: string, projectFieldMetadata: FieldMetadata) => {\n if (!projectId) return;\n dispatch(\n actions.updateProjectFieldMetadata(projectId, fieldId, {\n ...projectFieldMetadata\n })\n );\n },\n [projectId, dispatch]\n );\n\n const updateProjectMetadata = useCallback(\n (projectMetadata: { [key: string]: FieldMetadata }) => {\n if (!projectId) return;\n dispatch(actions.updateProjectMetadata(projectId, projectMetadata));\n },\n [projectId, dispatch]\n );\n\n return {\n projectFieldsMetadata,\n projectFieldsMetadataPristine,\n hasChanged,\n updateProjectMetadata,\n updateProjectFieldMetadata,\n checkIfFieldIsLocked\n };\n};\n\nexport const usePreConId = () => {\n const [preConId, setPreConId] = useState(null);\n const [isLoading, setIsLoading] = useState(false);\n const dispatch = useDispatch();\n const token = useSelector(accountSelectors.getHcssToken);\n const businessUnitId = useSelectedBusinessUnitId();\n\n const projectSchema = useSelector(schemaSelectors.getProjectSchema);\n const isPreConIdDisabled =\n projectSchema?.fields?.[\"preconId\"]?.hiddenInForm &&\n projectSchema?.fields?.[\"preconId\"]?.hiddenInTable;\n\n const getAndReservePreConId = useCallback(async () => {\n if (isPreConIdDisabled) return;\n if (token) {\n try {\n const api = new ProjectsApi(token, businessUnitId);\n setIsLoading(true);\n const result = await api.getLastPreConIdAndReserveNext();\n if (Object.keys(result.data).length === 0) setPreConId(null);\n setPreConId(result.data);\n setIsLoading(false);\n return result.data;\n } catch (error) {\n setIsLoading(false);\n console.error(\"Failed to get and reserve PreCon Id.\");\n dispatch(actions.loadPreConID.failure(error as Error));\n }\n }\n }, [token, dispatch, isPreConIdDisabled, businessUnitId]);\n\n const discardReservedPreConId = useCallback(\n async (preConId: string) => {\n if (isPreConIdDisabled) return;\n if (token && preConId) {\n try {\n setIsLoading(true);\n const api = new ProjectsApi(token, businessUnitId);\n await api.discardReservedPreConId(preConId);\n setIsLoading(false);\n } catch (error) {\n setIsLoading(false);\n console.error(\"Failed to discard the reserved PreCon Id.\");\n dispatch(actions.loadPreConID.failure(error as Error));\n }\n }\n },\n [dispatch, token, isPreConIdDisabled, businessUnitId]\n );\n\n return {\n isLoading,\n preConId,\n getAndReservePreConId,\n discardReservedPreConId\n };\n};\n","import appconfig from \"../config\";\nimport getDefaultInstance from \"./AxiosInstance\";\nimport { ContactsClient } from \"./GeneratedClients/ContactsClient\";\n\nconst baseUrl: string = appconfig.endpoints.CONTACTS;\n\nexport default class ContactsApi extends ContactsClient {\n constructor(token: string, selectedBusinessUnitId: string) {\n const instance = getDefaultInstance(token);\n instance.interceptors.request.use(config => {\n config.headers[\"X-Requested-Business-Unit-Id\"] = selectedBusinessUnitId;\n return config;\n });\n\n super(baseUrl, instance);\n }\n}\n","/* tslint:disable */\n/* eslint-disable */\n//----------------------\n// \n// Generated using the NSwag toolchain v13.1.3.0 (NJsonSchema v10.0.27.0 (Newtonsoft.Json v11.0.0.0)) (http://NSwag.org)\n// \n//----------------------\n// ReSharper disable InconsistentNaming\n\nimport axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from \"axios\";\n\nexport class EstimateHistoryClient {\n private instance: AxiosInstance;\n private baseUrl: string;\n protected jsonParseReviver:\n | ((key: string, value: any) => any)\n | undefined = undefined;\n\n constructor(baseUrl?: string, instance?: AxiosInstance) {\n this.instance = instance ? instance : axios.create();\n this.baseUrl = baseUrl ? baseUrl : \"\";\n }\n\n estimates_GetEstimates(\n opts: ODataQueryOptionsOfEstimate | null,\n dto: string | null | undefined,\n activeEstimates: boolean | undefined,\n hideExcludedEstimates: boolean | undefined\n ): Promise {\n let url_ = this.baseUrl + \"/api/estimates?\";\n if (dto !== undefined) url_ += \"dto=\" + encodeURIComponent(\"\" + dto) + \"&\";\n if (activeEstimates === null)\n throw new Error(\"The parameter 'activeEstimates' cannot be null.\");\n else if (activeEstimates !== undefined)\n url_ +=\n \"activeEstimates=\" + encodeURIComponent(\"\" + activeEstimates) + \"&\";\n if (hideExcludedEstimates === null)\n throw new Error(\"The parameter 'hideExcludedEstimates' cannot be null.\");\n else if (hideExcludedEstimates !== undefined)\n url_ +=\n \"hideExcludedEstimates=\" +\n encodeURIComponent(\"\" + hideExcludedEstimates) +\n \"&\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(opts);\n\n let options_ = {\n data: content_,\n method: \"GET\",\n url: url_,\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processEstimates_GetEstimates(_response);\n });\n }\n\n protected processEstimates_GetEstimates(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200) {\n const _responseText = response.data;\n let result200: any = null;\n let resultData200 = _responseText;\n if (Array.isArray(resultData200)) {\n result200 = [] as any;\n for (let item of resultData200)\n result200!.push(EstimateSummary.fromJS(item));\n }\n return result200;\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n estimates_GetEstimate(\n id: string,\n dto: string | null | undefined\n ): Promise {\n let url_ = this.baseUrl + \"/api/estimates/{id}?\";\n if (id === undefined || id === null)\n throw new Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n if (dto !== undefined) url_ += \"dto=\" + encodeURIComponent(\"\" + dto) + \"&\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n method: \"GET\",\n url: url_,\n headers: {\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processEstimates_GetEstimate(_response);\n });\n }\n\n protected processEstimates_GetEstimate(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200) {\n const _responseText = response.data;\n let result200: any = null;\n let resultData200 = _responseText;\n result200 = EstimateSummaryExtended.fromJS(resultData200);\n return result200;\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n estimates_GetEstimateBlob(id: string): Promise {\n let url_ = this.baseUrl + \"/api/estimates/download/{id}\";\n if (id === undefined || id === null)\n throw new Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n responseType: \"blob\",\n method: \"GET\",\n url: url_,\n headers: {\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processEstimates_GetEstimateBlob(_response);\n });\n }\n\n protected processEstimates_GetEstimateBlob(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200 || status === 206) {\n const contentDisposition = response.headers\n ? response.headers[\"content-disposition\"]\n : undefined;\n const fileNameMatch = contentDisposition\n ? /filename=\"?([^\"]*?)\"?(;|$)/g.exec(contentDisposition)\n : undefined;\n const fileName =\n fileNameMatch && fileNameMatch.length > 1\n ? fileNameMatch[1]\n : undefined;\n return Promise.resolve({\n fileName: fileName,\n status: status,\n data: response.data as Blob,\n headers: _headers\n });\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n estimates_PutEstimate(id: string, estimate: Estimate): Promise {\n let url_ = this.baseUrl + \"/api/estimates/{id}/{estimate}\";\n if (id === undefined || id === null)\n throw new Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n if (estimate === undefined || estimate === null)\n throw new Error(\"The parameter 'estimate' must be defined.\");\n url_ = url_.replace(\"{estimate}\", encodeURIComponent(\"\" + estimate));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n method: \"PUT\",\n url: url_,\n headers: {}\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processEstimates_PutEstimate(_response);\n });\n }\n\n protected processEstimates_PutEstimate(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 204) {\n const _responseText = response.data;\n return Promise.resolve(null);\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n estimates_PostExcluded(\n estimateToSet: EstimateExcluded | null\n ): Promise {\n let url_ = this.baseUrl + \"/api/estimates/excluded\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(estimateToSet);\n\n let options_ = {\n data: content_,\n method: \"POST\",\n url: url_,\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processEstimates_PostExcluded(_response);\n });\n }\n\n protected processEstimates_PostExcluded(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200) {\n const _responseText = response.data;\n let result200: any = null;\n let resultData200 = _responseText;\n result200 = Estimate.fromJS(resultData200);\n return result200;\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n estimates_DeleteEstimateById(id: string): Promise {\n let url_ = this.baseUrl + \"/api/estimates/delete/{id}\";\n if (id === undefined || id === null)\n throw new Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n method: \"GET\",\n url: url_,\n headers: {\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processEstimates_DeleteEstimateById(_response);\n });\n }\n\n protected processEstimates_DeleteEstimateById(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200) {\n const _responseText = response.data;\n let result200: any = null;\n let resultData200 = _responseText;\n result200 = DeleteEstimateViewModel.fromJS(resultData200);\n return result200;\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n estimates_DeleteEstimateByCode(\n code: string | null\n ): Promise {\n let url_ = this.baseUrl + \"/api/estimates/delete?\";\n if (code === undefined)\n throw new Error(\"The parameter 'code' must be defined.\");\n else url_ += \"code=\" + encodeURIComponent(\"\" + code) + \"&\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n method: \"GET\",\n url: url_,\n headers: {\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processEstimates_DeleteEstimateByCode(_response);\n });\n }\n\n protected processEstimates_DeleteEstimateByCode(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200) {\n const _responseText = response.data;\n let result200: any = null;\n let resultData200 = _responseText;\n result200 = DeleteEstimateViewModel.fromJS(resultData200);\n return result200;\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n estimates_DoesEstimateCodesExist(\n codes: string[] | null | undefined,\n searchActiveEstimates: boolean | undefined\n ): Promise {\n let url_ = this.baseUrl + \"/api/estimates/exist?\";\n if (codes !== undefined)\n codes &&\n codes.forEach(item => {\n url_ += \"codes=\" + encodeURIComponent(\"\" + item) + \"&\";\n });\n if (searchActiveEstimates === null)\n throw new Error(\"The parameter 'searchActiveEstimates' cannot be null.\");\n else if (searchActiveEstimates !== undefined)\n url_ +=\n \"searchActiveEstimates=\" +\n encodeURIComponent(\"\" + searchActiveEstimates) +\n \"&\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n method: \"GET\",\n url: url_,\n headers: {\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processEstimates_DoesEstimateCodesExist(_response);\n });\n }\n\n protected processEstimates_DoesEstimateCodesExist(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200) {\n const _responseText = response.data;\n let result200: any = null;\n let resultData200 = _responseText;\n if (Array.isArray(resultData200)) {\n result200 = [] as any;\n for (let item of resultData200)\n result200!.push(EstimateExistViewModel.fromJS(item));\n }\n return result200;\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n estimates_GetEstimateIndex(\n opts: ODataQueryOptionsOfEstimate | null,\n code: string | null\n ): Promise {\n let url_ = this.baseUrl + \"/api/estimates/estimateindex?\";\n if (code === undefined)\n throw new Error(\"The parameter 'code' must be defined.\");\n else url_ += \"code=\" + encodeURIComponent(\"\" + code) + \"&\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(opts);\n\n let options_ = {\n data: content_,\n method: \"GET\",\n url: url_,\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processEstimates_GetEstimateIndex(_response);\n });\n }\n\n protected processEstimates_GetEstimateIndex(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200) {\n const _responseText = response.data;\n let result200: any = null;\n let resultData200 = _responseText;\n result200 = EstimateIndex.fromJS(resultData200);\n return result200;\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n estimates_GetEstimateBackupHash(estimateId: string): Promise {\n let url_ = this.baseUrl + \"/api/estimates/hash?\";\n if (estimateId === undefined || estimateId === null)\n throw new Error(\n \"The parameter 'estimateId' must be defined and cannot be null.\"\n );\n else url_ += \"estimateId=\" + encodeURIComponent(\"\" + estimateId) + \"&\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n method: \"GET\",\n url: url_,\n headers: {\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processEstimates_GetEstimateBackupHash(_response);\n });\n }\n\n protected processEstimates_GetEstimateBackupHash(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200) {\n const _responseText = response.data;\n let result200: any = null;\n let resultData200 = _responseText;\n result200 = resultData200 !== undefined ? resultData200 : null;\n return result200;\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n insightsEstimates_GetEstimate(\n opts: ODataQueryOptionsOfEstimateFlattened | null,\n liveEstimates: boolean | undefined\n ): Promise {\n let url_ = this.baseUrl + \"/api/insights/estimates?\";\n if (liveEstimates === null)\n throw new Error(\"The parameter 'liveEstimates' cannot be null.\");\n else if (liveEstimates !== undefined)\n url_ += \"liveEstimates=\" + encodeURIComponent(\"\" + liveEstimates) + \"&\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(opts);\n\n let options_ = {\n data: content_,\n method: \"GET\",\n url: url_,\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processInsightsEstimates_GetEstimate(_response);\n });\n }\n\n protected processInsightsEstimates_GetEstimate(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200) {\n const _responseText = response.data;\n let result200: any = null;\n let resultData200 = _responseText;\n if (Array.isArray(resultData200)) {\n result200 = [] as any;\n for (let item of resultData200)\n result200!.push(EstimateFlattened.fromJS(item));\n }\n return result200;\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n insightsEstimates_GetReportsForEstimate(\n estimateId: string\n ): Promise {\n let url_ = this.baseUrl + \"/api/insights/estimates/reports?\";\n if (estimateId === undefined || estimateId === null)\n throw new Error(\n \"The parameter 'estimateId' must be defined and cannot be null.\"\n );\n else url_ += \"estimateId=\" + encodeURIComponent(\"\" + estimateId) + \"&\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n method: \"GET\",\n url: url_,\n headers: {\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processInsightsEstimates_GetReportsForEstimate(_response);\n });\n }\n\n protected processInsightsEstimates_GetReportsForEstimate(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200) {\n const _responseText = response.data;\n let result200: any = null;\n let resultData200 = _responseText;\n result200 = ReportsDTO.fromJS(resultData200);\n return result200;\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n insightsEstimates_GetUniqueEstimateFilters(): Promise {\n let url_ = this.baseUrl + \"/api/insights/estimates/filters\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n method: \"GET\",\n url: url_,\n headers: {\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processInsightsEstimates_GetUniqueEstimateFilters(_response);\n });\n }\n\n protected processInsightsEstimates_GetUniqueEstimateFilters(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200) {\n const _responseText = response.data;\n let result200: any = null;\n let resultData200 = _responseText;\n result200 = UniqueEstimateFilters.fromJS(resultData200);\n return result200;\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n insightsEstimates_PostExcluded(\n estimateToSet: EstimateExcluded | null\n ): Promise {\n let url_ = this.baseUrl + \"/api/insights/estimates/excluded\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(estimateToSet);\n\n let options_ = {\n data: content_,\n method: \"POST\",\n url: url_,\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processInsightsEstimates_PostExcluded(_response);\n });\n }\n\n protected processInsightsEstimates_PostExcluded(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200) {\n const _responseText = response.data;\n let result200: any = null;\n let resultData200 = _responseText;\n result200 = Estimate.fromJS(resultData200);\n return result200;\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n quoteFolders_GetQuoteFolders(\n estimateId: string | null | undefined\n ): Promise {\n let url_ = this.baseUrl + \"/api/v1/quoteFolders?\";\n if (estimateId !== undefined)\n url_ += \"estimateId=\" + encodeURIComponent(\"\" + estimateId) + \"&\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n method: \"GET\",\n url: url_,\n headers: {\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processQuoteFolders_GetQuoteFolders(_response);\n });\n }\n\n protected processQuoteFolders_GetQuoteFolders(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200) {\n const _responseText = response.data;\n let result200: any = null;\n let resultData200 = _responseText;\n if (Array.isArray(resultData200)) {\n result200 = [] as any;\n for (let item of resultData200)\n result200!.push(QuoteFolderSummaryDto.fromJS(item));\n }\n return result200;\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n quoteFolders_CreateQuoteFolder(): Promise {\n let url_ = this.baseUrl + \"/api/v1/quoteFolders\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n responseType: \"blob\",\n method: \"POST\",\n url: url_,\n headers: {\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processQuoteFolders_CreateQuoteFolder(_response);\n });\n }\n\n protected processQuoteFolders_CreateQuoteFolder(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200 || status === 206) {\n const contentDisposition = response.headers\n ? response.headers[\"content-disposition\"]\n : undefined;\n const fileNameMatch = contentDisposition\n ? /filename=\"?([^\"]*?)\"?(;|$)/g.exec(contentDisposition)\n : undefined;\n const fileName =\n fileNameMatch && fileNameMatch.length > 1\n ? fileNameMatch[1]\n : undefined;\n return Promise.resolve({\n fileName: fileName,\n status: status,\n data: response.data as Blob,\n headers: _headers\n });\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n quoteFolders_GetQuoteFolder(id: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/quoteFolders/{id}\";\n if (id === undefined || id === null)\n throw new Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n method: \"GET\",\n url: url_,\n headers: {\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processQuoteFolders_GetQuoteFolder(_response);\n });\n }\n\n protected processQuoteFolders_GetQuoteFolder(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200) {\n const _responseText = response.data;\n let result200: any = null;\n let resultData200 = _responseText;\n result200 = QuoteFolderDto.fromJS(resultData200);\n return result200;\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n quoteFolders_UpdateQuoteFolder(id: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/quoteFolders/{id}\";\n if (id === undefined || id === null)\n throw new Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n responseType: \"blob\",\n method: \"PUT\",\n url: url_,\n headers: {\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processQuoteFolders_UpdateQuoteFolder(_response);\n });\n }\n\n protected processQuoteFolders_UpdateQuoteFolder(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200 || status === 206) {\n const contentDisposition = response.headers\n ? response.headers[\"content-disposition\"]\n : undefined;\n const fileNameMatch = contentDisposition\n ? /filename=\"?([^\"]*?)\"?(;|$)/g.exec(contentDisposition)\n : undefined;\n const fileName =\n fileNameMatch && fileNameMatch.length > 1\n ? fileNameMatch[1]\n : undefined;\n return Promise.resolve({\n fileName: fileName,\n status: status,\n data: response.data as Blob,\n headers: _headers\n });\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n quoteFolders_DeleteQuoteFolder(id: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/quoteFolders/{id}\";\n if (id === undefined || id === null)\n throw new Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n responseType: \"blob\",\n method: \"DELETE\",\n url: url_,\n headers: {\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processQuoteFolders_DeleteQuoteFolder(_response);\n });\n }\n\n protected processQuoteFolders_DeleteQuoteFolder(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200 || status === 206) {\n const contentDisposition = response.headers\n ? response.headers[\"content-disposition\"]\n : undefined;\n const fileNameMatch = contentDisposition\n ? /filename=\"?([^\"]*?)\"?(;|$)/g.exec(contentDisposition)\n : undefined;\n const fileName =\n fileNameMatch && fileNameMatch.length > 1\n ? fileNameMatch[1]\n : undefined;\n return Promise.resolve({\n fileName: fileName,\n status: status,\n data: response.data as Blob,\n headers: _headers\n });\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n quoteFolders_GetQuoteFoldersDetails(\n estimateId: string | null | undefined\n ): Promise {\n let url_ = this.baseUrl + \"/api/v1/quoteFolders/details?\";\n if (estimateId !== undefined)\n url_ += \"estimateId=\" + encodeURIComponent(\"\" + estimateId) + \"&\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n method: \"GET\",\n url: url_,\n headers: {\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processQuoteFolders_GetQuoteFoldersDetails(_response);\n });\n }\n\n protected processQuoteFolders_GetQuoteFoldersDetails(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200) {\n const _responseText = response.data;\n let result200: any = null;\n let resultData200 = _responseText;\n if (Array.isArray(resultData200)) {\n result200 = [] as any;\n for (let item of resultData200)\n result200!.push(QuoteFolderDto.fromJS(item));\n }\n return result200;\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n quoteMasters_GetQuoteMasterByEstimateCode(\n estimateCode: string\n ): Promise {\n let url_ = this.baseUrl + \"/api/v1/quoteMasters/{estimateCode}\";\n if (estimateCode === undefined || estimateCode === null)\n throw new Error(\"The parameter 'estimateCode' must be defined.\");\n url_ = url_.replace(\n \"{estimateCode}\",\n encodeURIComponent(\"\" + estimateCode)\n );\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n method: \"GET\",\n url: url_,\n headers: {\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processQuoteMasters_GetQuoteMasterByEstimateCode(_response);\n });\n }\n\n protected processQuoteMasters_GetQuoteMasterByEstimateCode(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200) {\n const _responseText = response.data;\n let result200: any = null;\n let resultData200 = _responseText;\n result200 = QuoteMasterDto.fromJS(resultData200);\n return result200;\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n quoteMasters_SyncQuotesMaster(\n model: QuoteMasterDto | null\n ): Promise {\n let url_ = this.baseUrl + \"/api/v1/quoteMasters\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(model);\n\n let options_ = {\n data: content_,\n method: \"POST\",\n url: url_,\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processQuoteMasters_SyncQuotesMaster(_response);\n });\n }\n\n protected processQuoteMasters_SyncQuotesMaster(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200) {\n const _responseText = response.data;\n let result200: any = null;\n let resultData200 = _responseText;\n result200 = GenericResponse.fromJS(resultData200);\n return result200;\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n quotes_GetQuotes(): Promise {\n let url_ = this.baseUrl + \"/api/v1/quotes\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n responseType: \"blob\",\n method: \"GET\",\n url: url_,\n headers: {\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processQuotes_GetQuotes(_response);\n });\n }\n\n protected processQuotes_GetQuotes(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200 || status === 206) {\n const contentDisposition = response.headers\n ? response.headers[\"content-disposition\"]\n : undefined;\n const fileNameMatch = contentDisposition\n ? /filename=\"?([^\"]*?)\"?(;|$)/g.exec(contentDisposition)\n : undefined;\n const fileName =\n fileNameMatch && fileNameMatch.length > 1\n ? fileNameMatch[1]\n : undefined;\n return Promise.resolve({\n fileName: fileName,\n status: status,\n data: response.data as Blob,\n headers: _headers\n });\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n quotes_CreateQuote(): Promise {\n let url_ = this.baseUrl + \"/api/v1/quotes\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n method: \"POST\",\n url: url_,\n headers: {}\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processQuotes_CreateQuote(_response);\n });\n }\n\n protected processQuotes_CreateQuote(response: AxiosResponse): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 204) {\n const _responseText = response.data;\n return Promise.resolve(null);\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n quotes_GetQuote(id: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/quotes/{id}\";\n if (id === undefined || id === null)\n throw new Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n responseType: \"blob\",\n method: \"GET\",\n url: url_,\n headers: {\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processQuotes_GetQuote(_response);\n });\n }\n\n protected processQuotes_GetQuote(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200 || status === 206) {\n const contentDisposition = response.headers\n ? response.headers[\"content-disposition\"]\n : undefined;\n const fileNameMatch = contentDisposition\n ? /filename=\"?([^\"]*?)\"?(;|$)/g.exec(contentDisposition)\n : undefined;\n const fileName =\n fileNameMatch && fileNameMatch.length > 1\n ? fileNameMatch[1]\n : undefined;\n return Promise.resolve({\n fileName: fileName,\n status: status,\n data: response.data as Blob,\n headers: _headers\n });\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n quotes_UpdateQuote(id: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/quotes/{id}\";\n if (id === undefined || id === null)\n throw new Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n method: \"PUT\",\n url: url_,\n headers: {}\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processQuotes_UpdateQuote(_response);\n });\n }\n\n protected processQuotes_UpdateQuote(response: AxiosResponse): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 204) {\n const _responseText = response.data;\n return Promise.resolve(null);\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n quotes_DeleteQuote(id: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/quotes/{id}\";\n if (id === undefined || id === null)\n throw new Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n method: \"DELETE\",\n url: url_,\n headers: {}\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processQuotes_DeleteQuote(_response);\n });\n }\n\n protected processQuotes_DeleteQuote(response: AxiosResponse): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 204) {\n const _responseText = response.data;\n return Promise.resolve(null);\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n quotes_AcceptQuote(dto: AcceptQuoteDto | null): Promise {\n let url_ = this.baseUrl + \"/api/v1/quotes/accept\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(dto);\n\n let options_ = {\n data: content_,\n responseType: \"blob\",\n method: \"POST\",\n url: url_,\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processQuotes_AcceptQuote(_response);\n });\n }\n\n protected processQuotes_AcceptQuote(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200 || status === 206) {\n const contentDisposition = response.headers\n ? response.headers[\"content-disposition\"]\n : undefined;\n const fileNameMatch = contentDisposition\n ? /filename=\"?([^\"]*?)\"?(;|$)/g.exec(contentDisposition)\n : undefined;\n const fileName =\n fileNameMatch && fileNameMatch.length > 1\n ? fileNameMatch[1]\n : undefined;\n return Promise.resolve({\n fileName: fileName,\n status: status,\n data: response.data as Blob,\n headers: _headers\n });\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n quoteVendors_GetVendors(\n code: string | null | undefined,\n estimateId: string | null | undefined,\n folderId: string | null | undefined\n ): Promise {\n let url_ = this.baseUrl + \"/api/v1/quoteVendors?\";\n if (code !== undefined)\n url_ += \"code=\" + encodeURIComponent(\"\" + code) + \"&\";\n if (estimateId !== undefined)\n url_ += \"estimateId=\" + encodeURIComponent(\"\" + estimateId) + \"&\";\n if (folderId !== undefined)\n url_ += \"folderId=\" + encodeURIComponent(\"\" + folderId) + \"&\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n method: \"GET\",\n url: url_,\n headers: {\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processQuoteVendors_GetVendors(_response);\n });\n }\n\n protected processQuoteVendors_GetVendors(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200) {\n const _responseText = response.data;\n let result200: any = null;\n let resultData200 = _responseText;\n if (Array.isArray(resultData200)) {\n result200 = [] as any;\n for (let item of resultData200)\n result200!.push(QuoteVendorSummaryDto.fromJS(item));\n }\n return result200;\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n quoteVendors_CreateVendor(): Promise {\n let url_ = this.baseUrl + \"/api/v1/quoteVendors\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n method: \"POST\",\n url: url_,\n headers: {}\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processQuoteVendors_CreateVendor(_response);\n });\n }\n\n protected processQuoteVendors_CreateVendor(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 204) {\n const _responseText = response.data;\n return Promise.resolve(null);\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n quoteVendors_UpdateVendor(): Promise {\n let url_ = this.baseUrl + \"/api/v1/quoteVendors\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n method: \"PUT\",\n url: url_,\n headers: {}\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processQuoteVendors_UpdateVendor(_response);\n });\n }\n\n protected processQuoteVendors_UpdateVendor(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 204) {\n const _responseText = response.data;\n return Promise.resolve(null);\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n quoteVendors_DeleteVendor(): Promise {\n let url_ = this.baseUrl + \"/api/v1/quoteVendors\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n method: \"DELETE\",\n url: url_,\n headers: {}\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processQuoteVendors_DeleteVendor(_response);\n });\n }\n\n protected processQuoteVendors_DeleteVendor(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 204) {\n const _responseText = response.data;\n return Promise.resolve(null);\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n quoteVendors_GetVendor(id: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/quoteVendors/{id}\";\n if (id === undefined || id === null)\n throw new Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n method: \"GET\",\n url: url_,\n headers: {\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processQuoteVendors_GetVendor(_response);\n });\n }\n\n protected processQuoteVendors_GetVendor(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200) {\n const _responseText = response.data;\n let result200: any = null;\n let resultData200 = _responseText;\n result200 = QuoteVendorDto.fromJS(resultData200);\n return result200;\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n quoteMasters_GetEstimatesPriceSyncStatus(): Promise {\n let url_ = this.baseUrl + \"/api/v1/quoteMasters/lastsyncedprices\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n method: \"GET\",\n url: url_,\n headers: {\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processQuoteMasters_GetEstimatesPriceSyncStatus(_response);\n });\n }\n\n protected processQuoteMasters_GetEstimatesPriceSyncStatus(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200) {\n const _responseText = response.data;\n let result200: any = null;\n let resultData200 = _responseText;\n if (Array.isArray(resultData200)) {\n result200 = [] as any;\n for (let item of resultData200)\n result200!.push(EstimateSyncStatusDto.fromJS(item));\n }\n return result200;\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n\n storageReports_GetReport(\n estimateId: string,\n reportType: string | null\n ): Promise {\n let url_ = this.baseUrl + \"/api/v1/storage/reports?\";\n if (estimateId === undefined || estimateId === null)\n throw new Error(\n \"The parameter 'estimateId' must be defined and cannot be null.\"\n );\n else url_ += \"estimateId=\" + encodeURIComponent(\"\" + estimateId) + \"&\";\n if (reportType === undefined)\n throw new Error(\"The parameter 'reportType' must be defined.\");\n else url_ += \"reportType=\" + encodeURIComponent(\"\" + reportType) + \"&\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_ = {\n responseType: \"blob\",\n method: \"GET\",\n url: url_,\n headers: {\n Accept: \"application/json\"\n }\n };\n\n return this.instance.request(options_).then((_response: AxiosResponse) => {\n return this.processStorageReports_GetReport(_response);\n });\n }\n\n protected processStorageReports_GetReport(\n response: AxiosResponse\n ): Promise {\n const status = response.status;\n let _headers: any = {};\n if (response.headers && typeof response.headers === \"object\") {\n for (let k in response.headers) {\n if (response.headers.hasOwnProperty(k)) {\n _headers[k] = response.headers[k];\n }\n }\n }\n if (status === 200 || status === 206) {\n const contentDisposition = response.headers\n ? response.headers[\"content-disposition\"]\n : undefined;\n const fileNameMatch = contentDisposition\n ? /filename=\"?([^\"]*?)\"?(;|$)/g.exec(contentDisposition)\n : undefined;\n const fileName =\n fileNameMatch && fileNameMatch.length > 1\n ? fileNameMatch[1]\n : undefined;\n return Promise.resolve({\n fileName: fileName,\n status: status,\n data: response.data as Blob,\n headers: _headers\n });\n } else if (status !== 200 && status !== 204) {\n const _responseText = response.data;\n return throwException(\n \"An unexpected server error occurred.\",\n status,\n _responseText,\n _headers\n );\n }\n return Promise.resolve(null);\n }\n}\n\nexport class EstimateSummary implements IEstimateSummary {\n id!: string;\n code!: string;\n name!: string;\n processedStatus!: StatusCode;\n processedStatusFailureCode!: ProcessedEstimateFailureCode;\n estimateVersion?: string | undefined;\n bid_Date?: Date | undefined;\n start_Date?: Date | undefined;\n review_Date?: Date | undefined;\n created_Date?: Date | undefined;\n modified_Date?: Date | undefined;\n uploaded_Datetime?: Date | undefined;\n isExcludedEstimate!: boolean;\n\n constructor(data?: IEstimateSummary) {\n if (data) {\n for (var property in data) {\n if (data.hasOwnProperty(property))\n (this)[property] = (data)[property];\n }\n }\n }\n\n init(_data?: any) {\n if (_data) {\n this.id = _data[\"id\"];\n this.code = _data[\"code\"];\n this.name = _data[\"name\"];\n this.processedStatus = _data[\"processedStatus\"];\n this.processedStatusFailureCode = _data[\"processedStatusFailureCode\"];\n this.estimateVersion = _data[\"estimateVersion\"];\n this.bid_Date = _data[\"bid_Date\"]\n ? new Date(_data[\"bid_Date\"].toString())\n : undefined;\n this.start_Date = _data[\"start_Date\"]\n ? new Date(_data[\"start_Date\"].toString())\n : undefined;\n this.review_Date = _data[\"review_Date\"]\n ? new Date(_data[\"review_Date\"].toString())\n : undefined;\n this.created_Date = _data[\"created_Date\"]\n ? new Date(_data[\"created_Date\"].toString())\n : undefined;\n this.modified_Date = _data[\"modified_Date\"]\n ? new Date(_data[\"modified_Date\"].toString())\n : undefined;\n this.uploaded_Datetime = _data[\"uploaded_Datetime\"]\n ? new Date(_data[\"uploaded_Datetime\"].toString())\n : undefined;\n this.isExcludedEstimate = _data[\"isExcludedEstimate\"];\n }\n }\n\n static fromJS(data: any): EstimateSummary {\n data = typeof data === \"object\" ? data : {};\n let result = new EstimateSummary();\n result.init(data);\n return result;\n }\n\n toJSON(data?: any) {\n data = typeof data === \"object\" ? data : {};\n data[\"id\"] = this.id;\n data[\"code\"] = this.code;\n data[\"name\"] = this.name;\n data[\"processedStatus\"] = this.processedStatus;\n data[\"processedStatusFailureCode\"] = this.processedStatusFailureCode;\n data[\"estimateVersion\"] = this.estimateVersion;\n data[\"bid_Date\"] = this.bid_Date\n ? this.bid_Date.toISOString()\n : undefined;\n data[\"start_Date\"] = this.start_Date\n ? this.start_Date.toISOString()\n : undefined;\n data[\"review_Date\"] = this.review_Date\n ? this.review_Date.toISOString()\n : undefined;\n data[\"created_Date\"] = this.created_Date\n ? this.created_Date.toISOString()\n : undefined;\n data[\"modified_Date\"] = this.modified_Date\n ? this.modified_Date.toISOString()\n : undefined;\n data[\"uploaded_Datetime\"] = this.uploaded_Datetime\n ? this.uploaded_Datetime.toISOString()\n : undefined;\n data[\"isExcludedEstimate\"] = this.isExcludedEstimate;\n return data;\n }\n}\n\nexport interface IEstimateSummary {\n id: string;\n code: string;\n name: string;\n processedStatus: StatusCode;\n processedStatusFailureCode: ProcessedEstimateFailureCode;\n estimateVersion?: string | undefined;\n bid_Date?: Date | undefined;\n start_Date?: Date | undefined;\n review_Date?: Date | undefined;\n created_Date?: Date | undefined;\n modified_Date?: Date | undefined;\n uploaded_Datetime?: Date | undefined;\n isExcludedEstimate: boolean;\n}\n\nexport enum StatusCode {\n Queued = 0,\n Processing = 1,\n Success = 2,\n Failure = 3,\n QueuedLarge = 4,\n TooLarge = 5\n}\n\nexport enum ProcessedEstimateFailureCode {\n None = 0,\n Unknown = 1,\n BlobStorage = 2,\n ExtractEstimateData = 3,\n SaveEstimateData = 4,\n Attachments = 5,\n ConcurrencyCheck = 6,\n EstimateNotFound = 7,\n UpdateEstimateData = 8,\n Reports = 9,\n EstimateUpload = 10\n}\n\n/** This defines a composite OData query options that can be used to perform query composition. Currently this only supports $filter, $orderby, $top, $skip, and $count. */\nexport class ODataQueryOptions implements IODataQueryOptions {\n /** Gets the given ODataQueryContext */\n context?: ODataQueryContext | undefined;\n /** Gets the request message associated with this instance. */\n request?: HttpRequestMessage | undefined;\n /** Gets the raw string of all the OData query options */\n rawValues?: ODataRawQueryOptions | undefined;\n /** Gets the SelectExpandQueryOption. */\n selectExpand?: SelectExpandQueryOption | undefined;\n apply?: ApplyQueryOption | undefined;\n /** Gets the FilterQueryOption. */\n filter?: FilterQueryOption | undefined;\n /** Gets the OrderByQueryOption. */\n orderBy?: OrderByQueryOption | undefined;\n /** Gets the SkipQueryOption. */\n skip?: SkipQueryOption | undefined;\n /** Gets the TopQueryOption. */\n top?: TopQueryOption | undefined;\n /** Gets the CountQueryOption. */\n count?: CountQueryOption | undefined;\n /** Gets or sets the query validator. */\n validator?: ODataQueryValidator | undefined;\n /** Gets the ETag from IfMatch header. */\n ifMatch?: ETag | undefined;\n /** Gets the ETag from IfNoneMatch header. */\n ifNoneMatch?: ETag | undefined;\n\n constructor(data?: IODataQueryOptions) {\n if (data) {\n for (var property in data) {\n if (data.hasOwnProperty(property))\n (this)[property] = (data)[property];\n }\n }\n }\n\n init(_data?: any) {\n if (_data) {\n this.context = _data[\"context\"]\n ? ODataQueryContext.fromJS(_data[\"context\"])\n : undefined;\n this.request = _data[\"request\"]\n ? HttpRequestMessage.fromJS(_data[\"request\"])\n : undefined;\n this.rawValues = _data[\"rawValues\"]\n ? ODataRawQueryOptions.fromJS(_data[\"rawValues\"])\n : undefined;\n this.selectExpand = _data[\"selectExpand\"]\n ? SelectExpandQueryOption.fromJS(_data[\"selectExpand\"])\n : undefined;\n this.apply = _data[\"apply\"]\n ? ApplyQueryOption.fromJS(_data[\"apply\"])\n : undefined;\n this.filter = _data[\"filter\"]\n ? FilterQueryOption.fromJS(_data[\"filter\"])\n : undefined;\n this.orderBy = _data[\"orderBy\"]\n ? OrderByQueryOption.fromJS(_data[\"orderBy\"])\n : undefined;\n this.skip = _data[\"skip\"]\n ? SkipQueryOption.fromJS(_data[\"skip\"])\n : undefined;\n this.top = _data[\"top\"]\n ? TopQueryOption.fromJS(_data[\"top\"])\n : undefined;\n this.count = _data[\"count\"]\n ? CountQueryOption.fromJS(_data[\"count\"])\n : undefined;\n this.validator = _data[\"validator\"]\n ? ODataQueryValidator.fromJS(_data[\"validator\"])\n : undefined;\n this.ifMatch = _data[\"ifMatch\"]\n ? ETag.fromJS(_data[\"ifMatch\"])\n : undefined;\n this.ifNoneMatch = _data[\"ifNoneMatch\"]\n ? ETag.fromJS(_data[\"ifNoneMatch\"])\n : undefined;\n }\n }\n\n static fromJS(data: any): ODataQueryOptions {\n data = typeof data === \"object\" ? data : {};\n let result = new ODataQueryOptions();\n result.init(data);\n return result;\n }\n\n toJSON(data?: any) {\n data = typeof data === \"object\" ? data : {};\n data[\"context\"] = this.context ? this.context.toJSON() : undefined;\n data[\"request\"] = this.request ? this.request.toJSON() :