{"version":3,"file":"index.js","sources":["../../src/api/httpHandlerBase.ts","../../src/api/httpHandler.ts","../../src/api/systemIncidentsApi.ts","../../src/helpers/base64.ts","../../src/helpers/checkPermission.ts","../../src/helpers/constants.ts","../../src/helpers/cookie.ts","../../src/helpers/date.ts","../../src/helpers/general.ts","../../src/helpers/format.ts","../../src/helpers/inlineReducer.ts","../../src/helpers/list.ts","../../src/helpers/listeners.ts","../../src/helpers/logger.ts","../../src/helpers/queryKeys.ts","../../src/helpers/routes.ts","../../src/helpers/useBreakpoint.ts","../../src/api/gridFiltersApi.ts","../../src/contexts/userContext.tsx","../../src/helpers/useRoutePermissions.ts","../../src/components/basic/alertsBanner/alertsBanner.tsx","../../src/components/basic/spinner/index.tsx","../../src/components/basic/button/button.tsx","../../src/components/basic/calendar/calendar.tsx","../../src/api/settingsApi.ts","../../src/components/basic/slider/slider.tsx","../../src/components/basic/charts/noData.tsx","../../src/components/basic/charts/googleMaps/utils.ts","../../src/components/basic/charts/googleMaps/map.tsx","../../src/components/basic/charts/bar.tsx","../../src/components/basic/charts/combo.tsx","../../src/components/basic/charts/heatmap.tsx","../../src/components/basic/charts/pie.tsx","../../src/components/basic/charts/spline.tsx","../../src/components/basic/charts/stackedBar.tsx","../../src/models/keyedArray.ts","../../src/models/sortType.ts","../../src/components/basic/inputs/checkbox.tsx","../../src/components/basic/inputs/indeterminateCheckbox.tsx","../../src/components/basic/loading/textSkeleton.tsx","../../src/components/basic/loading/chartSkeleton.tsx","../../src/components/basic/loading/gridSkeleton.tsx","../../src/components/basic/loading/imageSkeleton.tsx","../../src/components/basic/loading/inputSkeleton.tsx","../../src/components/basic/dataGrid/loading.tsx","../../src/components/basic/dataGrid/mobile.tsx","../../src/components/basic/select/select.tsx","../../src/components/basic/textBox/textBox.tsx","../../src/components/basic/dataGrid/pagination.tsx","../../src/components/basic/dataGrid/tableBody.tsx","../../src/components/basic/dataGrid/tableHead.tsx","../../src/components/basic/dataGrid/utils.ts","../../src/components/basic/dataGrid/dataGrid.tsx","../../src/components/basic/dataGrid/client.tsx","../../src/components/basic/dataGrid/models/column.ts","../../src/components/basic/popover/popover.tsx","../../src/components/basic/date/datePicker.tsx","../../src/components/basic/date/dateRangePicker.tsx","../../src/components/basic/dialog/dialog.tsx","../../src/components/basic/dropzone/dropzone.tsx","../../src/components/basic/link/link.tsx","../../src/components/basic/tabs/tabPage.tsx","../../src/components/basic/tabs/tabsHeader.tsx","../../src/components/basic/tabs/tabs.tsx","../../src/components/basic/tabs/tab.tsx","../../src/components/basic/textBoxWithSelect/textBoxWithSelect.tsx","../../src/components/basic/timeline/timeline.tsx","../../src/components/basic/toast/toastContext.tsx","../../src/components/basic/toast/toast.tsx","../../src/components/basic/toggleButton/toggleButton.tsx","../../src/components/basic/tooltip/tooltip.tsx","../../src/api/homeApi.ts","../../src/api/pingApi.ts","../../src/components/footer/footer.tsx","../../src/hooks/verbiageHooks.tsx","../../src/models/permissions.ts","../../src/components/layout/logo.tsx","../../src/components/layout/header.tsx","../../src/components/basic/link/navLink.tsx","../../src/components/layout/mobileSidebar.tsx","../../src/components/layout/sidebar.tsx","../../src/components/layout/layout.tsx","../../src/api/loginApi.ts","../../src/components/auth/AuthWrapper.tsx","../../src/components/auth/Auth.tsx","../../src/api/scanApi.ts","../../src/contexts/checkInContext.tsx","../../src/components/checkIn/checkInProviders.tsx","../../src/api/auditApi.ts","../../src/api/formApi.ts","../../src/components/basic/loading/textOrLoading.tsx","../../src/components/audit/counts.tsx","../../src/components/audit/auditHooks.ts","../../src/components/audit/missingTpv.tsx","../../src/components/audit/auditPanels.tsx","../../src/components/barcode/barcodeGenerator.tsx","../../src/components/barcode/utils.ts","../../src/components/barcode/barcodeScanner.tsx","../../src/components/safeHtml/safeHtml.tsx","../../src/components/audit/auditQuestionnaire.tsx","../../src/components/audit/multiAudit.tsx","../../src/components/audit/singleAudit.tsx","../../src/pages/audit.tsx","../../src/api/campaignApi.ts","../../src/components/gridFilters/types.ts","../../src/helpers/gridCustomization.ts","../../src/components/gridFilters/filters/activeFilter.tsx","../../src/components/dropdowns/filterDropDown.tsx","../../src/components/dropdowns/campaignDropdown.tsx","../../src/components/gridFilters/filters/campaignFilter.tsx","../../src/components/gridFilters/filters/dateFilters.tsx","../../src/api/verificationsApi.ts","../../src/components/dropdowns/dispositionsDropdown.tsx","../../src/components/gridFilters/filters/dispositionFilter.tsx","../../src/components/dropdowns/languagesDropdown.tsx","../../src/components/gridFilters/filters/languageFilter.tsx","../../src/api/mapApi.ts","../../src/components/dropdowns/locationDropdown.tsx","../../src/components/gridFilters/filters/mapFilter.tsx","../../src/components/dropdowns/repsDropdown.tsx","../../src/components/gridFilters/filters/repFilter.tsx","../../src/components/gridFilters/filters/searchFilter.tsx","../../src/components/gridFilters/filters/searchItemFilter.tsx","../../src/components/dropdowns/channelsDropdown.tsx","../../src/components/dropdowns/clientsDropdown.tsx","../../src/components/dropdowns/statesDropdown.tsx","../../src/components/gridFilters/filters/segmentFilters.tsx","../../src/components/gridFilters/filters/timeFilters.tsx","../../src/components/dropdowns/verModesDropdown.tsx","../../src/components/dropdowns/verTypesDropdown.tsx","../../src/components/gridFilters/filters/verificationFilters.tsx","../../src/components/gridFilters/index.tsx","../../src/components/gridPage/gridPage.tsx","../../src/components/campaigns/createUpdateFormTemplate.tsx","../../src/components/basic/dataGrid/options.tsx","../../src/components/campaigns/options.tsx","../../src/components/campaigns/subComponent.tsx","../../src/components/campaigns/campaignGrid.tsx","../../src/pages/campaigns.tsx","../../src/models/scanBatchDirection.ts","../../src/models/scanBatchMethod.ts","../../src/components/checkIn/createCheckIn.tsx","../../src/components/checkIn/checkInGrid.tsx","../../src/pages/checkIn.tsx","../../src/components/dropdowns/formTemplatesDropdown.tsx","../../src/components/form/createPrintFormBatch.tsx","../../src/components/form/options.tsx","../../src/components/form/selectedActions.tsx","../../src/components/form/formGrid.tsx","../../src/pages/forms.tsx","../../src/api/reportingApi.ts","../../src/components/reporting/generated/exportLink.tsx","../../src/components/reporting/generated/generatedDetails.tsx","../../src/pages/generated.tsx","../../src/api/representativesApi.ts","../../src/components/reporting/historical/agentLeaderboard.tsx","../../src/components/reporting/historical/allDispositions.tsx","../../src/components/dropdowns/districtsDropdown.tsx","../../src/components/reporting/historical/districts.tsx","../../src/components/reporting/historical/generatedResponseCountTable.tsx","../../src/components/reporting/historical/heatMap.tsx","../../src/components/reporting/historical/heatmapMap.tsx","../../src/components/reporting/historical/locations.tsx","../../src/components/reporting/historical/noSaleDispositions.tsx","../../src/components/reporting/historical/percentTPV.tsx","../../src/components/reporting/historical/repLocations.tsx","../../src/components/reporting/historical/repProduction.tsx","../../src/components/reporting/historical/salesOutcome.tsx","../../src/pages/historical.tsx","../../src/components/basic/button/dropdownButton.tsx","../../src/components/gridFilters/filters/export.tsx","../../src/models/verTypes.ts","../../src/components/basic/dataItemView/dataItemView.tsx","../../src/components/verifications/details/files.tsx","../../src/components/verifications/details/map.tsx","../../src/components/verifications/details/receipt.tsx","../../src/components/verifications/details/recording.tsx","../../src/components/representatives/createUpdateRepresentative.tsx","../../src/components/representatives/useToggleRepresentative.ts","../../src/components/representatives/details/repBasicInfoView.tsx","../../src/components/representatives/details/repWidgetEventGrid.tsx","../../src/components/representatives/details/repWidgetGraphs.tsx","../../src/components/representatives/details/repGridDetail.tsx","../../src/components/verifications/details/repData.tsx","../../src/components/verifications/details/verificationInfo.tsx","../../src/components/verifications/details/index.tsx","../../src/components/verifications/verificationGrid.tsx","../../src/pages/home.tsx","../../src/components/reporting/realtime/data.tsx","../../src/components/reporting/realtime/grid.tsx","../../src/components/reporting/realtime/realtime.tsx","../../src/pages/realtime.tsx","../../src/components/basic/tabs/tabLink.tsx","../../src/pages/reports.tsx","../../src/components/representatives/createPrintFormBatch.tsx","../../src/components/representatives/selectedActions.tsx","../../src/components/representatives/representativeGrid.tsx","../../src/pages/representatives.tsx","../../src/components/checkIn/counts.tsx","../../src/components/checkIn/lastScan.tsx","../../src/pages/scan.tsx","../../src/components/audit/createAudit.tsx","../../src/components/audit/auditGrid.tsx","../../src/pages/startAudit.tsx","../../src/components/settings/changePasswordForm.tsx","../../src/pages/user/changePassword.tsx","../../src/pages/user/forgotPassword.tsx","../../src/pages/user/login.tsx","../../src/components/settings/changeThemeForm.tsx","../../src/api/usersApi.ts","../../src/components/settings/updateUserForm.tsx","../../src/pages/user/settings.tsx","../../src/components/users/createUpdateUser.tsx","../../src/components/users/details/userWidgetEventGrid.tsx","../../src/components/users/details/userGridDetail.tsx","../../src/components/users/options.tsx","../../src/components/users/useToggleUser.ts","../../src/components/users/selectedActions.tsx","../../src/components/users/usersGrid.tsx","../../src/pages/users.tsx","../../src/routes.tsx","../../src/app.tsx","../../src/main.tsx"],"sourcesContent":["import axios, { type AxiosError, AxiosHeaders, type AxiosRequestConfig, type AxiosResponse } from \"axios\";\nimport { parseISO } from \"date-fns\";\n\nexport interface Params {\n [key: string]: string | number | boolean | undefined;\n}\n\nexport interface HttpError {\n status: number;\n message: string;\n}\n\nexport async function handlePost(url: string, params?: Params, data?: TIn, config?: AxiosRequestConfig) {\n try {\n const response = await getApi().post(url, data, { params, ...config });\n return response && response.data;\n } catch (error: unknown) {\n throw handleError(error as AxiosError);\n }\n}\n\nexport async function handlePut(url: string, params?: Params, data?: TIn, config?: AxiosRequestConfig) {\n try {\n const response = await getApi().put(url, data, { params, ...config });\n return response && response.data;\n } catch (error: unknown) {\n throw handleError(error as AxiosError);\n }\n}\n\nexport async function handleGet(url: string, params?: Params, config?: AxiosRequestConfig) {\n try {\n const response = await getApi().get(url, { params, ...config });\n return response && response.data;\n } catch (error: unknown) {\n throw handleError(error as AxiosError);\n }\n}\n\nexport async function handleDelete(url: string, id: number) {\n try {\n const response = await getApi().delete(`${url}/${id}`);\n return response && response.data;\n } catch (error: unknown) {\n throw handleError(error as AxiosError);\n }\n}\n\nexport async function handleDownload(url: string, fileName: string, params?: Params, config?: AxiosRequestConfig) {\n try {\n const response = await getApi().get(url, { params, ...config });\n createDownload(response.data, fileName);\n } catch (error: unknown) {\n throw handleError(error as AxiosError);\n }\n}\n\nexport async function handleDownloadPost(\n url: string,\n fileName: string,\n params?: Params,\n data?: TIn,\n config?: AxiosRequestConfig,\n) {\n try {\n const response = await getApi().post(url, data, { params, ...config });\n createDownload(response, fileName);\n } catch (error: unknown) {\n throw handleError(error as AxiosError);\n }\n}\n\nfunction createDownload(file: File, fileName: string) {\n const url = window.URL.createObjectURL(new Blob([file]));\n const link = document.createElement(\"a\");\n link.href = url;\n link.setAttribute(\"download\", fileName);\n document.body.appendChild(link);\n link.click();\n\n document.body.removeChild(link);\n window.URL.revokeObjectURL(url);\n}\n\nfunction handleError(error: AxiosError) {\n error = error || ({} as AxiosError);\n const resp = error.response || ({} as AxiosResponse);\n return {\n status: resp.status,\n message: (resp.data || error.message) as string,\n } as HttpError;\n}\n\nfunction getApi(config?: AxiosRequestConfig) {\n config = config ?? ({ headers: new AxiosHeaders() } as AxiosRequestConfig);\n const client = axios.create({ ...config, withCredentials: true });\n\n client.interceptors.response.use((originalResponse) => {\n handleDates(originalResponse.data);\n return originalResponse;\n });\n\n return client;\n}\n\nconst isoDateFormat = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d*)?(?:[-+]\\d{2}:?\\d{2}|Z)?$/;\n\nfunction isIsoDateString(value: T): boolean {\n return value && typeof value === \"string\" && isoDateFormat.test(value);\n}\n\nfunction handleDates(body: T) {\n if (body === null || body === undefined || typeof body !== \"object\") return;\n\n for (const key of Object.keys(body)) {\n const value = body[key as keyof T];\n if (isIsoDateString(value)) {\n // @ts-ignore\n body[key] = parseISO(value);\n } else if (typeof value === \"object\") handleDates(value);\n }\n}\n","import { type AxiosRequestConfig } from \"axios\";\n\nimport {\n type Params,\n handleDelete,\n handleDownload,\n handleDownloadPost,\n handleGet,\n handlePost,\n handlePut,\n} from \"./httpHandlerBase\";\n\nexport async function doPost(\n controller: string,\n action: string,\n params?: Params,\n data?: TIn,\n config?: AxiosRequestConfig,\n) {\n return await handlePost(buildUrl(controller, action), params, data, config);\n}\n\nexport async function doPut(\n controller: string,\n action: string,\n params?: Params,\n data?: TIn,\n config?: AxiosRequestConfig,\n) {\n return await handlePut(buildUrl(controller, action), params, data, config);\n}\n\nexport async function doGet(controller: string, action: string, params?: Params, config?: AxiosRequestConfig) {\n return await handleGet(buildUrl(controller, action), params, config);\n}\n\nexport async function doDelete(controller: string, action: string, id: number) {\n return await handleDelete(buildUrl(controller, action), id);\n}\n\nexport async function doDownload(\n controller: string,\n action: string,\n fileName: string,\n params?: Params,\n config?: AxiosRequestConfig,\n) {\n return await handleDownload(buildUrl(controller, action), fileName, params, config);\n}\n\nexport async function doDownloadPost(\n controller: string,\n action: string,\n fileName: string,\n params?: Params,\n data?: TIn,\n config?: AxiosRequestConfig,\n) {\n return await handleDownloadPost(buildUrl(controller, action), fileName, params, data, config);\n}\n\nexport function buildUrl(controller: string, action: string) {\n return controller + (action ? `/${action}` : \"\");\n}\n","import { type SystemIncidentDto } from \"../models\";\nimport { doGet } from \"./httpHandler\";\n\nexport class SystemIncidentsApi {\n public static getAlerts = async () => {\n return await doGet(\"/api/SystemIncidents\", \"Active\");\n };\n}\n","import { Buffer } from \"buffer\";\n\nexport function encodeBase64(data: WithImplicitCoercion) {\n return Buffer.from(data).toString(\"base64\");\n}\n\nexport function decodeBase64(data: string) {\n return Buffer.from(data, \"base64\");\n}\n","import { type UserDto } from \"../models\";\nimport { type UserPermissionFlags } from \"../models/permissions\";\n\nexport function checkPermission(user: UserDto | undefined, flag: UserPermissionFlags) {\n return !!user && (user.permissionFlags & flag) === flag;\n}\n","export const HOURS_IN_DAY = 24;\nexport const MINUTES_IN_HOUR = 60;\nexport const SECONDS_IN_MINUTE = 60;\nexport const MS_IN_SECOND = 1000;\n\nexport const TOAST_TIMEOUT = 5000;\n\nconst MS_IN_MINUTE = MS_IN_SECOND * SECONDS_IN_MINUTE;\nconst MS_IN_HOUR = MS_IN_MINUTE * MINUTES_IN_HOUR;\nconst MS_IN_DAY = MS_IN_HOUR * HOURS_IN_DAY;\n\nexport const MS_IN = {\n SECOND: MS_IN_SECOND,\n MINUTE: MS_IN_MINUTE,\n HOUR: MS_IN_HOUR,\n DAY: MS_IN_DAY,\n};\n\nexport default {\n HOURS_IN_DAY,\n MINUTES_IN_HOUR,\n SECONDS_IN_MINUTE,\n MS_IN_SECOND,\n MS_IN,\n};\n\nexport const MAX_INT = 2147483647;\nexport const MIN_INT = -2147483648;\n","import { decodeBase64 } from \"./base64\";\n\nexport function getCookieValue(name: string) {\n const matchingRegex = /([\\w_-]+) ?= ?(.*?)(;|$)/g;\n const matches = document.cookie.matchAll(matchingRegex);\n if (matches === null) return null;\n for (const match of matches) {\n if (match.length < 3) continue;\n if (match[1] === name) {\n return match[2];\n }\n }\n return null;\n}\n\nexport function getPayloadCookie() {\n return getCookieValue(cookieName);\n}\n\nexport function getPayloadCookieAsType() {\n const cookie = getPayloadCookie();\n if (!cookie) return null;\n return JSON.parse(decodeBase64(cookie).toString()) as T;\n}\n\nexport function clearPayloadCookie() {\n setCookie(cookieName, \"\", -1);\n}\n\nexport function setPayloadCookie(value: string) {\n setCookie(cookieName, value, 168);\n}\n\nexport function setCookie(name: string, value: string, hours: number) {\n const seconds = 60 * 60 * hours;\n document.cookie = `${name}=${value}; path=/; samesite=strict; max-age=${seconds}`;\n}\n\nlet cookieName = \"payload\";\n\nexport function setCookieName(name: string) {\n cookieName = name;\n}\n","import { isValid, parse } from \"date-fns\";\n\nimport { DATE_FORMAT } from \"../components/basic\";\n\nexport function tryParseDate(dateString?: string): Date | undefined {\n if (!dateString) return undefined;\n\n const date = parse(dateString, DATE_FORMAT, new Date());\n\n return isValid(date) ? date : undefined;\n}\n\nexport function getTimezoneAbbreviation(date = new Date()): string {\n return date.toLocaleDateString(\"en-us\", { day: \"2-digit\", timeZoneName: \"short\" }).substring(4);\n}\n","/** Not null or undefined */\nexport function hasValue(i: unknown) {\n return i !== undefined && i !== null;\n}\n\n/** Concatenates css styles */\nexport function styles(...styles: (string | boolean | undefined)[]) {\n return styles.filter((s) => s).join(\" \");\n}\n\n/** Creates deep copy of object */\nexport function clone(obj: T) {\n return obj && (JSON.parse(JSON.stringify(obj)) as T);\n}\n\nexport function sleep(ms: number) {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function guid() {\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, function (c) {\n const r = (Math.random() * 16) | 0,\n v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\nexport function isGuid(s: string) {\n const guidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n return guidRegex.test(s);\n}\n\nexport function toFixed(num: number, decimals: number) {\n decimals = Math.pow(10, decimals);\n return Math.floor(num * decimals) / decimals;\n}\n","import { addHours, format, startOfDay } from \"date-fns\";\n\nimport * as constants from \"./constants\";\nimport { hasValue } from \"./general\";\n\n/**Formats name into a string */\nexport function formatName(firstName: string, lastName: string, normal?: boolean) {\n if (!firstName || !lastName) return firstName || lastName;\n if (!normal) return lastName + \", \" + firstName;\n return firstName + \" \" + lastName;\n}\n\n/** Format city and state info into string */\nexport function formatCityState(city: string, state: string, zip: string | number) {\n let str = \"\";\n if (city && state) str = city + \", \" + state;\n else if (city) str = city;\n else if (state) str = state;\n\n if (zip) {\n if (str) str += \" \";\n str += zip;\n }\n return str;\n}\n\n/** Format phone number into single string\n * NOTE: currently doesn't handle extensions\n */\nexport function formatPhone(number: string | number) {\n if (!number) return number;\n let onlyDigits = number.toString().replace(/\\D/g, \"\");\n if (onlyDigits.length < 10) return onlyDigits;\n let formatted = \"\";\n if (onlyDigits.length > 10) {\n const extraDigits = onlyDigits.length - 10;\n formatted += \"+\";\n formatted += onlyDigits.slice(0, extraDigits);\n formatted += \" \";\n onlyDigits = onlyDigits.slice(extraDigits);\n }\n formatted += \"(\" + onlyDigits.slice(0, 3) + \") \";\n formatted += onlyDigits.slice(3, 6);\n formatted += \"-\" + onlyDigits.slice(6);\n return formatted;\n}\n\n/** Format phone number into single string\n * NOTE: currently doesn't handle extensions\n */\nexport function formatPhoneOnlyNumeric(number: string) {\n if (!number) return number;\n return number.replace(/\\D/g, \"\");\n}\n\nexport const formatFullTimeFromSeconds = (totalSeconds: number) => formatTimeFromSeconds(totalSeconds, true);\n\n/** Formats total seconds into hours and minutes and seconds */\nexport function formatTimeFromSeconds(totalSeconds: number, full?: boolean) {\n if (!hasValue(totalSeconds)) return null;\n const totalMinutes = Math.floor(totalSeconds / constants.SECONDS_IN_MINUTE);\n const hours = Math.floor(totalMinutes / constants.MINUTES_IN_HOUR);\n const minutes = totalMinutes % constants.MINUTES_IN_HOUR;\n const seconds = totalSeconds % constants.SECONDS_IN_MINUTE;\n\n const displayingHours = full || hours;\n\n const dispSeconds = `${seconds < 10 ? \"0\" : \"\"}${seconds}`;\n const dispMinutes = `${displayingHours && minutes < 10 ? \"0\" : \"\"}${minutes}`;\n let time = dispMinutes + \":\" + dispSeconds;\n if (displayingHours) time = `${hours}:\"${time}`;\n return time;\n}\n\n/** Formats date into date-only string */\nexport function formatDateOf(date?: Date | string, fullYear = false) {\n if (!date) return null;\n if (typeof date === \"string\") date = new Date(date);\n\n return format(date, fullYear ? \"PP\" : \"M/d/yy\");\n}\n\n/** Formats date into time-only string */\nexport function formatTimeOf(date?: Date | string, displaySeconds = true) {\n if (!date) return null;\n if (typeof date === \"string\") date = new Date(date);\n\n return format(date, displaySeconds ? \"h:mm:ss a\" : \"h:mm a\");\n}\n\nexport function intToTime(hour: number) {\n const date = addHours(startOfDay(new Date()), hour);\n return format(date, \"haaa\").toLowerCase(); // 'haaa' formats the hour, followed by am/pm\n}\n\n/** Formats date into date/time string */\nexport function formatDateTimeOf(date?: Date | string, displaySeconds = true, fullYear = false) {\n if (!date) return null;\n if (typeof date === \"string\") date = new Date(date);\n\n let formatString = \"\";\n\n if (fullYear) {\n formatString += \"PP\";\n } else {\n formatString += \"M/d/yy\";\n }\n\n if (displaySeconds) {\n formatString += \" h:mm:ss a\";\n } else {\n formatString += \" h:mm a\";\n }\n\n return format(date, formatString);\n}\n\nexport const formatShortDateOf = (date?: Date | string) => formatDateOf(date, false);\n\n/** Date Time, without seconds */\nexport const formatShortDateTimeOf = (date?: Date | string) => formatDateTimeOf(date, false, false);\n\nexport function formatHoldInUtc(date?: Date | string) {\n if (!date) return \"\";\n if (typeof date === \"string\") date = new Date(date);\n date.setMinutes(date.getTimezoneOffset());\n return date;\n}\n\nexport function formatOriginalDateOf(date?: Date | string) {\n return formatDateOf(formatHoldInUtc(date));\n}\n\nexport function formatDateWithSeparator(date: Date | string, separator: string) {\n if (!date) return \"\";\n if (typeof date === \"string\") date = new Date(date);\n let strDate = date.toISOString();\n strDate = strDate.substring(0, strDate.indexOf(\".\"));\n return strDate.replaceAll(/[-T:]/gm, separator);\n}\n\nexport function formatDateRange(\n start: Date | string,\n end: Date | string,\n showTime = false,\n showSeconds = true,\n showYear = false,\n) {\n if (showTime) {\n return `${formatDateTimeOf(start, showSeconds, showYear) ?? \"\"} - ${formatDateTimeOf(end, showSeconds, showYear) ?? \"\"}`;\n }\n\n return `${formatDateOf(start, showYear) ?? \"\"} - ${formatDateOf(end, showYear) ?? \"\"}`;\n}\n\nexport function formatActive(active: boolean) {\n return active ? \"Active\" : \"Inactive\";\n}\n\nexport function formatBool(b: boolean) {\n return b ? \"Yes\" : \"No\";\n}\n\nexport function splitWordsOnUpperCase(str: string) {\n return str?.replace(/(?<=[A-Z])(?=[A-Z][a-z])|(?<=[^A-Z])(?=[A-Z])|(?<=[A-Za-z])(?=[^A-Za-z])/gm, \" \");\n}\n\nexport function toStartCase(str: string) {\n return str?.replace(/\\w\\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substring(1).toLowerCase());\n}\nexport function toPercent(fraction: number | undefined, decimalPlaces: number, noValue: string = \"\") {\n if (fraction === null || fraction === undefined) return noValue;\n return (fraction * 100.0).toFixed(decimalPlaces) + \"%\";\n}\n\nexport function safePercent(numerator: number, denominator: number, decimalPlaces: number, noValue: string = \"\") {\n if (numerator === 0) return noValue;\n return toPercent(numerator / denominator, decimalPlaces, noValue);\n}\n","import { useReducer } from \"react\";\n\n/** Wraps React's useReducer with a basic reducer that just takes actions of individual property updates */\nexport function usePropReducer(initialState: T) {\n const [state, dispatch] = useReducer(simpleReducer, initialState);\n const update = (...props: PropUpdate[]) => props.forEach(dispatch);\n return [state, update] as [T, (...props: PropUpdate[]) => void];\n function simpleReducer(state: T, action: PropUpdate): T {\n const actionObj = { [action.name]: action.value };\n\n return Object.assign({}, state, actionObj);\n }\n}\ntype PropUpdate = { name: string; value: object };\n","export function first(list: T[]): T | undefined {\n return list.at(0);\n}\n\nexport function last(list: T[]): T | undefined {\n return list.at(list.length - 1);\n}\n\nexport function insertIf(condition: boolean, ...elements: T[]): T[] {\n return condition ? elements : [];\n}\n","import { type MouseEvent } from \"react\";\n\n/** Fires if specific key is pressed, along with specified combo button */\nexport const comboKeyPress =\n (ctrl: boolean, alt = false, shift = false) =>\n (keyCode: number, key?: string) =>\n (fn: (e?: React.KeyboardEvent | KeyboardEvent | MouseEvent) => void) =>\n (event: React.KeyboardEvent | KeyboardEvent) => {\n const altValid = alt === event.altKey;\n const ctrlValid = ctrl === event.ctrlKey;\n const shiftValid = shift === event.shiftKey;\n if (altValid && ctrlValid && shiftValid) {\n specificKeyPress(keyCode, key)(fn)(event);\n }\n };\n\n/** Fires if specific key is pressed with no combo key */\nexport const soloKeyPress = comboKeyPress(false, false, false);\n/** Fires if specific key is pressed with combo key */\nexport function specificKeyPress(keyCode: number, key?: string) {\n return function (fn: (e?: React.KeyboardEvent | KeyboardEvent | MouseEvent) => void) {\n return function (event: React.KeyboardEvent | KeyboardEvent) {\n let keyPressed = key && key.toLowerCase() === event.key.toLowerCase();\n keyPressed = keyPressed || event.keyCode === keyCode;\n if (keyPressed) fn(event);\n };\n };\n}\n\n/** Allows optional definition for whether kicks off is ctrl key is pressed or whatnot */\nexport const optionalSoloKeyPress = (solo?: boolean) => (solo ? soloKeyPress : specificKeyPress);\n\n/** onEnter fn happens when enter key pressed */\nexport const enterKey = soloKeyPress(13, \"Enter\");\n/** onTab fn happens when tab key pressed */\nexport const tabKey = soloKeyPress(8, \"Tab\");\n/** onEnter fn happens when enter key pressed */\nexport const pgUpKey = soloKeyPress(33, \"PageUp\");\n/** onEnter fn happens when enter key pressed */\nexport const pgDownKey = soloKeyPress(34, \"PageDown\");\n\n/** Concatenates keyDown listeners */\nexport const keyListeners =\n (...listeners: ((event: React.KeyboardEvent | KeyboardEvent) => void)[]) =>\n (event: React.KeyboardEvent | KeyboardEvent) => {\n listeners.forEach((l) => l(event));\n };\n","/* eslint-disable no-console */\n\nexport enum Severity {\n Info,\n Warning,\n Error,\n}\n\nexport function logger(severity: Severity, message: unknown) {\n // TODO: In production log to external service, application insights?\n if (import.meta.env.PROD && severity !== Severity.Error) {\n return;\n }\n\n switch (severity) {\n case Severity.Info:\n console.log(message);\n break;\n case Severity.Warning:\n console.warn(message);\n break;\n case Severity.Error:\n console.error(message);\n break;\n default:\n console.log(message);\n }\n}\n","export class QueryKeys {\n public static VERIFICATIONS = \"Verifications\";\n public static VERIFICATION_EVENTS = \"VerificationEvents\";\n public static VERIFICATION_FILES = \"VerificationFiles\";\n public static REP = \"SingleRep\";\n public static REPS = \"Representatives\";\n public static USERS = \"Users\";\n public static USER_EVENTS = \"UserEvents\";\n public static FORMS = \"Forms\";\n public static FORM_TEMPLATES = \"FormTemplates\";\n public static CAMPAIGN = \"Campaign\";\n public static CAMPAIGNS = \"Campaigns\";\n public static CAMPAIGNS_FILTER = \"CampaignsFilter\";\n public static CHANNELS_FILTER = \"ChannelsFilter\";\n public static CLIENTS_FILTER = \"ClientsFilter\";\n public static LANGUAGES_FILTER = \"LanguagesFilter\";\n public static STATES_FILTER = \"StatesFilter\";\n public static VER_MODES_FILTER = \"VerificationModesFilter\";\n public static VER_TYPES_FILTER = \"VerificationTypesFilter\";\n public static DISTRICTS_FILTER = \"DistrictsFilter\";\n public static TITLE = \"Title\";\n public static BASE_URL = \"BaseUrl\";\n public static TEMP_TOKEN = \"TempToken\";\n public static ALERTS = \"Alerts\";\n public static PRINT_FORM_BATCH_FILE_URL = \"PrintFormBatchFileUrl\";\n public static USERS_TOGGLE_MUTATION = \"UsersToggleMutation\";\n public static REPS_TOGGLE_MUTATION = \"RepresentativeToggleMutation\";\n public static USER = \"User\";\n public static AGENT_LEADERBOARD = \"AgentLeaderboard\";\n public static SALES_OUTCOMES = \"SalesOutcomes\";\n public static VERIFICATION_POSITIONS = \"VerificationPositions\";\n public static REP_POSITIONS = \"RepPositions\";\n public static VERIFICATION_HEATMAP = \"VerificationHeatmap\";\n public static NO_SALE_DISPOSITIONS = \"NoSaleDispositions\";\n public static TOPOLOGY = \"Topology\";\n public static PREVIOUS_SCAN = \"PreviousScan\";\n public static PREVIOUS_AUDIT = \"PreviousAudit\";\n public static DISPOSITIONS = \"Dispositions\";\n public static TPV_DISPOSITIONS = \"TPVDispositions\";\n public static AUDIT_BATCH_SUMMARY = \"AuditBatchSummary\";\n public static CHECK_IN = \"CheckIn\";\n public static AUDIT = \"Audit\";\n public static CHECK_IN_BATCH_SUMMARY = \"CheckInBatchSummary\";\n public static CAMPAIGN_REPORTS = \"CampaignReports\";\n public static CAMPAIGN_GOALS = \"CampaignGoals\";\n public static GET_FORM_TEMPLATE = \"GetFormTemplate\";\n public static PERMISSIONS = \"Permissions\";\n public static FORM_TEMPLATE = \"FormTemplate\";\n public static VERSION = \"Version\";\n public static AUDIT_QUESTIONS = \"AuditQuestions\";\n public static GENERATED_REPORTS = \"GeneratedReports\";\n public static SETTINGS_MAP = \"MapSettings\";\n public static RESPONSE_COUNTS = \"SelfGeneratedResponseCounts\";\n public static FORGOT_PASSWORD = \"ForgotPassword\";\n}\n","export class RouteValues {\n public static HOME = \"\";\n public static LOGIN = \"login\";\n public static FORGOT_PASSWORD = \"forgot-password\";\n public static CHANGE_PASSWORD = \"change-password\";\n public static REPS = \"reps\";\n public static USERS = \"users\";\n public static FORMS = \"forms\";\n public static CAMPAIGNS = \"campaigns\";\n public static CHECK_IN = \"check-in\";\n public static AUDIT = \"audit\";\n public static REALTIME = \"realtime\";\n public static HISTORICAL = \"historical\";\n public static GENERATED = \"generated\";\n public static SETTINGS = \"settings\";\n public static REPORTS = \"reports\";\n}\n","import { useMediaQuery } from \"react-responsive\";\n// This will use the default theme from tailwindcss. If we update the breakpoints in tailwind.config.js, we will need to update this file as well.\nimport defaultTheme from \"tailwindcss/defaultTheme\";\n\nconst breakpoints = defaultTheme.screens;\n\ntype BreakpointKey = keyof typeof breakpoints;\n\nexport function useBreakpoint(breakpointKey: K) {\n const bool = !useMediaQuery({\n query: `(min-width: ${breakpoints[breakpointKey]})`,\n });\n const capitalizedKey = `${breakpointKey?.at(0)?.toUpperCase() ?? \"\"}${breakpointKey.substring(1)}`;\n type Key = `is${Capitalize}`;\n return {\n [`is${capitalizedKey}`]: bool,\n } as Record;\n}\n","import { type FilterDto } from \"../models\";\nimport { doGet, doPost } from \"./httpHandler\";\n\nconst controller = \"/api/gridfilters\";\n\nexport class GridFiltersApi {\n static async getFunction(\n action: string,\n all?: boolean,\n ids?: number[],\n ): Promise<{ value: number; label: string }[]> {\n const id = ids ? ids.join(\",\") : undefined;\n const filters = await doGet(controller, action, { all, id });\n\n return filters.map((item) => ({ value: item.id, label: item.name }));\n }\n\n public static getClients = () => GridFiltersApi.getFunction(\"GetClients\");\n public static getChannels = () => GridFiltersApi.getFunction(\"GetChannels\");\n public static getStates = (all?: boolean) => GridFiltersApi.getFunction(\"GetUSStates\", all);\n public static getStateAbbreviations = async (all?: boolean) => {\n const filters = await doGet(controller, \"GetUSStates\", { all });\n\n return filters.map((item) => ({ value: item.id, label: item.code }));\n };\n public static getVerTypes = () => GridFiltersApi.getFunction(\"GetVerTypes\");\n public static getVerModes = () => GridFiltersApi.getFunction(\"GetVerModes\");\n public static getLanguages = () => GridFiltersApi.getFunction(\"GetLanguages\");\n public static getCampaigns = () => GridFiltersApi.getFunction(\"GetCampaigns\");\n public static getCampaignsBySegment = async (\n clientId: number | undefined,\n channelId: number | undefined,\n stateId: number | undefined,\n ) => {\n const filters = await doGet(controller, \"GetCampaignsBySegment\", { clientId, channelId, stateId });\n return filters.map((item) => ({ value: item.id, label: item.name }));\n };\n public static getDistricts = () => GridFiltersApi.getFunction(\"GetDistricts\");\n public static getFormTemplates = (id: number) => GridFiltersApi.getFunction(\"GetFormTemplates\", undefined, [id]);\n public static getRepresentatives = async (ids?: number[]) => {\n const body = {\n campaigns: ids ?? [],\n };\n\n const filters = await doPost(\n controller,\n \"GetRepresentatives\",\n undefined,\n body,\n );\n\n return filters.map((item) => ({ value: item.id, label: item.name }));\n };\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { type FC, type ReactNode, createContext, useCallback, useState } from \"react\";\n\nimport { GridFiltersApi } from \"../api/gridFiltersApi\";\nimport { QueryKeys, checkPermission } from \"../helpers\";\nimport { type UserDto } from \"../models\";\nimport { type UserPermissionFlags } from \"../models/permissions\";\n\ninterface UserContextType {\n user?: UserDto;\n setUser: (user?: UserDto) => void;\n hasPermission: (permission: UserPermissionFlags) => boolean;\n isVoterFi: boolean;\n}\n\nexport const UserContext = createContext({\n user: undefined,\n setUser: () => undefined,\n hasPermission: () => false,\n isVoterFi: false,\n});\n\ninterface UserProviderProps {\n children: ReactNode;\n}\n\nexport const UserProvider: FC = ({ children }) => {\n const [user, setUser] = useState(undefined);\n\n const campaignQuery = useQuery({\n queryKey: [QueryKeys.CAMPAIGNS_FILTER],\n queryFn: () => GridFiltersApi.getCampaigns(),\n enabled: !!user,\n });\n const isVoterFi = !!user?.channelId && !!campaignQuery.data?.length;\n const hasPermission = useCallback((permission: UserPermissionFlags) => checkPermission(user, permission), [user]);\n\n return {children};\n};\n","import { useContext, useEffect } from \"react\";\nimport { useNavigate } from \"react-router-dom\";\n\nimport { UserContext } from \"../contexts/userContext\";\nimport { type UserPermissionFlags } from \"../models/permissions\";\nimport { RouteValues } from \"./routes\";\n\nexport const useRoutePermissions = (flag: UserPermissionFlags) => {\n const { hasPermission, user } = useContext(UserContext);\n const navigate = useNavigate();\n\n useEffect(() => {\n if (!user) return;\n if (!hasPermission(flag)) {\n navigate(`/${RouteValues.HOME}`);\n }\n }, [flag, hasPermission, navigate, user]);\n};\n","import { useQuery } from \"@tanstack/react-query\";\nimport { type FC, useMemo } from \"react\";\n\nimport { SystemIncidentsApi } from \"../../../api/systemIncidentsApi\";\nimport { QueryKeys } from \"../../../helpers\";\nimport { type SystemIncidentDto } from \"../../../models\";\nimport style from \"./style.module.scss\";\n\nexport function AlertsBanner() {\n const alertsQuery = useQuery({ queryKey: [QueryKeys.ALERTS], queryFn: SystemIncidentsApi.getAlerts });\n\n if (alertsQuery.isLoading) return null;\n\n if ((alertsQuery.data?.length ?? 0) < 1) return null;\n\n return (\n
\n \n
\n );\n}\n\nconst Alerts: FC<{ alerts?: SystemIncidentDto[] }> = (props) => {\n return (\n
\n {props.alerts?.map((alert, index) => )}\n
\n );\n};\n\nconst SingleAlert: FC<{ alert: SystemIncidentDto }> = (props) => {\n const { alert } = props;\n const msg = useMemo(\n () => [alert.started, alert.status, alert.type, alert.impact, alert.description, alert.carrier].join(\" | \"),\n [alert],\n );\n return {msg};;\n};\n","import { type FC } from \"react\";\n\nimport { styles } from \"../../../helpers\";\nimport { type Props } from \"./types\";\n\nexport const Spinner: FC = ({ right, children, ...props }) => {\n return (\n
\n {!right ? : null}\n {children}\n {right ? : null}\n
\n );\n};\n\nconst SpinnerIcon: FC = ({ className, svgClassName }) => {\n return (\n
\n
\n \n \n \n \n Loading...\n
\n
\n );\n};\n","import { Slot } from \"@radix-ui/react-slot\";\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport { type ButtonHTMLAttributes, type MouseEvent, forwardRef } from \"react\";\n\nimport { styles } from \"../../../helpers\";\nimport { Spinner } from \"../spinner\";\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\",\n {\n variants: {\n variant: {\n default: \"bg-indigo-600 text-white hover:bg-indigo-700 active:bg-indigo-800 shadow\",\n destructive: \"bg-rose-600 text-white hover:bg-rose-700 active:bg-rose-800\",\n success: \"bg-green-600 text-white hover:bg-green-700 active:bg-green-800\",\n warning: \"bg-amber-600 text-white hover:bg-amber-700 active:bg-amber-800\",\n outline:\n \"border border-indigo-600 dark:border-indigo-300 dark:text-indigo-300 text-indigo-600 bg-transparent hover:bg-indigo-100 active:bg-indigo-200 dark:hover:text-indigo-600 dark:active:text-indigo-600\",\n secondary:\n \"dark:bg-white dark:hover:bg-indigo-100 dark:active:bg-indigo-200 bg-indigo-50 text-indigo-600 hover:bg-indigo-100 active:bg-indigo-200 shadow disabled:text-indigo-600\",\n ghost: \"hover:bg-gray-300 dark:hover:bg-gray-600 active:bg-gray-400 dark:active:bg-gray-500\",\n link: \"dark:text-white underline-offset-4 underline hover:text-indigo-600 active:text-indigo-700 dark:hover:text-indigo-200 dark:active:text-indigo-300\",\n },\n size: {\n default: \"h-10 px-4 py-2\",\n sm: \"h-9 rounded-md px-3\",\n lg: \"h-11 rounded-md px-8\",\n icon: \"h-10 w-10\",\n barcode: \"h-40 p-4\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nexport interface ButtonProps extends ButtonHTMLAttributes, VariantProps {\n asChild?: boolean;\n isLoading?: boolean;\n}\n\nconst Button = forwardRef(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const onClick = (e: MouseEvent) => {\n if (props.isLoading) return;\n if (props.onClick) return props.onClick(e);\n };\n\n const Comp = asChild ? Slot : \"button\";\n\n return (\n \n <>\n {props.isLoading && (\n \n )}\n {props.children}\n \n \n );\n },\n);\n\nButton.displayName = \"Button\";\n\nexport { Button, buttonVariants };\n","import { ChevronLeftIcon, ChevronRightIcon } from \"@heroicons/react/24/outline\";\nimport { type ComponentProps } from \"react\";\nimport { DayPicker } from \"react-day-picker\";\n\nimport { styles } from \"../../../helpers\";\nimport { buttonVariants } from \"../button\";\n\nexport type CalendarProps = ComponentProps;\n\nfunction Calendar({ className, classNames, showOutsideDays = true, ...props }: CalendarProps) {\n return (\n ,\n IconRight: () => ,\n }}\n {...props}\n />\n );\n}\n\nCalendar.displayName = \"Calendar\";\n\nexport { Calendar };\n","import { doGet } from \"./httpHandler\";\n\ninterface MapSettings {\n apiKey: string;\n}\n\nconst controller = \"/api/Settings\";\n\nexport const SettingsApi = {\n getMapSettings: () => doGet(controller, \"Map\"),\n};\n","import RCSlider from \"rc-slider\";\nimport { type FC } from \"react\";\n\nimport \"./slider.scss\";\n\ninterface SliderProps {\n className?: string;\n marks?: Record;\n min?: number;\n max?: number;\n defaultValue?: number[];\n value?: number[];\n onChange?: (value: number[]) => void;\n step?: number;\n draggableTrack?: boolean;\n}\n\nexport const Slider: FC = ({\n className,\n marks,\n min,\n max,\n defaultValue,\n value,\n onChange,\n step,\n draggableTrack,\n}) => {\n return (\n
\n \n
\n );\n};\n","import { type FC } from \"react\";\n\ninterface NoDataProps {\n title: string;\n noCard?: boolean;\n}\n\nexport const NoData: FC = ({ title, noCard }) => {\n return (\n
\n

{title}

\n
\n No data available for chosen timeframe/filters\n
\n
\n );\n};\n","// 38, -96 is around the center of the continental US\nimport type { MapPinType, ReportingLocation, UserDto } from \"../../../../models\";\n\nexport function findCenterCoordinate(coords: number[][]) {\n if (!coords || !coords.length) {\n return { lat: 38, lng: -96 };\n }\n\n const filtered = coords.filter((item) => item[0] && item[1]);\n\n const x = filtered.map((item) => item[0] as number);\n const y = filtered.map((item) => item[1] as number);\n\n const minX = Math.min(...x);\n const maxX = Math.max(...x);\n const minY = Math.min(...y);\n const maxY = Math.max(...y);\n\n if (minX === Infinity || maxX === -Infinity || minY === Infinity || maxY === -Infinity) {\n return { lat: 38, lng: -96 };\n }\n\n return {\n lat: (minX + maxX) / 2,\n lng: (minY + maxY) / 2,\n bounds: {\n north: maxY,\n south: minY,\n east: maxX,\n west: minX,\n },\n };\n}\n\nexport function getSegment(user: UserDto | undefined, item: ReportingLocation | undefined) {\n if (!user) return \"\";\n const segments: (string | undefined)[] = [];\n\n if (!user.clientId) segments.push(item?.client);\n if (!user.channelId) segments.push(item?.channel);\n if (!user.stateId) segments.push(item?.state);\n\n return segments.filter(Boolean).join(\"-\");\n}\n\nexport function typeToImage(type: MapPinType, disposition?: string) {\n if (type === \"userPin\") return \"/images/user.png\";\n\n if (!disposition) return \"/images/flag-yellow.png\";\n else if (disposition === \"Good Sale\") return \"/images/flag-green.png\";\n else return \"/images/flag-red.png\";\n}\n","import \"@googlemaps/js-api-loader\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport GoogleMapReact from \"google-map-react\";\nimport { type FC, useContext, useEffect, useMemo, useState } from \"react\";\n\nimport { SettingsApi } from \"../../../../api/settingsApi\";\nimport { UserContext } from \"../../../../contexts/userContext\";\nimport {\n QueryKeys,\n Severity,\n formatShortDateTimeOf,\n intToTime,\n logger,\n splitWordsOnUpperCase,\n} from \"../../../../helpers\";\nimport { type MapPinType, type ReportingLocation } from \"../../../../models\";\nimport { Slider } from \"../../slider\";\nimport { Spinner } from \"../../spinner\";\nimport { NoData } from \"../noData\";\nimport \"./index.css\";\nimport { findCenterCoordinate, getSegment, typeToImage } from \"./utils\";\n\ninterface TimeSliderSettings {\n enabled: boolean;\n min: number;\n max: number;\n}\n\ninterface MapChartProps {\n id: string;\n title: string;\n data: ReportingLocation[];\n type: MapPinType;\n keyFunc: (item: ReportingLocation) => any;\n realtime?: boolean;\n timeSlider?: TimeSliderSettings;\n}\n\nconst marks = {\n 0: intToTime(0),\n 6: intToTime(6),\n 12: intToTime(12),\n 18: intToTime(18),\n 24: intToTime(24),\n};\n\nexport const MapChart: FC = ({ id, title, data, type, realtime, keyFunc, timeSlider }) => {\n const { user } = useContext(UserContext);\n const [mapRef, setMapRef] = useState(null);\n const [displayMarkers, setDisplayMarkers] = useState(false);\n const [markers, setMarkers] = useState([]);\n const [sliderValue, setSliderValue] = useState([0, 24]);\n\n useEffect(() => {\n if (timeSlider?.enabled) {\n setSliderValue([timeSlider.min, timeSlider.max]);\n }\n }, [timeSlider]);\n\n const filteredData = useMemo(() => {\n return data\n .filter((item) => item.datetime)\n .filter((item) => item.lat && item.lon)\n .filter((item) => item.datetime.getHours() >= (sliderValue?.at(0) ?? 0))\n .filter((item) => item.datetime.getHours() <= (sliderValue?.at(1) ?? 24));\n }, [data, sliderValue]);\n\n const mapSettings = useQuery({\n queryKey: [QueryKeys.SETTINGS_MAP],\n queryFn: SettingsApi.getMapSettings,\n });\n\n const center = useMemo(() => {\n return findCenterCoordinate(data.map((item) => [item.lat, item.lon]));\n }, [data]);\n\n const bounds: google.maps.LatLngBounds | undefined = useMemo(() => {\n if (!displayMarkers) return undefined;\n if (!data) return undefined;\n if (!window.google?.maps) return undefined;\n\n const filtered = data.filter((item) => item.lat && item.lon);\n\n const b = new window.google.maps.LatLngBounds();\n filtered.forEach((item) => {\n b.extend(new window.google.maps.LatLng(item.lat, item.lon));\n });\n return b;\n }, [data, displayMarkers]);\n\n const onGoogleApiLoaded = ({ map }: { map: google.maps.Map }) => {\n setMapRef(map);\n setDisplayMarkers(true);\n };\n\n const heatmapData = useMemo(() => {\n return {\n positions: filteredData.map((item) => ({ lat: item.lat, lng: item.lon, weight: 100 })),\n options: {},\n };\n }, [filteredData]);\n\n useEffect(() => {\n if (!mapRef) return;\n if (!bounds) return;\n\n mapRef.fitBounds(bounds);\n }, [bounds, mapRef]);\n\n useEffect(() => {\n if (!displayMarkers) return;\n if (!filteredData) return;\n if (type === \"heatmap\") return;\n\n async function initMap() {\n const { InfoWindow } = (await google.maps.importLibrary(\"maps\")) as google.maps.MapsLibrary;\n const { AdvancedMarkerElement } = (await google.maps.importLibrary(\"marker\")) as google.maps.MarkerLibrary;\n\n const infoWindow = new InfoWindow();\n let existingMarkerMap: Map | null;\n\n if (realtime) {\n existingMarkerMap = new Map(markers.map((marker) => [(marker as any).key, marker]));\n } else {\n // Clear markers before adding new ones\n markers.forEach((marker) => (marker.map = null));\n setMarkers([]);\n }\n\n filteredData.forEach((item) => {\n const key = keyFunc(item);\n\n if (realtime) {\n const existingMarker = existingMarkerMap?.get(key);\n\n // Check if marker exists and needs color update\n if (existingMarker) {\n const currentImage = (existingMarker.content as HTMLImageElement).src;\n const newImage = typeToImage(type, item.disposition);\n\n if (currentImage !== newImage) {\n const flagImage = existingMarker.content as HTMLImageElement;\n flagImage.src = newImage;\n }\n\n // Update position if changed\n const position = existingMarker.position as google.maps.LatLng;\n // @ts-ignore\n if (position.lat !== item.lat || position.lng !== item.lon) {\n existingMarker.position = { lat: item.lat, lng: item.lon };\n }\n\n existingMarkerMap?.delete(key); // Remove from map to track which ones are new //JES: I don't understand why?\n return;\n }\n }\n\n // Create new marker\n const flagImage = document.createElement(\"img\");\n flagImage.src = typeToImage(type, item.disposition);\n flagImage.width = 32;\n flagImage.height = 32;\n\n const marker = new AdvancedMarkerElement({\n position: { lat: item.lat, lng: item.lon },\n map: mapRef,\n content: flagImage,\n gmpClickable: true,\n });\n\n // Store key for future reference\n (marker as any).key = key;\n\n // Add animation for new markers\n const content = marker.content as HTMLElement;\n content.style.opacity = \"0\";\n content.classList.add(\"drop\");\n content.addEventListener(\"animationend\", () => {\n content.classList.remove(\"drop\");\n content.style.opacity = \"1\";\n });\n\n // Add marker click handler (rest of the click handler code remains the same)\n marker.addListener(\"click\", () => {\n const currentMapTypeId = mapRef?.getMapTypeId();\n const currentBounds = mapRef?.getBounds();\n\n mapRef?.setMapTypeId(\"satellite\");\n mapRef?.setCenter({ lat: item.lat, lng: item.lon });\n mapRef?.setZoom(19);\n\n const repInfo = item.repName ? `(${item.repName})` : \"\";\n const segment = getSegment(user, item);\n\n infoWindow.close();\n infoWindow.setContent(\n `
\n
\n
Rep:
\n
${item.repId} ${repInfo}
\n
TPV:
\n
${item.tpvId} ${segment ? `(${segment})` : \"\"}
\n
Date:
\n
${formatShortDateTimeOf(item.datetime) ?? \"\"}
\n
Status:
\n
${splitWordsOnUpperCase(item.status ?? \"\")}
\n
Disposition:
\n
${item.disposition ?? \"\"}
\n
\n \n
`,\n );\n infoWindow.open(mapRef, marker);\n\n infoWindow.addListener(\"closeclick\", () => {\n mapRef?.setMapTypeId(currentMapTypeId ?? \"roadmap\");\n if (currentBounds) {\n mapRef?.fitBounds(currentBounds);\n } else {\n mapRef?.setCenter({ lat: item.lat, lng: item.lon });\n mapRef?.setZoom(3);\n }\n });\n\n infoWindow.addListener(\"domready\", () => {\n const openStreetViewButton = document.getElementById(\"openStreetView\");\n if (!openStreetViewButton) return;\n openStreetViewButton.addEventListener(\"click\", () => openStreetView(item.lat, item.lon));\n });\n\n function openStreetView(lat: number, lng: number) {\n if (!mapRef) return;\n\n const panorama: google.maps.StreetViewPanorama = mapRef?.getStreetView();\n\n panorama.setPosition({ lat, lng });\n panorama.setVisible(true);\n\n const size = new google.maps.Size(96, 96);\n const image = {\n url: flagImage.src,\n size,\n scaledSize: size,\n } as google.maps.Icon;\n\n const marker = new google.maps.Marker({\n position: { lat, lng },\n title: \"\",\n icon: image,\n });\n\n marker.setMap(panorama);\n\n mapRef.setStreetView(panorama);\n\n panorama.addListener(\"closeclick\", () => {\n marker.setMap(null);\n });\n }\n });\n\n markers.push(marker);\n });\n\n setMarkers(markers);\n }\n\n initMap().catch((e) => logger(Severity.Error, e));\n }, [filteredData, keyFunc, type, user, bounds, realtime, markers, displayMarkers, mapRef]);\n\n if (mapSettings.isLoading) return ;\n\n if (mapSettings.error || mapSettings.data?.apiKey === \"\") return ;\n\n if (data.length === 0) return ;\n\n return (\n
\n

{title}

\n {timeSlider?.enabled ? (\n \n ) : null}\n
\n \n
\n
\n );\n};\n","import * as Highcharts from \"highcharts\";\nimport HighchartsReact from \"highcharts-react-official\";\nimport \"highcharts/modules/accessibility\";\nimport { type FC, useEffect, useRef, useState } from \"react\";\n\nimport \"./bar.scss\";\nimport \"./charts.scss\";\nimport { NoData } from \"./noData\";\nimport { type DataItem } from \"./types\";\n\ninterface BarChartProps extends HighchartsReact.Props {\n id: string;\n title: string;\n data: { name: string; data: DataItem[] }[];\n}\n\nexport const BarChart: FC = (props) => {\n const chartComponentRef = useRef(null);\n\n const [options, setOptions] = useState();\n\n useEffect(() => {\n if (props.data) {\n setOptions({\n chart: {\n styledMode: true,\n },\n credits: {\n enabled: false,\n },\n title: {\n text: props.title,\n align: \"left\",\n },\n accessibility: {\n point: {\n valueSuffix: \"%\",\n },\n },\n plotOptions: {\n bar: {\n allowPointSelect: true,\n cursor: \"pointer\",\n dataLabels: {\n enabled: false,\n },\n showInLegend: true,\n },\n },\n xAxis: {\n visible: false,\n },\n tooltip: {\n formatter: function () {\n return `${this.name}: ${this.y ?? \"0\"}`;\n },\n },\n series: props.data.map((item) => ({\n type: \"column\",\n name: item.name,\n data: item.data,\n accessibility: {\n enabled: true,\n },\n })),\n });\n }\n }, [props.data, props.title]);\n\n if (!options) return null;\n\n if (props.data?.length === 0) return ;\n\n return (\n
\n \n
\n );\n};\n","import * as Highcharts from \"highcharts\";\nimport HighchartsReact from \"highcharts-react-official\";\nimport \"highcharts/modules/accessibility\";\nimport { type FC, useEffect, useRef, useState } from \"react\";\n\nimport \"./charts.scss\";\nimport { NoData } from \"./noData\";\n\ninterface AreaChartProps extends HighchartsReact.Props {\n id: string;\n title: string;\n axis: string[];\n columns: { name: string; data: number[] }[];\n areas: { name: string; data: number[] }[];\n spline?: { name: string; data: number[] };\n noCard?: boolean;\n}\n\nexport const ComboChart: FC = ({ id, title, axis, columns, areas, spline, noCard }) => {\n const chartComponentRef = useRef(null);\n\n const [options, setOptions] = useState();\n\n useEffect(() => {\n if (columns && spline && areas) {\n setOptions({\n chart: {\n type: \"area\",\n styledMode: true,\n },\n credits: {\n enabled: false,\n },\n title: {\n text: title,\n align: \"left\",\n },\n accessibility: {\n point: {\n valueSuffix: \"%\",\n },\n },\n yAxis: {\n title: {\n text: \"Counts\",\n },\n },\n xAxis: {\n categories: axis,\n },\n tooltip: {\n formatter: function () {\n return `${this.series.name}: ${this.y ?? \"0\"}`;\n },\n },\n plotOptions: {\n area: {\n stacking: \"normal\",\n marker: {\n enabled: false,\n symbol: \"circle\",\n radius: 2,\n states: {\n hover: {\n enabled: true,\n },\n },\n },\n },\n },\n series: [\n ...columns.map(\n (item) =>\n ({\n type: \"column\",\n name: item.name,\n data: item.data,\n accessibility: {\n enabled: true,\n },\n }) as Highcharts.SeriesOptionsType,\n ),\n ...areas.map(\n (item) =>\n ({\n type: \"area\",\n name: item.name,\n data: item.data,\n accessibility: {\n enabled: true,\n },\n }) as Highcharts.SeriesOptionsType,\n ),\n {\n type: \"spline\",\n name: spline.name,\n data: spline.data,\n accessibility: {\n enabled: true,\n },\n },\n ],\n });\n }\n }, [areas, axis, columns, spline, title]);\n\n if (!options) return null;\n\n if (spline?.data.length === 0) return ;\n\n return (\n
\n \n
\n );\n};\n","import * as Highcharts from \"highcharts\";\nimport HighchartsReact from \"highcharts-react-official\";\nimport \"highcharts/modules/accessibility\";\nimport \"highcharts/modules/map\";\nimport { type FC, useEffect, useMemo, useRef, useState } from \"react\";\n\nimport { MAX_INT, first, last } from \"../../../helpers\";\nimport \"./charts.scss\";\nimport { NoData } from \"./noData\";\n\nexport interface HeatmapData {\n date: Date;\n time: number;\n z: number;\n}\n\ninterface HeatmapChartProps extends HighchartsReact.Props {\n id: string;\n title: string;\n data: HeatmapData[];\n}\n\nfunction toTimestamp(d: Date) {\n return Date.UTC(d.getFullYear(), d.getMonth(), d.getDate());\n}\n\nexport const HeatmapChart: FC = (props) => {\n const chartComponentRef = useRef(null);\n\n const [options, setOptions] = useState();\n\n const [min, max, ticks] = useMemo(() => {\n if (!props.data || props.data.length === 0) return [0, 23, [0, 5, 11, 17, 23]];\n\n let newMin = MAX_INT;\n let newMax = 0;\n const newTicks = [];\n\n props.data.forEach((d) => {\n if (d.time < newMin) newMin = d.time;\n if (d.time > newMax) newMax = d.time;\n });\n\n const tickInterval = Math.floor((newMax - newMin) / 5);\n\n if (tickInterval === 0) return [newMin, newMax, [newMin, newMax]];\n\n for (let i = newMin; i <= newMax; i += tickInterval) {\n newTicks.push(i);\n }\n\n return [newMin, newMax, newTicks];\n }, [props.data]);\n\n useEffect(() => {\n if (!props.data || props.data.length === 0) return;\n\n setOptions({\n chart: {\n inverted: true,\n styledMode: true,\n },\n credits: {\n enabled: false,\n },\n title: {\n text: props.title,\n align: \"left\",\n },\n tooltip: {\n formatter: function () {\n return `TPVs: ${this.value ?? \"0\"}`;\n },\n },\n xAxis: {\n min: toTimestamp(first(props.data)?.date ?? new Date()),\n max: toTimestamp(last(props.data)?.date ?? new Date()),\n labels: {\n format: \"{value:%B / %d}\", // long month\n },\n },\n yAxis: {\n accessibility: {\n description: \"Hours in the day\",\n },\n title: {\n text: null,\n },\n labels: {\n format: \"{value}:00\",\n },\n lineWidth: 1,\n minPadding: 0,\n maxPadding: 0,\n startOnTick: false,\n endOnTick: false,\n tickPositions: ticks,\n tickWidth: 1,\n min,\n max,\n },\n colorAxis: {\n stops: [\n [0, \"#3b82f6\"],\n [0.5, \"#d946ef\"],\n [0.9, \"#14b8a6\"],\n ],\n min: 0,\n },\n series: [\n {\n type: \"heatmap\",\n name: \"TPVs\",\n data: props.data.map((d) => [Date.parse(d.date.toDateString()), d.time, d.z]),\n borderWidth: 0,\n colsize: 24 * 36e5, // one day\n accessibility: {\n enabled: true,\n },\n },\n ],\n });\n }, [max, min, props.data, props.title, ticks]);\n\n if (!options) return null;\n\n if (props.data?.length === 0) return ;\n\n return (\n
\n \n
\n );\n};\n","import * as Highcharts from \"highcharts\";\nimport HighchartsReact from \"highcharts-react-official\";\nimport \"highcharts/modules/accessibility\";\nimport { type FC, useEffect, useRef, useState } from \"react\";\n\nimport { useBreakpoint } from \"../../../helpers\";\nimport \"./charts.scss\";\nimport { NoData } from \"./noData\";\nimport \"./pie.scss\";\nimport { type DataItem } from \"./types\";\n\ninterface PieChartProps extends HighchartsReact.Props {\n id: string;\n title: string;\n data: DataItem[];\n}\n\nexport const PieChart: FC = (props) => {\n const chartComponentRef = useRef(null);\n const [options, setOptions] = useState();\n const { isSm } = useBreakpoint(\"sm\");\n\n useEffect(() => {\n if (!props.data || props.data.length === 0) return;\n\n setOptions({\n chart: {\n styledMode: true,\n },\n credits: {\n enabled: false,\n },\n title: {\n text: props.title,\n align: \"left\",\n },\n accessibility: {\n point: {\n valueSuffix: \"%\",\n },\n },\n plotOptions: {\n pie: {\n allowPointSelect: true,\n cursor: \"pointer\",\n dataLabels: {\n enabled: false,\n },\n showInLegend: true,\n },\n },\n tooltip: {\n enabled: false,\n headerFormat: \"\",\n pointFormat: \"\",\n },\n legend: {\n enabled: isSm,\n labelFormat: \"{name}: {y} ({percentage:.1f}%)\",\n },\n series: [\n {\n type: \"pie\",\n data: props.data,\n dataLabels: {\n enabled: !isSm,\n format: \"{point.name}: {point.y} ({point.percentage:.1f}%)\",\n },\n accessibility: {\n point: {\n valueDescriptionFormat: \"{point.name}\",\n },\n enabled: true,\n },\n },\n ],\n });\n }, [isSm, props.data, props.title]);\n\n if (!options) return null;\n\n if (props.data[0]?.y === 0 && props.data[1]?.y === 0 && props.data[2]?.y === 0)\n return ;\n\n return (\n
\n \n
\n );\n};\n","import * as Highcharts from \"highcharts\";\nimport HighchartsReact from \"highcharts-react-official\";\nimport \"highcharts/modules/accessibility\";\nimport { type FC, useEffect, useRef, useState } from \"react\";\n\nimport \"./charts.scss\";\nimport { NoData } from \"./noData\";\n\ninterface SplineChartProps extends HighchartsReact.Props {\n id: string;\n title: string;\n axis: string[];\n data: { name: string; data: number[] }[];\n}\n\nexport const SplineChart: FC = (props) => {\n const chartComponentRef = useRef(null);\n\n const [options, setOptions] = useState();\n\n useEffect(() => {\n if (props.data) {\n setOptions({\n chart: {\n styledMode: true,\n },\n credits: {\n enabled: false,\n },\n title: {\n text: props.title,\n align: \"left\",\n },\n accessibility: {\n point: {\n valueSuffix: \"%\",\n },\n },\n xAxis: {\n categories: props.axis,\n },\n yAxis: {\n labels: {\n format: \"{value}%\",\n },\n title: {\n text: \"Percent\",\n },\n },\n tooltip: {\n formatter: function () {\n return `${this.series.name}: ${this.y?.toFixed(0) ?? \"0\"}%`;\n },\n },\n plotOptions: {\n area: {\n marker: {\n enabled: false,\n symbol: \"circle\",\n radius: 2,\n states: {\n hover: {\n enabled: true,\n },\n },\n },\n },\n },\n series: props.data.map((item) => ({\n type: \"spline\",\n name: item.name,\n data: item.data,\n accessibility: {\n enabled: true,\n },\n tooltip: {\n valueSuffix: \"%\",\n },\n })),\n });\n }\n }, [props.axis, props.data, props.title]);\n\n if (!options) return null;\n\n if (props.data.length === 0) return ;\n\n return (\n
\n \n
\n );\n};\n","import * as Highcharts from \"highcharts\";\nimport HighchartsReact from \"highcharts-react-official\";\nimport \"highcharts/modules/accessibility\";\nimport { type FC, useEffect, useRef, useState } from \"react\";\n\nimport \"./charts.scss\";\nimport { NoData } from \"./noData\";\n\ninterface StackedBarChartProps extends HighchartsReact.Props {\n id: string;\n title: string;\n axis: string[];\n data: { name: string; data: number[] }[];\n}\n\nexport const StackedBarChart: FC = (props) => {\n const chartComponentRef = useRef(null);\n\n const [options, setOptions] = useState();\n\n useEffect(() => {\n if (props.data) {\n setOptions({\n chart: {\n styledMode: true,\n },\n credits: {\n enabled: false,\n },\n title: {\n text: props.title,\n align: \"left\",\n },\n plotOptions: {\n series: {\n allowPointSelect: true,\n cursor: \"pointer\",\n stacking: \"normal\",\n dataLabels: {\n enabled: false,\n },\n showInLegend: true,\n },\n },\n tooltip: {\n formatter: function () {\n return `${this.series.name}: ${this.y ?? \"0\"}`;\n },\n },\n xAxis: {\n categories: props.axis,\n },\n series: props.data.map((item) => ({\n type: \"column\",\n name: item.name,\n data: item.data,\n accessibility: {\n enabled: true,\n },\n })),\n });\n }\n }, [props.axis, props.data, props.title]);\n\n if (!options) return null;\n\n if (props.data.length === 0) return ;\n\n return (\n
\n \n
\n );\n};\n","/* eslint-disable */\nimport { clone, hasValue } from \"../helpers\";\n\nexport type KeyFn = (obj: T) => string | number;\n\n/**\n * Essentially a sorted dictionary. Has most functionality of Array, plus O[1] lookup\n * (at expense of slower initialization and sorting)\n */\nexport class KeyedArray {\n data: T[];\n keys: any = {};\n keyFn: KeyFn;\n\n constructor(data: T[], keyFn: KeyFn) {\n data = data || [];\n this.data = data.map((d) => d);\n this.keyFn = (o: T) => o && keyFn(o);\n this.data.forEach((o, i) => (this.keys[keyFn(o)] = i));\n }\n\n protected newArray(data: T[]) {\n return new KeyedArray(data, this.keyFn);\n }\n\n /** Adds to list, or updates if exists */\n set(obj: T) {\n const key = this.keyFn(obj);\n if (this.keys[key] === undefined) {\n this.keys[key] = this.data.length;\n this.data.push(obj);\n } else {\n this.data[this.keys[key]] = obj;\n }\n }\n\n remove(item: T) {\n return this.removeId(this.keyFn(item));\n }\n\n removeId(id: string | number) {\n if (this.get(id)) {\n const index = this.keys[id];\n delete this.keys[id];\n this.data.splice(index, 1);\n return true;\n }\n return false;\n }\n\n /** O[1] accessor (gets by id) */\n get(id: string | number) {\n return this.data[this.keys[id]];\n }\n\n /** O[1] accessor (gets by id) */\n has(id: string | number) {\n return hasValue(this.keys[id]);\n }\n\n sort(compareFn: (a: T, b: T) => number) {\n const sortedData = this.data.map((d) => d).sort(compareFn);\n return this.newArray(sortedData);\n }\n\n map(callbackfn: (value: T, index: number, array: T[]) => TOut) {\n return this.data.map(callbackfn);\n }\n\n filter(callbackfn: (value: T, index: number, array: T[]) => boolean) {\n return this.newArray(this.data.filter(callbackfn));\n }\n\n count() {\n return (this.data && this.data.length) || 0;\n }\n\n any() {\n return this.count() > 0;\n }\n\n /** Returns copy of array */\n copy() {\n return this.newArray(this.data.map(clone));\n }\n}\n\nexport class KeyPairArray extends KeyedArray> {\n constructor(data?: keyPair[]) {\n super(data || [], (k) => k.id);\n }\n\n values() {\n return this.map((o) => o.value);\n }\n\n setValue(id: string | number, value: TValue) {\n const pair = { id, value };\n super.set(pair);\n }\n\n getValue(id: string | number) {\n const pair = this.get(id);\n return pair && pair.value;\n }\n\n /** Returns copy of array */\n copy(): KeyPairArray {\n return super.copy() as KeyPairArray;\n }\n\n protected newArray(data: keyPair[]) {\n return new KeyPairArray(data);\n }\n}\n\nexport interface keyPair {\n id: string | number;\n value: TValue;\n}\n\nexport interface keyPairAll {\n id: string | number | boolean | undefined;\n value: TValue;\n}\n\nexport interface keyPairAny {\n id: any;\n value: TValue;\n}\n\n/**\n * Maps object (which should be a dictionary-like) to a key pair array\n * @param obj\n * @returns\n */\nexport function objToKeyPairs(obj: Object) {\n const kvps = Object.keys(obj).map((k) => {\n return { id: k, value: obj[k as keyof Object] };\n });\n return new KeyPairArray(kvps);\n}\n\nexport class KeyPairArrayGeneric extends KeyedArray<{ id: TId; value: TValue }> {\n constructor(data: { id: TId; value: TValue }[]) {\n super(data, (k) => k.id);\n }\n\n getValue(id: string | number) {\n const pair = this.get(id);\n return pair && pair.value;\n }\n\n protected newArray(data: { id: TId; value: TValue }[]) {\n return new KeyPairArrayGeneric(data);\n }\n\n /** Returns copy of array */\n copy() {\n return super.copy() as KeyPairArrayGeneric;\n }\n}\n\nconst ALL_KEY = \"_ALL_\";\nexport class KeyedSetArray extends KeyPairArray> {\n constructor(data: keyPair>[] = []) {\n super(data);\n }\n\n addElement(id: string | number, itemId: T) {\n //Clear before re-adding, in case was moved to different category\n for (const s of this.data) s.value.delete(itemId);\n\n const set = this.get(id)?.value ?? new Set();\n set.add(itemId);\n super.setValue(id, set);\n\n const allSet = this.get(ALL_KEY)?.value ?? new Set();\n allSet.add(itemId);\n super.setValue(ALL_KEY, allSet);\n }\n\n getValue(id: string | number) {\n const val = super.getValue(id);\n return val ?? new Set();\n }\n\n getFullSet() {\n return this.getValue(ALL_KEY);\n }\n}\n","export enum SortType {\n none = 0,\n ascending = 1,\n descending = -1,\n}\n","import { type ChangeEvent, type FC, type HTMLProps, type LegacyRef, useCallback } from \"react\";\n\nimport { styles } from \"../../../helpers\";\n\ninterface CheckboxProps extends HTMLProps {\n label?: string;\n changeHandler?: (checked: boolean, id: string) => void;\n inline?: boolean;\n ref?: LegacyRef;\n}\n\nexport const Checkbox: FC = (props) => {\n const { label, changeHandler, inline, ...rest } = props;\n\n const handleChange = useCallback(\n (e: ChangeEvent) => changeHandler && changeHandler(e.target.checked, e.target.id),\n [changeHandler],\n );\n\n const labelEl = (\n \n );\n\n return (\n
\n {!inline && labelEl}\n \n {inline && labelEl}\n
\n );\n};\n","import { type FC, type HTMLProps, useEffect, useRef } from \"react\";\n\nimport { styles } from \"../../../helpers\";\n\ninterface CheckboxProps extends HTMLProps {\n label?: string;\n inline?: boolean;\n indeterminate?: boolean;\n}\n\nexport const IndeterminateCheckbox: FC = (props) => {\n const { label, inline, indeterminate, className, ...rest } = props;\n const ref = useRef(null);\n\n useEffect(() => {\n if (typeof indeterminate === \"boolean\" && ref.current) {\n ref.current.indeterminate = !rest.checked && indeterminate;\n }\n }, [ref, indeterminate, rest.checked]);\n\n return (\n
\n \n \n
\n );\n};\n","import { type FC } from \"react\";\n\nexport const TextSkeleton: FC = () => {\n return (\n <>\n
\n
\n loading\n
\n \n );\n};\n","import { type FC } from \"react\";\n\nimport { TextSkeleton } from \"./textSkeleton\";\n\nexport const ChartSkeleton: FC = () => {\n return (\n
\n \n
\n loading\n
\n );\n};\n","import { type FC } from \"react\";\n\nexport const GridSkeleton: FC = () => {\n return (\n <>\n
\n loading\n \n );\n};\n","import { type FC } from \"react\";\n\nexport const ImageSkeleton: FC = () => {\n return (\n <>\n
\n loading image\n \n );\n};\n","import { type FC } from \"react\";\n\nexport const InputSkeleton: FC = () => {\n return (\n <>\n
\n
\n
\n loading\n
\n \n );\n};\n","import { GridSkeleton } from \"../loading\";\nimport { type CustomTable } from \"./dataGrid\";\n\ninterface DataGridTableBodyProps {\n table: CustomTable;\n}\n\nexport function Loading({ table }: DataGridTableBodyProps) {\n return (\n \n {Array.from({ length: 10 }, (_, i) => i).map((i) => {\n return (\n \n {table.getHeaderGroups()[0]?.headers.map((header) => (\n \n \n \n ))}\n \n );\n })}\n \n );\n}\n","import { ChevronDownIcon, ChevronUpIcon } from \"@heroicons/react/20/solid\";\nimport { type Row, flexRender } from \"@tanstack/react-table\";\nimport { Fragment, type ReactNode } from \"react\";\n\nimport { type CustomTable } from \"./dataGrid\";\n\ninterface DataGridMobileProps {\n table: CustomTable;\n options?: (row: T) => ReactNode;\n rightOptions?: (row: T) => ReactNode;\n renderSubComponent?: (props: { row: Row }) => ReactNode;\n expandable?: boolean;\n}\n\nexport function DataGridMobile(props: DataGridMobileProps) {\n return (\n
\n {props.table.getRowModel().rows.map((row) => (\n
\n
\n
\n
{props.rightOptions && props.rightOptions(row.original)}
\n {row.getVisibleCells().map((cell) => (\n // eslint-disable-next-line @typescript-eslint/restrict-plus-operands\n \n \n {/* eslint-disable-next-line @typescript-eslint/no-unsafe-argument */}\n {flexRender(cell.column.columnDef.header, cell.getContext() as any)}\n \n \n {flexRender(cell.column.columnDef.cell, cell.getContext())}\n \n \n ))}\n
\n
{props.options && props.options(row.original)}
\n {row.getCanExpand() && props.expandable && props.renderSubComponent && (\n
\n {row.getIsExpanded() &&
{props.renderSubComponent({ row })}
}\n {getExpandButton(row)}\n
\n )}\n
\n
\n ))}\n
\n );\n function getExpandButton(row: Row) {\n return (\n
\n \n
\n );\n }\n}\n","import { useEffect, useState } from \"react\";\nimport ReactSelect, {\n type ClearIndicatorProps,\n type ControlProps,\n type DropdownIndicatorProps,\n type GroupBase,\n type GroupProps,\n type IndicatorsContainerProps,\n type InputProps,\n type MenuListProps,\n type MenuProps,\n type OptionProps,\n type Props,\n type SingleValueProps,\n type ValueContainerProps,\n components,\n} from \"react-select\";\nimport AsyncSelect from \"react-select/async\";\n\nimport { styles } from \"../../../helpers\";\n\nexport interface SelectOption {\n value: string | number;\n label: string | number;\n}\n\nexport interface SelectProps extends Props {\n label?: string;\n inline?: boolean;\n changeHandler?: (value?: any, name?: any) => void;\n textProp?: string;\n valueProp?: string;\n value?: SelectOption | number | string | SelectOption[] | number[] | string[];\n options?: SelectOption[];\n loadOptions?: (inputValue: string, callback: (options: SelectOption[]) => void) => Promise | void;\n}\n\nfunction Control = GroupBase>({\n children,\n ...props\n}: ControlProps) {\n const { isFocused, isDisabled } = props;\n\n return (\n \n {children}\n \n );\n}\n\nfunction ValueContainer = GroupBase>({\n children,\n ...props\n}: ValueContainerProps) {\n return (\n \n \n {children}\n \n \n );\n}\n\nfunction SingleValue = GroupBase>({\n children,\n ...props\n}: SingleValueProps) {\n return (\n \n {children}\n \n );\n}\n\nfunction Option = GroupBase>({\n children,\n ...props\n}: OptionProps) {\n return (\n \n \n {children}\n \n \n );\n}\n\nfunction Group = GroupBase>({\n children,\n ...props\n}: GroupProps) {\n return (\n \n {children}\n \n );\n}\n\nfunction Menu = GroupBase>({\n children,\n ...props\n}: MenuProps) {\n return (\n \n \n {children}\n \n \n );\n}\n\nfunction MenuList = GroupBase>({\n children,\n ...props\n}: MenuListProps) {\n return (\n \n {children}\n \n );\n}\n\nfunction IndicatorsContainer = GroupBase>({\n children,\n ...props\n}: IndicatorsContainerProps) {\n return {children};\n}\n\nfunction ClearIndicator = GroupBase>({\n children,\n ...props\n}: ClearIndicatorProps) {\n return (\n \n {children}\n \n );\n}\n\nfunction DropdownIndicator = GroupBase>({\n children,\n ...props\n}: DropdownIndicatorProps) {\n return (\n \n {children}\n \n );\n}\n\nfunction Input = GroupBase>({\n children,\n ...props\n}: InputProps) {\n return (\n \n {children}\n \n );\n}\n\nexport function Select(props: SelectProps) {\n const [val, setVal] = useState(undefined);\n const { value, label, inline, changeHandler, className, ...rest } = props;\n\n useEffect(() => {\n if (typeof value === \"string\" || typeof value === \"number\" || Array.isArray(value)) {\n setVal(\n rest.options\n ?.filter((item) => item.value?.toString()?.toLowerCase() === value?.toString()?.toLowerCase())\n .pop(),\n );\n } else {\n setVal(value as SelectOption);\n }\n }, [value, rest.options]);\n\n useEffect(() => {\n if (rest.defaultValue) {\n changeHandler?.(rest.defaultValue);\n }\n // disabling exhaustive-deps because we only want to run this when default value is changed\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [rest.defaultValue]);\n\n return (\n
\n
\n \n
\n {props.loadOptions ? (\n \n ) : (\n \n )}\n
\n
\n
\n );\n}\n","import { type ChangeEvent, type FC, type InputHTMLAttributes, useCallback } from \"react\";\n\nimport { styles } from \"../../../helpers\";\nimport \"./style.scss\";\n\ninterface TextBoxProps extends InputHTMLAttributes {\n label?: string;\n inline?: boolean;\n changeHandler?: (value: any, name?: any) => void;\n outerClassName?: string;\n}\n\nexport const TextBox: FC = (props) => {\n const { label, inline, changeHandler, className, outerClassName, onChange, id, ...rest } = props;\n\n const handleChange = useCallback(\n (e: ChangeEvent) => {\n if (changeHandler) {\n changeHandler(e.target.value, id);\n } else if (onChange) {\n onChange(e);\n }\n },\n [changeHandler, onChange, id],\n );\n\n return (\n
\n
\n \n
\n \n
\n
\n
\n );\n};\n","import {\n ChevronDoubleLeftIcon,\n ChevronDoubleRightIcon,\n ChevronLeftIcon,\n ChevronRightIcon,\n} from \"@heroicons/react/24/outline\";\nimport { type ReactNode, useCallback, useEffect, useMemo, useState } from \"react\";\n\nimport { styles, useBreakpoint } from \"../../../helpers\";\nimport { type keyPair } from \"../../../models\";\nimport { Select } from \"../select\";\nimport { TextBox } from \"../textBox\";\nimport { type CustomTable } from \"./dataGrid\";\n\ninterface DataGridPaginationProps {\n table: CustomTable;\n page: number;\n setPage: (page: number) => void;\n itemsPerPage: number;\n setItemsPerPage: (itemsPerPage: number) => void;\n pageSizeOptions: number[];\n totalItems: number;\n}\n\nexport function DataGridPagination({\n table,\n page,\n setPage,\n itemsPerPage,\n setItemsPerPage,\n pageSizeOptions,\n totalItems,\n}: DataGridPaginationProps) {\n const [pageString, setPageString] = useState(page.toString());\n const { isSm } = useBreakpoint(\"sm\");\n const pageCount = table.getPageCount();\n const pageSizes = useMemo(\n () => (pageSizeOptions ?? []).map((item) => ({ value: item, label: item })),\n [pageSizeOptions],\n );\n useEffect(() => {\n setPageString(page.toString());\n }, [page]);\n\n /**Sets new page, protecting against values to low or high */\n const updatePage = useCallback(\n (newPage: number) => {\n const fixedPage = Math.max(Math.min(newPage, pageCount), 1);\n setPage(fixedPage);\n },\n [pageCount, setPage],\n );\n\n const goToFirstPage = useCallback(() => updatePage(1), [updatePage]);\n const goToLastPage = useCallback(() => updatePage(pageCount), [updatePage, pageCount]);\n const pageBack = useCallback(() => updatePage(page - 1), [updatePage, page]);\n const pageForward = useCallback(() => updatePage(page + 1), [updatePage, page]);\n const gotoPage = useCallback(() => updatePage(parseInt(pageString)), [updatePage, pageString]);\n\n const selectPageSize = useCallback(\n (value: keyPair) => {\n goToFirstPage();\n setItemsPerPage(parseInt(value.value?.toString() ?? \"10\"));\n },\n [goToFirstPage, setItemsPerPage],\n );\n\n if ((pageSizeOptions?.at(0) ?? 10) > totalItems) {\n if (isSm) return null;\n\n return (\n
\n
\n
\n );\n }\n\n return (\n
\n
\n \n
\n \n \n \n \n \n \n\n
\n Page\n \n of {pageCount}\n
\n \n \n \n \n \n \n
\n
\n
\n \n Items Per Page\n
\n
\n );\n}\n\ninterface PageNavButtonProps {\n onClick: () => any;\n label: string;\n disabled?: boolean;\n children?: ReactNode;\n}\n\nfunction PageNavButton(props: PageNavButtonProps) {\n const { onClick, label, disabled, children } = props;\n return (\n \n {children}\n \n );\n}\n","import { type Row, flexRender } from \"@tanstack/react-table\";\nimport { type ReactNode } from \"react\";\n\nimport { styles } from \"../../../helpers\";\nimport { type CustomTable } from \"./dataGrid\";\n\ninterface Props {\n table: CustomTable;\n options?: (row: T) => ReactNode;\n rightOptions?: (row: T) => ReactNode;\n renderSubComponent?: (props: { row: Row }) => ReactNode;\n}\n\nexport function DataGridTableBody(props: Props) {\n const { table } = props;\n\n return (\n \n {renderPinnedRows()}\n {table.getCenterRows().map((row) => (\n \n ))}\n \n );\n\n function renderPinnedRows() {\n if (!table.getTopRows().length) return null;\n\n const numColumns = table.getAllColumns().filter((c) => c.getIsVisible()).length;\n\n return (\n <>\n \n \n Pinned\n \n \n {table.getTopRows().map((row) => (\n \n ))}\n \n \n Unpinned\n \n \n \n );\n }\n}\n\ninterface RowProps extends Props {\n row: Row;\n}\n\nfunction TableRow(props: RowProps) {\n const { table, row } = props;\n const { options, rightOptions, renderSubComponent } = props;\n return (\n <>\n \n {rightOptions ? {rightOptions(row.original)} : null}\n {row.getVisibleCells().map((cell) => {\n return (\n \n {flexRender(cell.column.columnDef.cell, cell.getContext())}\n \n );\n })}\n {options ? {options(row.original)} : null}\n \n {renderSubComponent && (\n \n {renderSubComponent({ row })}\n \n )}\n \n );\n}\n","import { ChevronDownIcon, ChevronUpIcon } from \"@heroicons/react/24/outline\";\nimport { type Header, type SortDirection, flexRender } from \"@tanstack/react-table\";\nimport { type DragEvent, type ReactNode, useCallback, useMemo, useRef, useState } from \"react\";\n\nimport { styles } from \"../../../helpers\";\nimport { type IFilter } from \"../../../models\";\nimport { type CustomTable } from \"./dataGrid\";\n\ninterface Props {\n table: CustomTable;\n options?: (row: T) => ReactNode;\n rightOptions?: (row: T) => ReactNode;\n setSortFilter?: (sortFilter: IFilter) => void;\n}\n\nexport function DataGridTableHead(props: Props) {\n const { table, options, rightOptions, setSortFilter } = props;\n const columnBeingDragged = useRef(-1);\n\n const [columnOver, setColumnOver] = useState(-1);\n const onDragStart = (e: DragEvent): void => {\n columnBeingDragged.current = Number(e.currentTarget.dataset.columnIndex);\n };\n\n //TODO: don't allow drag over action columns\n const onDragOver = (e: DragEvent): void => {\n e.preventDefault();\n const index = Number(e.currentTarget.dataset.columnIndex);\n /*\n const headers = table.getHeaderGroups().reverse()[0]?.headers;\n const matchingHeader = headers?.[index];\n\n console.log(matchingHeader);\n */\n setColumnOver(index);\n };\n\n const onDrop = (e: DragEvent): void => {\n e.preventDefault();\n const newPosition = Number(e.currentTarget.dataset.columnIndex);\n const currentCols = table.getVisibleLeafColumns().map((c) => c.id);\n const colToBeMoved = currentCols.splice(columnBeingDragged.current, 1)[0] as string;\n\n currentCols.splice(newPosition, 0, colToBeMoved);\n table.setColumnOrder(currentCols); //TODO: possibly save elsewhere?\n setColumnOver(-1);\n };\n\n //TODO: maybe color header groups?\n return (\n \n {table.getHeaderGroups().map((headerGroup) => (\n \n {rightOptions ? : null}\n {headerGroup.headers.map((header) => {\n const isGroup = header.column.columns.length > 0;\n return (\n columnBeingDragged.current &&\n \"border-r-4\",\n isGroup && \"\",\n )}\n >\n 0}\n sortable={header.column.getCanSort()}\n sortDirection={header.column.getIsSorted()}\n />\n \n );\n })}\n {options ? : null}\n \n ))}\n \n );\n}\n\ninterface HeaderCellProps {\n header: Header;\n setSortFilter?: (sortFilter: IFilter) => void;\n sortable: boolean;\n sortDirection: SortDirection | false;\n isGroup?: boolean;\n}\nfunction HeaderGroupCell(props: HeaderCellProps) {\n return (\n \n \n \n );\n}\nfunction HeaderCell(props: HeaderCellProps) {\n const { header, setSortFilter, sortable, isGroup, sortDirection } = props;\n const titleClass = useMemo(\n () =>\n styles(\n sortable && \"cursor-pointer select-none\",\n \"py-3.5 px-2 text-left flex\",\n \"text-sm font-semibold text-gray-900 dark:text-white\",\n ),\n [sortable],\n );\n\n const setSort = useCallback(() => {\n if (!sortable) return;\n setSortFilter?.({\n sortBy: {\n name: header.column.id.split(\"|\")[0] ?? \"\", //Pull out property name\n order:\n // Get what the current sorting is, then change to what it should be next before toggling\n sortDirection === \"desc\" ? -1 : sortDirection === \"asc\" ? 0 : 1,\n },\n });\n header.column.toggleSorting();\n }, [header, sortable, setSortFilter, sortDirection]);\n\n if (header.isPlaceholder) return null;\n if (isGroup) return ;\n\n return (\n \n );\n}\n","import { type ColumnDef, type ColumnHelper } from \"@tanstack/react-table\";\n\nimport { type Column } from \"./models/column\";\n\nexport function mapColumn(columnHelper: ColumnHelper) {\n return function (column: Column): ColumnDef {\n if (column.subColumns.length)\n return columnHelper.group({\n id: `${column.propertyName ?? \"\"}|${column.name}|${column.id}`,\n header: column.name,\n columns: column.subColumns.map(mapColumn(columnHelper)),\n });\n return columnHelper.accessor(column.propertyName as any, {\n id: `${column.propertyName ?? \"\"}|${column.name}|${column.id}`,\n header: column.name,\n enableSorting: column.sortable,\n minSize: column.minWidth,\n sortDescFirst: true,\n cell: (row) => {\n return (\n column.displayFunction\n ? column.displayFunction(column.propertyName ? row.getValue() : row.row.original)\n : row.getValue()\n ) as string;\n },\n });\n };\n}\n","import { ChevronDownIcon, ChevronRightIcon, MinusCircleIcon, PlusCircleIcon } from \"@heroicons/react/24/outline\";\nimport { type QueryKey, useQuery } from \"@tanstack/react-query\";\nimport {\n type ColumnDef,\n type ExpandedState,\n type Row,\n type RowPinningState,\n type RowSelectionState,\n type Table,\n createColumnHelper,\n getCoreRowModel,\n getExpandedRowModel,\n useReactTable,\n} from \"@tanstack/react-table\";\nimport { type ReactNode, useCallback, useEffect, useMemo, useState } from \"react\";\n\nimport { hasValue, styles, useBreakpoint } from \"../../../helpers\";\nimport { type GridResult, type IFilter, KeyedArray } from \"../../../models\";\nimport { IndeterminateCheckbox } from \"../inputs\";\nimport { Loading } from \"./loading\";\nimport { DataGridMobile } from \"./mobile\";\nimport { type Column } from \"./models/column\";\nimport { DataGridPagination } from \"./pagination\";\nimport { DataGridTableBody } from \"./tableBody\";\nimport { DataGridTableHead } from \"./tableHead\";\nimport { mapColumn } from \"./utils\";\n\ninterface Props {\n queryKey: QueryKey;\n getData: (signal: AbortSignal) => Promise | undefined>;\n columns: Column[];\n page: number;\n itemsPerPage: number;\n setItemsPerPage: (itemsPerPage: number) => void;\n setPage: (page: number) => void;\n pageSizeOptions?: number[];\n setSortFilter?: (sortFilter: IFilter) => void;\n options?: (row: T) => ReactNode;\n rightOptions?: (row: T) => ReactNode;\n selectedActions?: (rows: T[]) => ReactNode;\n expandable?: boolean;\n\n pinnable?: boolean;\n\n renderSubRow?: string;\n renderSubComponent?: (props: { row: Row }) => ReactNode;\n getRowCanExpand?: (row?: Row) => boolean;\n getRowClassNames?: (row: Row) => string;\n getRowId?: (item: T) => string;\n mapColumnName?: (colName: any) => string;\n\n pinnedIds?: string[];\n setPinnedIds?: (ids: string[]) => void;\n}\n\nexport type CustomTable = Table & {\n options?: {\n meta?: {\n getRowClassNames?: (row: Row) => string;\n };\n };\n};\n\nexport function DataGrid(props: Props) {\n const {\n queryKey,\n getData,\n columns,\n page,\n itemsPerPage,\n setItemsPerPage,\n setPage,\n pageSizeOptions,\n setSortFilter,\n options,\n rightOptions,\n selectedActions,\n expandable,\n renderSubRow,\n renderSubComponent,\n getRowCanExpand,\n getRowClassNames,\n mapColumnName,\n } = props;\n const gridDataQuery = useQuery({ queryKey: [...queryKey], queryFn: ({ signal }) => getData(signal) });\n const [rowSelection, setRowSelection] = useState({});\n const [expanded, setExpanded] = useState({});\n const [totalItems, setTotalItems] = useState(0);\n\n const selectColumn = useMemo(getSelectColumn, [selectedActions]);\n const expandColumn = useMemo(getExpandableColumn, [expandable]);\n const [pinnedItems, setPinnedItems] = useState(() => new KeyedArray([], props.getRowId ?? (() => \"\")));\n const [rowPinning, setRowPinning] = useState({\n top: [],\n bottom: [],\n });\n\n const pinItem = useCallback(\n (row: Row) => {\n const item = row.original;\n const newPinned = pinnedItems.copy();\n if (row.getIsPinned() === \"top\") {\n row.pin(false);\n newPinned.remove(item);\n } else {\n row.pin(\"top\");\n newPinned.set(item);\n }\n setPinnedItems(newPinned);\n\n if (props.setPinnedIds) {\n props.setPinnedIds(newPinned.data.map((item) => (props.getRowId ? props.getRowId(item) : \"\")));\n }\n },\n [props, pinnedItems, setPinnedItems],\n );\n\n const pinColumn = useMemo(getPinColumn, [props.pinnable, pinItem]);\n\n const columnHelper = createColumnHelper();\n\n const mappedColumns: ColumnDef[] = useMemo(() => {\n const baseColumns = columns.map(mapColumn(columnHelper));\n\n if (mapColumnName) baseColumns.forEach((c) => (c.header = mapColumnName(c.header)));\n\n return [selectColumn, pinColumn, expandColumn, ...baseColumns].filter(hasValue) as ColumnDef[];\n }, [columns, columnHelper, mapColumnName, selectColumn, pinColumn, expandColumn]);\n\n function getSelectColumn(): ColumnDef | undefined {\n if (!selectedActions) return undefined;\n return {\n id: \"select\",\n meta: { isIcon: true },\n header: ({ table }) => (\n \n ),\n cell: ({ row }) => (\n \n ),\n };\n }\n function getExpandableColumn(): ColumnDef | undefined {\n if (!expandable) return undefined;\n return {\n id: \"expand\",\n meta: { isIcon: true },\n header: () => null,\n cell: ({ row }) =>\n row.getCanExpand() ? (\n \n ) : null,\n };\n }\n function getPinColumn(): ColumnDef | undefined {\n if (!props.pinnable) return undefined;\n return {\n id: \"pin\",\n meta: { isIcon: true },\n header: () => null,\n cell: ({ row }) => {\n const isPinned = row.getIsPinned();\n return (\n
\n \n
\n );\n },\n };\n }\n\n useEffect(() => {\n if (gridDataQuery.isFetching) return;\n\n setTotalItems(gridDataQuery.data?.totalItems ?? 0);\n }, [gridDataQuery.data?.totalItems, gridDataQuery.isFetching]);\n\n const data = useMemo(() => {\n const queryItems = gridDataQuery.data?.items;\n\n if (!queryItems) return [];\n if (!pinnedItems.any()) return queryItems;\n return [...queryItems, ...pinnedItems.data];\n }, [gridDataQuery.data?.items, pinnedItems]);\n\n const table = useReactTable({\n data,\n columns: mappedColumns,\n state: {\n rowSelection,\n expanded,\n rowPinning,\n },\n getRowId: props.getRowId,\n enableRowSelection: !!selectedActions,\n onRowSelectionChange: setRowSelection,\n onExpandedChange: setExpanded,\n getSubRows: renderSubRow ? (row) => row[renderSubRow as keyof typeof row] as T[] : undefined,\n getExpandedRowModel: getExpandedRowModel(),\n getRowCanExpand,\n manualSorting: true,\n manualPagination: true,\n pageCount: Math.ceil(totalItems / (itemsPerPage ?? 10)),\n getCoreRowModel: getCoreRowModel(),\n debugTable: false,\n debugHeaders: false,\n debugColumns: false,\n meta: {\n getRowClassNames,\n },\n enableRowPinning: props.pinnable,\n keepPinnedRows: props.pinnable,\n onRowPinningChange: setRowPinning,\n }) as CustomTable;\n\n const selectedRows = table.getSelectedRowModel().rows.map(({ original }) => original);\n const { isSm } = useBreakpoint(\"sm\");\n\n return (\n
\n
\n
\n {selectedActions ? selectedActions(selectedRows) : null}\n
\n {!isSm ? (\n \n \n table={table}\n options={options}\n rightOptions={rightOptions}\n setSortFilter={setSortFilter}\n />\n {gridDataQuery.isFetching ? (\n \n ) : (\n \n table={table}\n options={options}\n rightOptions={rightOptions}\n renderSubComponent={renderSubComponent}\n />\n )}\n \n ) : (\n
\n {selectedActions ? (\n
\n
\n
\n \n
\n
\n
\n ) : null}\n \n table={table}\n options={options}\n rightOptions={rightOptions}\n renderSubComponent={renderSubComponent}\n expandable={expandable}\n />\n
\n )}\n
\n \n table={table}\n page={page}\n setPage={setPage}\n itemsPerPage={itemsPerPage}\n setItemsPerPage={setItemsPerPage}\n pageSizeOptions={pageSizeOptions ? pageSizeOptions : [10, 50, 100]}\n totalItems={totalItems}\n />\n
\n );\n}\n","import {\n type SortingState,\n createColumnHelper,\n getCoreRowModel,\n getSortedRowModel,\n useReactTable,\n} from \"@tanstack/react-table\";\nimport { type ReactNode, useState } from \"react\";\n\nimport { useBreakpoint } from \"../../../helpers\";\nimport { type IFilter } from \"../../../models\";\nimport { type CustomTable } from \"./dataGrid\";\nimport { Loading } from \"./loading\";\nimport { DataGridMobile } from \"./mobile\";\nimport { type Column } from \"./models/column\";\nimport { DataGridPagination } from \"./pagination\";\nimport { DataGridTableBody } from \"./tableBody\";\nimport { DataGridTableHead } from \"./tableHead\";\nimport { mapColumn } from \"./utils\";\n\ninterface DataGridClientProps {\n title?: string;\n data: T[];\n columns: Column[];\n loading?: boolean;\n pageSizeOptions?: number[];\n setSortFilter?: (sortFilter?: IFilter) => void;\n options?: (row: T) => ReactNode;\n embedded?: boolean;\n}\n\nexport function DataGridClient({\n title,\n data,\n columns,\n loading,\n pageSizeOptions,\n options,\n embedded,\n}: DataGridClientProps) {\n const [page, setPage] = useState(1);\n const [itemsPerPage, setItemsPerPage] = useState(10);\n const [sorting, setSorting] = useState([]);\n const columnHelper = createColumnHelper();\n const { isSm } = useBreakpoint(\"sm\");\n\n const table = useReactTable({\n data: embedded ? data : data.slice((page - 1) * itemsPerPage, page * itemsPerPage),\n columns: columns.map(mapColumn(columnHelper)),\n pageCount: Math.ceil(data.length / (itemsPerPage ?? 10)),\n state: {\n sorting,\n },\n manualPagination: true,\n onSortingChange: setSorting,\n getCoreRowModel: getCoreRowModel(),\n getSortedRowModel: getSortedRowModel(),\n debugTable: false,\n debugHeaders: false,\n debugColumns: false,\n }) as CustomTable;\n\n return (\n
\n {title ? (\n
\n

{title}

\n
\n ) : null}\n
\n {isSm ? (\n
\n table={table} options={options} />\n
\n ) : (\n \n table={table} options={options} />\n {loading ? : table={table} options={options} />}\n
\n )}\n
\n {!embedded && itemsPerPage && setItemsPerPage ? (\n \n page={page}\n setPage={setPage}\n table={table}\n itemsPerPage={itemsPerPage}\n setItemsPerPage={setItemsPerPage}\n pageSizeOptions={pageSizeOptions ?? [10, 50, 100]}\n totalItems={data.length}\n />\n ) : null}\n
\n );\n}\n","import { SortType } from \"../../../../models\";\n\nexport interface IColumn {\n name: string;\n propertyName?: keyof T;\n clientTemplate?: string;\n sortBy: SortType;\n sortable?: boolean;\n}\n\nexport class Column implements IColumn {\n public id: string | number = \"\";\n public name: string;\n public propertyName?: keyof TObj & string;\n public sortBy: SortType;\n public displayFunction?: (i: any) => any;\n public sortable?: boolean;\n public width?: string;\n public minWidth?: number;\n /* For templates/display functions, in case need access to other props */\n public useFullObject?: boolean;\n //to remove\n public clientTemplate?: string;\n public visible: boolean;\n\n public subColumns: Column[] = [];\n\n constructor(name: string, propertyName?: keyof TObj & string, sortBy: SortType = SortType.none, sortable = true) {\n this.name = name;\n this.propertyName = propertyName;\n this.sortBy = sortBy;\n this.sortable = sortable;\n this.visible = true;\n }\n\n public withSortBy(sortBy: SortType) {\n this.sortBy = sortBy;\n return this;\n }\n\n public clearFormat() {\n this.displayFunction = undefined;\n return this;\n }\n\n public withFormat(displayFunction?: (i: any) => any) {\n if (this.displayFunction) {\n const oldFn = this.displayFunction;\n if (displayFunction) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n this.displayFunction = (o: any) => displayFunction(oldFn(o));\n }\n } else this.displayFunction = displayFunction;\n return this;\n }\n\n public withSortable(sortable: boolean) {\n this.sortable = sortable;\n return this;\n }\n\n public withMinWidth(pixels: number) {\n this.minWidth = pixels;\n return this;\n }\n\n public withVisibility(visible: boolean) {\n this.visible = visible;\n return this;\n }\n\n public withId(id: string | number) {\n this.id = id;\n return this;\n }\n\n public withTemplate(template: string) {\n this.clientTemplate = template;\n return this;\n }\n\n public withSubColumn(subColumn: Column) {\n this.subColumns.push(subColumn);\n }\n\n public withFullObject(useFullObject = true) {\n this.useFullObject = useFullObject;\n return this;\n }\n}\n","import * as PopoverPrimitive from \"@radix-ui/react-popover\";\nimport { type ComponentPropsWithoutRef, type ElementRef, forwardRef } from \"react\";\n\nimport { styles } from \"../../../helpers\";\n\nconst Popover = PopoverPrimitive.Root;\n\nconst PopoverTrigger = PopoverPrimitive.Trigger;\n\nconst PopoverContent = forwardRef<\n ElementRef,\n ComponentPropsWithoutRef\n>(({ className, align = \"center\", sideOffset = 4, ...props }, ref) => (\n \n \n \n));\nPopoverContent.displayName = PopoverPrimitive.Content.displayName;\n\nexport { Popover, PopoverTrigger, PopoverContent };\n","import { CalendarIcon } from \"@heroicons/react/24/outline\";\n\nimport { formatDateOf } from \"../../../helpers\";\nimport { Button } from \"../button\";\nimport { Calendar } from \"../calendar\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../popover/popover\";\n\nexport const DATE_FORMAT = \"MM/dd/yy\";\n\ninterface DatePickerProps {\n id?: string;\n label?: string;\n value: Date | undefined;\n setValue: (date?: Date) => void;\n}\n\nexport function DatePicker(props: DatePickerProps) {\n const { id, label, value, setValue } = props;\n\n return (\n
\n {label}\n \n \n \n \n \n \n \n \n
\n );\n}\n","import { CalendarIcon } from \"@heroicons/react/24/outline\";\nimport { type DateRange } from \"react-day-picker\";\n\nimport { formatDateOf, styles } from \"../../../helpers\";\nimport { Button } from \"../button\";\nimport { Calendar } from \"../calendar\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../popover/popover\";\n\ninterface DateRangePickerProps {\n label?: string;\n id?: string;\n date?: DateRange | undefined;\n setDate?: (date: DateRange | undefined) => void;\n min?: Date;\n max?: Date;\n className?: string;\n}\n\nexport function DateRangePicker(props: DateRangePickerProps) {\n const { className, id, date, setDate, label = \"Date Range\" } = props;\n\n return (\n
\n {label}\n \n \n \n \n \n \n \n \n
\n );\n}\n","import { Dialog as HeadlessDialog, Transition } from \"@headlessui/react\";\nimport { XMarkIcon } from \"@heroicons/react/24/outline\";\nimport { type FC, Fragment, type ReactNode } from \"react\";\n\nimport { Button } from \"../button\";\n\ninterface DialogProps {\n open: boolean;\n setOpen: (open: boolean) => void;\n title?: ReactNode;\n body?: ReactNode;\n actions?: ReactNode;\n closeOnBackdropClick?: boolean;\n closeIcon?: boolean;\n}\n\nexport const Dialog: FC = ({ open, setOpen, title, body, actions, closeOnBackdropClick, closeIcon }) => {\n return (\n \n setOpen(false) : () => undefined}\n >\n \n
\n \n\n
\n
\n \n \n {closeIcon ? (\n
\n \n
\n ) : null}\n
\n
\n \n {title}\n \n\n
{body}
\n
\n
\n
{actions}
\n
\n \n
\n
\n \n \n );\n};\n","import { DocumentArrowUpIcon, FolderOpenIcon } from \"@heroicons/react/24/outline\";\nimport { type FC, useCallback } from \"react\";\nimport { useDropzone } from \"react-dropzone\";\n\ninterface DropzoneProps {\n files: File[];\n setFiles: (files: File[]) => void;\n maxFiles: number;\n accept?: { [key: string]: string[] };\n}\n\nexport const Dropzone: FC = (props) => {\n const onDrop = useCallback(\n (acceptedFiles: File[]) => {\n props.setFiles(acceptedFiles);\n },\n [props],\n );\n\n const { getRootProps, getInputProps, isDragActive } = useDropzone({\n onDrop,\n maxFiles: props.maxFiles,\n accept: props.accept,\n });\n\n return (\n
\n
\n \n {isDragActive ? (\n
\n \n Drop the files here ...\n
\n ) : (\n
\n \n Drag and drop your files here, or click to open file viewer\n
\n )}\n
\n
    \n {props.files?.map((file) => (\n
  • \n {file.name} - {file.size} B - {file.type}\n
  • \n ))}\n
\n
\n );\n};\n","import { type FC } from \"react\";\nimport { Link as RouterLink, type LinkProps as RouterLinkProps } from \"react-router-dom\";\n\nimport { Button } from \"../button\";\n\ntype LinkProps = RouterLinkProps;\n\nexport const Link: FC = (props) => {\n const { className, ...rest } = props;\n return (\n \n );\n};\n","import { type FC, type ReactNode } from \"react\";\n\nimport { styles } from \"../../../helpers\";\n\ninterface TabPageProps {\n className?: string;\n children: ReactNode;\n}\n\nexport const TabPage: FC = ({ children, className }) => {\n return
{children}
;\n};\n","import { type FC, type ReactNode } from \"react\";\n\nimport { styles } from \"../../../helpers\";\n\ninterface Props {\n children: ReactNode[];\n className?: string;\n}\n\nexport const TabsHeader: FC = ({ children, className }) => {\n return (\n
\n \n
\n );\n};\n","import { type ReactNode, createContext, useMemo, useState } from \"react\";\n\nimport { TabPage } from \"./tabPage\";\nimport { TabsHeader } from \"./tabsHeader\";\n\nexport interface TabsProps {\n containerClass?: string;\n headerClass?: string;\n tabHeaderClass?: string;\n bodyClass?: string;\n tabBodyClass?: string;\n defaultTab?: string | number;\n readOnly?: boolean;\n children: ReactNode[];\n}\n\nexport function Tabs(props: TabsProps) {\n const { children, containerClass, defaultTab } = props;\n const { headerClass, bodyClass, tabHeaderClass, tabBodyClass } = props;\n const [activeTab, setActiveTab] = useState(defaultTab ?? \"\");\n const providerProps = useMemo(\n () => ({\n activeTab,\n setActiveTab,\n tabBodyClass,\n tabHeaderClass,\n }),\n [activeTab, setActiveTab, tabBodyClass, tabHeaderClass],\n );\n return (\n
\n {children.length && (\n \n {children}\n \n )}\n \n {children}\n \n
\n );\n}\n\ninterface TabsContextType {\n activeTab?: string | number;\n setActiveTab: (tab: string | number) => void;\n isLink?: boolean;\n tabBodyClass?: string;\n tabHeaderClass?: string;\n}\n\nexport const TabsContext = createContext({\n setActiveTab: () => {\n return;\n },\n});\n\ninterface TabsProviderProps extends TabsContextType {\n children: ReactNode;\n}\n\nexport function TabsProvider(props: TabsProviderProps) {\n const { children, ...rest } = props;\n return {children};\n}\n","import {\n type ForwardRefExoticComponent,\n type MouseEvent,\n type PropsWithoutRef,\n type ReactNode,\n type SVGProps,\n useCallback,\n useContext,\n useMemo,\n} from \"react\";\n\nimport { styles, useBreakpoint, useOneShot } from \"../../../helpers\";\nimport { TabsContext } from \"./tabs\";\n\ninterface TabProps {\n title: string | ReactNode | ReactNode[];\n id: string | number;\n delayLoad?: boolean;\n alwaysReload?: boolean;\n className?: string;\n children?: ReactNode | ReactNode[];\n}\n\nexport function Tab(props: TabProps) {\n const { activeTab, isLink, tabBodyClass } = useContext(TabsContext);\n const { id, children, delayLoad, alwaysReload } = props;\n\n const isActive = useMemo(() => activeTab === id, [activeTab, id]);\n const activeOnce = useOneShot(isActive);\n\n if (isLink) return ;\n\n if (delayLoad && !activeOnce) return null;\n if (alwaysReload && !isActive) return null;\n\n return (\n
\n {children}\n
\n );\n}\n\nexport function TabHeader(props: TabProps) {\n const { title, id } = props;\n const { activeTab, setActiveTab, tabHeaderClass } = useContext(TabsContext);\n const { isSm } = useBreakpoint(\"sm\");\n\n const onClick = useCallback(() => setActiveTab(id), [setActiveTab, id]);\n const isActive = useMemo(() => activeTab === id, [activeTab, id]);\n\n return (\n \n {title}\n \n );\n}\n\ntype IconType = ForwardRefExoticComponent>>;\ninterface TabTitleProps {\n text: string;\n icon?: IconType;\n rightIcon?: IconType;\n onClickRight?: (e?: MouseEvent) => void;\n}\n\nexport function TabTitle(props: TabTitleProps) {\n return (\n
\n
\n
\n {renderIcon(props.icon, \"justify-self-end\")}\n {props.text}\n
\n {renderIcon(props.rightIcon, undefined, props.onClickRight)}\n
\n );\n function renderIcon(Icon?: IconType, className?: string, onClick?: (e?: MouseEvent) => void) {\n if (!Icon) return null;\n const clickHandler =\n onClick &&\n function (e: MouseEvent) {\n e.preventDefault();\n e.stopPropagation();\n onClick(e);\n };\n return ;\n }\n}\n","import { useCallback, useEffect, useState } from \"react\";\n\nimport { enterKey, styles } from \"../../../helpers\";\nimport { type keyPair } from \"../../../models\";\nimport { Select, type SelectOption } from \"../select\";\nimport { TextBox } from \"../textBox\";\n\ninterface Props {\n label?: string;\n inline?: boolean;\n onBlur?: () => void;\n onEnter?: () => void;\n changeHandler: (value: keyPair, name?: any) => void;\n onSelectChange?: (value: any) => void;\n value?: keyPair;\n id?: string;\n className?: string;\n options: SelectOption[];\n}\n\nexport function TextBoxWithSelect(props: Props) {\n const { label, inline, changeHandler, className, value, options, onBlur, onEnter, onSelectChange, ...rest } = props;\n\n const [selectVal, setSelectVal] = useState();\n const [textVal, setTextVal] = useState(\"\");\n\n const selectChange = useCallback(\n (val: SelectOption) => {\n setSelectVal(val?.value);\n onSelectChange?.(val?.value);\n },\n [onSelectChange],\n );\n\n const enterCallback = useCallback(() => onEnter && enterKey(onEnter), [onEnter]);\n\n useEffect(() => {\n if (!value) return;\n\n setSelectVal(value.id);\n setTextVal(value.value ?? \"\");\n }, [value]);\n\n useEffect(() => {\n changeHandler({ id: selectVal ?? \"\", value: textVal }, props.id);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [textVal]);\n\n //TODO: run on enter\n return (\n
\n
\n \n
\n \n );\n};\n","import { TextBox } from \"../../basic\";\nimport { LocationDropdown } from \"../../dropdowns/locationDropdown\";\nimport { type AddressFilterProps } from \"../types\";\n\nexport default function AddressFilter(props: AddressFilterProps) {\n const { visible, filters, selectVal, changeHandler } = props;\n\n if (!visible) return null;\n\n return (\n <>\n
\n
\n \n
\n \n
\n \n );\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { type FC } from \"react\";\n\nimport { GridFiltersApi } from \"../../api/gridFiltersApi\";\nimport { QueryKeys } from \"../../helpers\";\nimport { useMapVerbiage } from \"../../hooks/verbiageHooks\";\nimport { type DropdownProps, FilterDropdown } from \"./filterDropDown\";\n\ninterface Props extends DropdownProps {\n campaignIds?: number[];\n}\n\nexport const RepsDropdown: FC = (props) => {\n const query = useQuery({\n queryKey: [QueryKeys.REPS, props.campaignIds],\n queryFn: () => GridFiltersApi.getRepresentatives(props.campaignIds),\n });\n\n const mapVerbiage = useMapVerbiage();\n\n return ;\n};\n","import { CampaignDropdown } from \"../../dropdowns/campaignDropdown\";\nimport { RepsDropdown } from \"../../dropdowns/repsDropdown\";\nimport { type RepFilterProps } from \"../types\";\n\nexport default function RepFilter(props: RepFilterProps) {\n const { visible, filters, selectVal } = props;\n\n if (!visible) return null;\n\n return (\n <>\n \n \n \n );\n}\n","import { useState } from \"react\";\n\nimport { TextBox } from \"../../basic\";\nimport { type SearchFilterProps } from \"../types\";\n\nexport default function SearchFilter(props: SearchFilterProps) {\n const { visible, filters, changeHandler } = props;\n const [value, setValue] = useState(filters.search ?? \"\");\n\n const handleSubmit = () => {\n changeHandler(value, \"search\");\n };\n\n if (!visible) return null;\n\n return (\n setValue(newValue)}\n value={value}\n onBlur={handleSubmit}\n onKeyDown={(e) => e.key === \"Enter\" && handleSubmit()}\n />\n );\n}\n","import { XMarkIcon } from \"@heroicons/react/24/outline\";\nimport { useCallback, useMemo, useState } from \"react\";\n\nimport { type TextSearchOption, type keyPair } from \"../../../models\";\nimport { Button, TextBoxWithSelect } from \"../../basic\";\nimport { type SearchItemFilterProps } from \"../types\";\n\n//TODO: ability to clear all filters\n//TODO leave generalized search\n\n//maybe trigger on some kind of time delay (if no changes in half a second or something) - only if not already triggered by blur or enter\n//need it to set up trigger on entering\n\nexport default function SearchItemFilter(props: SearchItemFilterProps) {\n const { visible, filters, changeHandler } = props;\n const searchOptions = useMemo(() => filters.searchOptions ?? [], [filters.searchOptions]);\n const [selectedItem, setSelectedItem] = useState(searchOptions[0]?.id ?? \"\");\n const [searchItems, setSearchItems] = useState({} as Record);\n\n const selectedRecord = useMemo(\n () => ({ id: selectedItem, value: searchItems?.[selectedItem] }) as keyPair,\n [searchItems, selectedItem],\n );\n\n const onChange = useCallback(\n function (value: keyPair) {\n const id = value.id;\n const record = { ...searchItems } as Record;\n const val = value.value || undefined;\n if (!value.value) delete record[id];\n else record[id] = val;\n setSearchItems(record);\n return record;\n },\n [setSearchItems, searchItems],\n );\n\n const commitChange = useCallback(\n (newItems?: Record) => {\n newItems = newItems ?? searchItems;\n const items = Object.keys(newItems).length ? newItems : undefined;\n changeHandler(items, \"searchItems\");\n },\n [changeHandler, searchItems],\n );\n\n const commitChangeEvent = useCallback(() => commitChange(searchItems), [commitChange, searchItems]);\n\n const clearItem = useCallback(\n (id: string | number) => {\n const newItems = onChange({ id, value: undefined });\n commitChange(newItems);\n },\n [commitChange, onChange],\n );\n\n const items = useMemo(() => searchOptions.map((o) => o.id), [searchOptions]);\n const mappedOptions = useMemo(() => searchOptions.map((o) => ({ value: o.id, label: o.label })), [searchOptions]);\n\n if (!visible) return null;\n\n return (\n
\n \n
\n {items?.map((i) => (\n \n ))}\n
\n
\n );\n}\n\ninterface ItemProps {\n searchItems: Record;\n options: TextSearchOption[];\n id: string | number;\n selectItem: (id: string | number) => void;\n clearItem: (id: string | number) => void;\n}\n\nfunction SearchItem(props: ItemProps) {\n const { searchItems, options, id, selectItem, clearItem } = props;\n\n const selectItemCallback = useCallback(() => selectItem(id), [selectItem, id]);\n\n const clearItemCallback = useCallback(() => {\n clearItem(id);\n }, [clearItem, id]);\n\n const label = useMemo(() => options.find((o) => o.id === id)?.label, [options, id]);\n const value = useMemo(() => searchItems?.[id], [searchItems, id]);\n\n if (!value) return null;\n\n return (\n
\n \n \n \n \n
\n );\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { type FC } from \"react\";\n\nimport { GridFiltersApi } from \"../../api/gridFiltersApi\";\nimport { QueryKeys } from \"../../helpers\";\nimport { type DropdownProps, FilterDropdown } from \"./filterDropDown\";\n\nexport const ChannelsDropdown: FC = (props) => {\n const query = useQuery({ queryKey: [QueryKeys.CHANNELS_FILTER], queryFn: () => GridFiltersApi.getChannels() });\n\n return ;\n};\n","import { useQuery } from \"@tanstack/react-query\";\nimport { type FC } from \"react\";\n\nimport { GridFiltersApi } from \"../../api/gridFiltersApi\";\nimport { QueryKeys } from \"../../helpers\";\nimport { type DropdownProps, FilterDropdown } from \"./filterDropDown\";\n\nexport const ClientsDropdown: FC = (props) => {\n const query = useQuery({ queryKey: [QueryKeys.CLIENTS_FILTER], queryFn: () => GridFiltersApi.getClients() });\n\n return ;\n};\n","import { useQuery } from \"@tanstack/react-query\";\nimport { type FC } from \"react\";\n\nimport { GridFiltersApi } from \"../../api/gridFiltersApi\";\nimport { QueryKeys } from \"../../helpers\";\nimport { type DropdownProps, FilterDropdown } from \"./filterDropDown\";\n\ninterface StatesDropdownProps extends DropdownProps {\n all?: boolean;\n abbreviation?: boolean;\n}\n\nexport const StatesDropdown: FC = (props) => {\n const query = useQuery({\n queryKey: [QueryKeys.STATES_FILTER, props.abbreviation, props.all],\n queryFn: async () => {\n if (props.abbreviation) {\n return await GridFiltersApi.getStateAbbreviations(props.all);\n }\n\n return await GridFiltersApi.getStates(props.all);\n },\n });\n\n return ;\n};\n","import { ChannelsDropdown } from \"../../dropdowns/channelsDropdown\";\nimport { ClientsDropdown } from \"../../dropdowns/clientsDropdown\";\nimport { StatesDropdown } from \"../../dropdowns/statesDropdown\";\nimport { type SegmentFilterProps } from \"../types\";\n\nexport default function SegmentFilters(props: SegmentFilterProps) {\n const { visible, filters, selectVal } = props;\n\n if (!visible) return null;\n\n return (\n <>\n \n \n \n \n );\n}\n","import { Button, TextBox } from \"../../basic\";\nimport { type TimeFilterProps } from \"../types\";\n\nexport default function TimeFilters(props: TimeFilterProps) {\n const { visible, filters, changeHandler } = props;\n\n function setValues(start?: string, end?: string) {\n changeHandler(start, \"startTime\");\n changeHandler(end, \"endTime\");\n }\n\n function setDates(startHour?: number, endHour?: number) {\n const start = startHour ? `${startHour.toString().padStart(2, \"0\")}:00` : undefined;\n const end = endHour ? `${endHour.toString().padStart(2, \"0\")}:00` : undefined;\n\n setValues(start, end);\n }\n\n function setLastHour() {\n const date = new Date().getHours();\n const today = new Date().getHours() + 1;\n setDates(date, today);\n }\n\n function setMorning() {\n const date = 8;\n const today = 12;\n setDates(date, today);\n }\n\n function setAfternoon() {\n const date = 12;\n const today = 17;\n setDates(date, today);\n }\n\n function set8to5() {\n const startDate = 8;\n const endDate = 17;\n setDates(startDate, endDate);\n }\n\n function setAllTime() {\n setValues(undefined, undefined);\n }\n\n if (!visible) return null;\n\n return (\n
\n
\n setValues(e.target.value, filters.endTime)}\n />\n setValues(filters.startTime, e.target.value)}\n />\n
\n
\n \n \n \n \n \n
\n
\n );\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { type FC } from \"react\";\n\nimport { GridFiltersApi } from \"../../api/gridFiltersApi\";\nimport { QueryKeys } from \"../../helpers\";\nimport { type DropdownProps, FilterDropdown } from \"./filterDropDown\";\n\nexport const VerModesDropdown: FC = (props) => {\n const query = useQuery({ queryKey: [QueryKeys.VER_MODES_FILTER], queryFn: () => GridFiltersApi.getVerModes() });\n\n return ;\n};\n","import { useQuery } from \"@tanstack/react-query\";\nimport { type FC } from \"react\";\n\nimport { GridFiltersApi } from \"../../api/gridFiltersApi\";\nimport { QueryKeys } from \"../../helpers\";\nimport { type DropdownProps, FilterDropdown } from \"./filterDropDown\";\n\nexport const VerTypesDropdown: FC = (props) => {\n const query = useQuery({ queryKey: [QueryKeys.VER_TYPES_FILTER], queryFn: () => GridFiltersApi.getVerTypes() });\n\n return ;\n};\n","import { VerModesDropdown } from \"../../dropdowns/verModesDropdown\";\nimport { VerTypesDropdown } from \"../../dropdowns/verTypesDropdown\";\nimport { type VerificationFilterProps } from \"../types\";\n\nexport default function VerificationFilters(props: VerificationFilterProps) {\n const { visible, filters, selectVal } = props;\n\n if (!visible) return null;\n\n return (\n <>\n \n \n \n );\n}\n","import { type AllGridFilters } from \"../../models\";\nimport { type SelectOption } from \"../basic\";\nimport ActiveFilter from \"./filters/activeFilter\";\nimport CampaignFilter from \"./filters/campaignFilter\";\nimport DateFilters from \"./filters/dateFilters\";\nimport DispositionFilter from \"./filters/dispositionFilter\";\nimport LanguageFilter from \"./filters/languageFilter\";\nimport AddressFilter from \"./filters/mapFilter\";\nimport RepFilter from \"./filters/repFilter\";\nimport SearchFilter from \"./filters/searchFilter\";\nimport SearchItemFilter from \"./filters/searchItemFilter\";\nimport SegmentFilter from \"./filters/segmentFilters\";\nimport TimeFilters from \"./filters/timeFilters\";\nimport VerificationFilters from \"./filters/verificationFilters\";\nimport type Props from \"./types\";\nimport { FilterGroups } from \"./types\";\n\nexport default function GridFilters>(props: Props) {\n const { filters, visibleFilters, updateFilter } = props;\n\n function selectVal(value?: SelectOption | SelectOption[], id?: string) {\n if (!id) return;\n\n if (Array.isArray(value)) {\n updateFilter({ name: id, value: value.map((v) => v.value) });\n } else {\n updateFilter({ name: id, value: value?.value });\n }\n }\n\n function changeHandler(value: string | undefined, name: string) {\n updateFilter({ name, value });\n }\n\n const filterInfo = { filters, updateFilter, selectVal, changeHandler };\n\n return (\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n );\n}\n","import { type Row } from \"@tanstack/react-table\";\nimport { type ReactNode } from \"react\";\n\nimport { styles } from \"../../helpers\";\nimport { useColumnMapper } from \"../../helpers/gridCustomization\";\nimport { type AllGridFilters, type GridResult, type IFilter } from \"../../models\";\nimport { type Column, DataGrid } from \"../basic\";\nimport GridFilters from \"../gridFilters\";\nimport { type FilterGroups } from \"../gridFilters/types\";\n\ninterface GridPageProps> {\n // Title Props\n title?: string;\n actionButton?: ReactNode;\n\n // Filters Props\n updateFilter: (...filters: { name: string; value: any }[]) => void;\n filters: F;\n visibleFilters: FilterGroups[];\n\n // DataGrid Props\n queryKey: string;\n getData: (signal: AbortSignal) => Promise | undefined>;\n columns: Column[];\n itemsPerPage: number;\n setItemsPerPage: (itemsPerPage: number) => void;\n currentPage: number;\n setCurrentPage: (page: number) => void;\n sortFilter?: IFilter;\n setSortFilter?: (sortFilter: IFilter) => void;\n options?: (row: T) => ReactNode;\n rightOptions?: (row: T) => ReactNode;\n selectedActions?: (rows: T[]) => ReactNode;\n expandable?: boolean;\n\n /** If true, can pin rows to top, persist across pages */\n pinnable?: boolean;\n getRowId?: (row: T) => string;\n\n renderSubRow?: string;\n renderSubComponent?: (props: { row: Row }) => ReactNode;\n getRowCanExpand?: (row?: Row) => boolean;\n getRowClassNames?: (row: Row) => string;\n\n pinnedIds?: string[];\n setPinnedIds?: (ids: string[]) => void;\n}\n\nexport function GridPage, T extends object>(props: GridPageProps) {\n const { title, actionButton, queryKey, ...passThruProps } = props;\n const mapColumnName = useColumnMapper(title ?? \"no title\");\n return (\n
\n
\n

{title}

\n {actionButton}\n
\n {...passThruProps} />\n \n {...passThruProps}\n mapColumnName={mapColumnName}\n queryKey={[queryKey, props.filters, props.sortFilter, props.currentPage, props.itemsPerPage]}\n page={props.currentPage}\n setPage={props.setCurrentPage}\n />\n
\n );\n}\n","import { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { type AxiosError } from \"axios\";\nimport { type FC, type FormEvent, useState } from \"react\";\n\nimport { FormApi } from \"../../api/formApi\";\nimport { QueryKeys } from \"../../helpers\";\nimport { useReplaceVerbiage } from \"../../hooks/verbiageHooks\";\nimport { type FormTemplateDto } from \"../../models/formTemplateDto\";\nimport {\n Button,\n Checkbox,\n DatePicker,\n Dropzone,\n TextBox,\n ToggleButton,\n useErrorToast,\n useSuccessToast,\n} from \"../basic\";\n\ninterface State {\n activeStart: Date | undefined;\n activeEnd: Date | undefined;\n repSpecific: boolean;\n active: boolean;\n frontPages: number;\n backPages: number;\n lineBarcodesInvisible: boolean;\n notes: string;\n}\n\ninterface UpdateFormTemplateProps {\n formTemplate: FormTemplateDto;\n setOpen: (open: boolean) => void;\n create: boolean;\n}\n\nexport const CreateUpdateFormTemplate: FC = ({ formTemplate, setOpen, create }) => {\n const [state, setState] = useState({\n activeStart: formTemplate.activeStart ? formTemplate.activeStart : undefined,\n activeEnd: formTemplate.activeEnd ? formTemplate.activeEnd : undefined,\n repSpecific: formTemplate.repSpecific ?? false,\n active: formTemplate.active ?? true,\n frontPages: formTemplate.frontPages ?? 0,\n backPages: formTemplate.backPages ?? 0,\n lineBarcodesInvisible: formTemplate.lineBarcodesInvisible ?? false,\n notes: formTemplate.notes ?? \"\",\n });\n const [file, setFile] = useState([]);\n const queryClient = useQueryClient();\n const successToast = useSuccessToast();\n const errorToast = useErrorToast();\n const replaceVerbiage = useReplaceVerbiage();\n\n const handleSubmit = (event: FormEvent) => {\n event.preventDefault();\n\n templateMutation.mutate({\n ...state,\n id: formTemplate.id,\n campaignId: formTemplate.campaignId,\n formPdf: file.at(0),\n active: state.active,\n repSpecific: state.repSpecific,\n lineBarcodesInvisible: state.lineBarcodesInvisible,\n activeStart: state.activeStart,\n activeEnd: state.activeEnd,\n } as FormTemplateDto);\n };\n\n const templateMutation = useMutation({\n mutationFn: FormApi.saveFormTemplate,\n onSuccess: async () => {\n await queryClient.invalidateQueries({ queryKey: [QueryKeys.CAMPAIGNS] });\n setOpen(false);\n successToast(\n create ? \"Create Form Template\" : \"Update Form Template\",\n create ? \"Form Template created successfully\" : \"Form Template updated successfully\",\n );\n },\n onError: (error: AxiosError) => {\n errorToast(\n create ? \"Create Form Template\" : \"Update Form Template\",\n error.message ? error.message : create ? \"Form Template create failed\" : \"Form Template updated failed\",\n );\n },\n });\n\n return (\n
\n {create ? (\n (newFile[0] ? setFile([newFile[0]]) : setFile([]))}\n />\n ) : null}\n setState({ ...state, active: value })}\n id=\"active\"\n />\n
\n setState({ ...state, activeStart: value })}\n value={state.activeStart}\n />\n setState({ ...state, activeEnd: value })}\n value={state.activeEnd}\n />\n
\n {create ? (\n {\n setState({ ...state, repSpecific: value });\n }}\n checked={state.repSpecific}\n />\n ) : null}\n {create ? (\n
\n {\n setState({ ...state, frontPages: value });\n }}\n required\n />\n {\n setState({ ...state, backPages: value });\n }}\n required\n />\n
\n ) : null}\n {create ? (\n {\n setState({ ...state, lineBarcodesInvisible: value });\n }}\n checked={state.lineBarcodesInvisible}\n />\n ) : null}\n {\n setState({ ...state, notes: value });\n }}\n />\n
\n
\n setOpen(false)}\n >\n Cancel\n \n \n
\n
\n \n );\n};\n","import { Transition } from \"@headlessui/react\";\nimport { EllipsisVerticalIcon } from \"@heroicons/react/24/outline\";\nimport { type FC, Fragment, useCallback, useState } from \"react\";\nimport ClickAwayListener from \"react-click-away-listener\";\nimport { usePopper } from \"react-popper\";\n\nimport { Severity, logger } from \"../../../helpers\";\n\ninterface Action {\n name: string;\n onClick: () => void;\n}\n\ninterface DataGridOptionsProps {\n actions: Action[];\n}\n\nexport const DataGridOptions: FC = ({ actions }) => {\n const [open, setOpen] = useState(false);\n const [referenceElement, setReferenceElement] = useState(null);\n const [popperElement, setPopperElement] = useState(null);\n const { styles, attributes, update } = usePopper(referenceElement, popperElement, { placement: \"bottom-end\" });\n\n const handleClose = useCallback(() => {\n setOpen(false);\n }, [setOpen]);\n\n const openMenu = useCallback(() => {\n setOpen(!open);\n if (update) {\n update().catch((error) => {\n logger(Severity.Error, error);\n });\n }\n }, [open, setOpen, update]);\n\n if (actions.length === 0) return null;\n\n return (\n \n
\n
\n \n
\n
\n \n
\n {actions.map(renderAction)}\n
\n \n
\n
\n
\n );\n\n function renderAction(action: Action) {\n return (\n \n {action.name}\n \n );\n }\n};\n","import { useQuery } from \"@tanstack/react-query\";\nimport { useState } from \"react\";\n\nimport { FormApi } from \"../../api/formApi\";\nimport { MS_IN, QueryKeys } from \"../../helpers\";\nimport { type FormTemplateDto } from \"../../models/formTemplateDto\";\nimport { Button, Dialog } from \"../basic\";\nimport { DataGridOptions } from \"../basic/dataGrid/options\";\nimport { CreateUpdateFormTemplate } from \"./createUpdateFormTemplate\";\n\nexport const Options = (formTemplate: FormTemplateDto) => {\n const [show, setShown] = useState(false);\n const [openEditDialog, setOpenEditDialog] = useState(false);\n\n const fileUrlQuery = useQuery({\n queryKey: [QueryKeys.GET_FORM_TEMPLATE, formTemplate.id],\n queryFn: async () => {\n return FormApi.getFormTemplateUrl(formTemplate.id);\n },\n enabled: show,\n gcTime: MS_IN.MINUTE,\n });\n\n return (\n <>\n setShown(true) },\n { name: \"Edit\", onClick: () => setOpenEditDialog(true) },\n ]}\n />\n undefined}\n title=\"Print Form Template\"\n body={