= (props: AlmeLiveChatOperatorResponseProps) => {\n const classes: AlmeLiveChatOperatorResponseClasses = useStyles();\n const theme = useTheme();\n\n const response = {\n ...props.response,\n // TODO: add support for limited markdown in live chat (i.e. URL links).\n html: '',\n } as AlmeMarkdownResponseNext;\n\n useEffect(() => {\n if (response.navUrl && props.almeConfig.liveChatOperatorResponse.shouldAutoNav) {\n onTriggerAutoNavUrl(response.navUrl);\n }\n // Empty dependency-list so autoNav only happens once:\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n if (!isValidLiveChatOperatorResponse(props.response)) {\n return null;\n }\n\n const responseRevision = (props.responseId || response.responseRevision).toString();\n const entryId = response.id || (response.extensionData && (response.extensionData as EntryExtensionData)?.entryId) || responseRevision;\n\n let avatar = props.almeConfig.liveChatOperatorResponse.chatIcon;\n\n const operatorName = props.response.extensionData.OperatorData?.Name;\n if (props.almeConfig.liveChatOperatorResponse.useUniqueChatIcons && operatorName) {\n const iconColor = theme.palette.augmentColor({ main: convertToHex(operatorName) });\n avatar = ({operatorName.charAt(0)});\n }\n\n if (avatar && operatorName) {\n avatar = React.cloneElement(avatar, { alt: operatorName, title: operatorName });\n }\n\n const responseText = getAgentResponse(response, `${props.classes.ivaCardBodyText}`, `${props.classes.inlineButton}`, props.onDisplayLinkClick);\n\n return (\n \n {avatar}\n \n \n {responseText}\n {paintNavUrl(entryId, response.navUrl, responseRevision, classes, props.almeConfig, props.onDisplayLinkClick, props.focusProps, true)}\n \n \n
\n );\n};\n\nexport default withStyles(liveChatOperatorResponseStyles)(AlmeLiveChatOperatorResponse);\n","import React from 'react';\n\nimport { CardContent, Card, Typography } from '@material-ui/core';\nimport { withStyles, WithStyles } from '@material-ui/core/styles';\n\nimport { LiveChatNotificationResponseNext } from '@alme/types/response/LiveChatNotificationResponse';\nimport { EntryExtensionData } from '@alme/types/response/EntryExtensionData';\n\nimport liveChatUserResponseStyles from './AlmeLiveChatUserResponse.styles';\nimport { FocusComponentProps } from '../../../../models/ComponentProps';\n\nimport { getMaskedUserInput } from '../../../AlmeResponse/AlmeResponseUtils';\nimport { AlmeLiveChatUserResponseConfig } from './AlmeLiveChatUserResponseConfig';\n\nexport function isValidLiveChatUserResponse(response: LiveChatNotificationResponseNext): boolean {\n const maskedUserInput = getMaskedUserInput(response);\n const hasUserInput = !!maskedUserInput;\n return response?.extensionData?.MessageOrigin === 'User' && hasUserInput;\n}\n\ninterface AlmeLiveChatUserResponseProps extends WithStyles {\n response: LiveChatNotificationResponseNext;\n isLastResponse: boolean;\n almeConfig: AlmeLiveChatUserResponseConfig;\n focusProps?: FocusComponentProps;\n responseId?: string | number;\n}\n\nconst AlmeLiveChatUserResponse: React.FC = (props: AlmeLiveChatUserResponseProps) => {\n if (!isValidLiveChatUserResponse(props.response)) {\n return null;\n }\n\n const maskedUserInput = getMaskedUserInput(props.response);\n const responseRevision = (props.responseId || props.response.responseRevision).toString();\n const entryId = props.response.id || (props.response.extensionData && (props.response.extensionData as unknown as EntryExtensionData)?.entryId) || responseRevision;\n\n return (\n \n \n \n {maskedUserInput}\n \n \n {props.almeConfig.liveChatUserResponse.userIcon}\n
\n );\n};\n\nexport default withStyles(liveChatUserResponseStyles)(AlmeLiveChatUserResponse);\n","import { Theme } from '@material-ui/core';\n\n// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\nexport default function componentWithStyles({ palette, spacing }: Theme) {\n const userColor = palette.augmentColor({ main: palette.background.default });\n const userColorLight = palette.augmentColor({ main: userColor.light });\n return {\n userCard: {\n wordWrap: 'break-word' as const,\n margin: spacing(1),\n backgroundColor: userColorLight.main,\n color: userColorLight.contrastText,\n },\n userCardBody: {},\n userCardContent: {\n '&:last-child': {\n padding: spacing(2),\n },\n },\n userCardContainer: {\n display: 'flex',\n flexFlow: 'row nowrap',\n justifyContent: 'flex-end',\n },\n };\n}","import React from 'react';\n\nimport { CardContent, Card, Typography } from '@material-ui/core';\nimport { withStyles, WithStyles } from '@material-ui/core/styles';\n\nimport { LiveChatNotificationResponseNext } from '@alme/types/response/LiveChatNotificationResponse';\nimport { EntryExtensionData } from '@alme/types/response/EntryExtensionData';\nimport { AlmeMarkdownResponseNext } from '../../../AlmeResponse/AlmeResponse';\n\nimport liveChatSystemResponseStyles from './AlmeLiveChatSystemResponse.styles';\nimport { FocusComponentProps } from '../../../../models/ComponentProps';\n\nimport { getAgentResponse, getAgentResponseText } from '../../../AlmeResponse/AlmeResponseUtils';\nimport { AlmeLiveChatSystemResponseConfig } from './AlmeLiveChatSystemResponseConfig';\n\nexport function isValidLiveChatSystemResponse(response: LiveChatNotificationResponseNext, isLastResponse: boolean): boolean {\n const responseText = getAgentResponseText(response);\n const hasIvaResponse = !!(responseText);\n\n return hasIvaResponse && (response?.extensionData?.MessageOrigin === 'System') &&\n (response?.extensionData?.Transient === false || (response?.extensionData?.Transient === true && isLastResponse));\n}\n\ninterface AlmeLiveChatSystemResponseProps extends WithStyles {\n response: LiveChatNotificationResponseNext;\n isLastResponse: boolean;\n almeConfig: AlmeLiveChatSystemResponseConfig;\n focusProps?: FocusComponentProps;\n responseId?: string | number;\n}\n\nconst AlmeLiveChatSystemResponse: React.FC = (props: AlmeLiveChatSystemResponseProps) => {\n const response = {\n ...props.response,\n // TODO: add support for limited markdown in live chat (i.e. URL links).\n html: '',\n } as AlmeMarkdownResponseNext;\n\n if (!isValidLiveChatSystemResponse(props.response, props.isLastResponse)) {\n return null;\n }\n\n const responseText = getAgentResponse(response, props.classes.systemBodyText, props.classes.inlineButton);\n const responseRevision = (props.responseId || response.responseRevision).toString();\n const entryId = response.id || (response.extensionData && (response.extensionData as EntryExtensionData)?.entryId) || responseRevision;\n\n return (\n \n \n \n {responseText}\n \n \n
\n );\n};\n\nexport default withStyles(liveChatSystemResponseStyles)(AlmeLiveChatSystemResponse);\n","import { Theme } from '@material-ui/core';\n\n// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\nexport default function componentWithStyles({ palette, spacing, typography }: Theme) {\n const gutterPxSm = spacing(1);\n\n return {\n systemCard: {\n backgroundColor: palette.background.default,\n margin: gutterPxSm,\n },\n systemCardBody: {},\n systemBodyText: {\n '& * a': {\n ...typography.button,\n },\n '& p': {\n ...typography.caption,\n },\n '& p:first-child': {\n marginTop: 0,\n },\n '& p:last-child': {\n marginBottom: 0,\n },\n },\n systemCardContent: {\n '&:last-child': {\n padding: spacing(2),\n },\n },\n systemCardContainer: {\n display: 'flex',\n flexFlow: 'row nowrap',\n justifyContent: 'flex-start',\n },\n inlineButton: {\n background: 'none',\n border: 'none',\n padding: 0,\n color: '#069',\n textDec: 'underline',\n cursor: 'pointer',\n 'text-decoration': 'underline',\n },\n };\n}","import React from 'react';\n\nimport { Slide } from '@material-ui/core';\n\nimport { LiveChatNotificationResponseNext } from '@alme/types/response/LiveChatNotificationResponse';\n\nimport { FocusComponentProps } from '../../../models/ComponentProps';\n\nimport { AlmeLiveChatResponseConfig } from './AlmeLiveChatResponseConfig';\nimport AlmeLiveChatBanner, { isValidLiveChatBannerResponse } from '../AlmeLiveChatBanner/AlmeLiveChatBanner';\nimport AlmeLiveChatOperatorResponse, { isValidLiveChatOperatorResponse } from './AlmeLiveChatOperatorResponse/AlmeLiveChatOperatorResponse';\nimport { isLiveChatResponse } from '@alme/api-ts/Utils';\nimport AlmeLiveChatUserResponse, { isValidLiveChatUserResponse } from './AlmeLiveChatUserResponse/AlmeLiveChatUserResponse';\nimport AlmeLiveChatSystemResponse, { isValidLiveChatSystemResponse } from './AlmeLiveChatSystemResponse/AlmeLiveChatSystemResponse';\nimport { DisplayLink } from '@alme/types/response/DisplayLink';\nimport { NavUrl } from '@alme/types/response/NavUrl';\n\ninterface AlmeLiveChatResponseProps {\n response: LiveChatNotificationResponseNext;\n isNewResponse?: boolean;\n isLastResponse: boolean;\n almeConfig: AlmeLiveChatResponseConfig;\n focusProps?: FocusComponentProps;\n responseId?: string | number;\n onScroll: () => void;\n onDisplayLinkClick: (link: DisplayLink | NavUrl) => void;\n}\n\nconst AlmeLiveChatResponse: React.FC = (props: AlmeLiveChatResponseProps) => {\n\n const isChatResponse = isLiveChatResponse(props.response);\n const isOperatorResponse = isValidLiveChatOperatorResponse(props.response);\n const isUserResponse = isValidLiveChatUserResponse(props.response);\n const isSystemResponse = isValidLiveChatSystemResponse(props.response, props.isLastResponse);\n const isBannerResponse = isValidLiveChatBannerResponse(props.response);\n const isErrorResponse = !!props.response?.extensionData?.IsError;\n\n if (isErrorResponse || !isChatResponse || (!isOperatorResponse && !isUserResponse && !isSystemResponse && !isBannerResponse)) {\n return null;\n }\n\n /**\n * Note: Transient messages can be very verbose.\n * If scrolled to user can be pinned to the bottom of the history area.\n */\n\n const lcResponseComponent = (\n \n );\n\n\n if (props.isNewResponse) {\n return (\n \n {lcResponseComponent}\n \n );\n } else {\n return lcResponseComponent;\n }\n};\n\nexport default AlmeLiveChatResponse;\n","\nexport class AlmeEnvironmentConfig {\n private static instance: AlmeEnvironmentConfig;\n private static config: Record;\n\n private constructor() {\n AlmeEnvironmentConfig.config = {};\n }\n\n public static getInstance(): AlmeEnvironmentConfig {\n if (!AlmeEnvironmentConfig.instance) {\n AlmeEnvironmentConfig.instance = new AlmeEnvironmentConfig();\n }\n\n return AlmeEnvironmentConfig.instance;\n }\n\n public setConfig(config: Record): void {\n AlmeEnvironmentConfig.config = config;\n }\n\n public getConfig(): Record {\n return AlmeEnvironmentConfig.config;\n }\n}","import React from 'react';\nimport { IconButton, Snackbar, SnackbarContent } from '@material-ui/core';\nimport { withStyles, WithStyles } from '@material-ui/core/styles';\nimport CloseIcon from '@material-ui/icons/Close';\nimport { AlmeHistoryConfig } from '../AlmeHistory/AlmeHistoryConfig';\nimport { FocusComponentProps } from '../../models/ComponentProps';\nimport { AlmeEnvironmentConfig } from '../../api/AlmeEnvironmentConfig';\nimport timeoutWarningStyles from './AlmeTimeoutWarning.styles';\nimport { getTextWithConfigs } from '../../utils/ConfigUtils';\nimport { useIntl } from 'react-intl';\nimport { useFocusStyles } from '../FocusVisibleStyle';\ninterface AlmeTimeoutWarningProps extends WithStyles {\n almeConfig: AlmeHistoryConfig;\n shouldDisplayTimeout: boolean;\n focusProps?: FocusComponentProps;\n setShouldDisplayTimeout:(newState:boolean)=> void;\n}\n\nconst AlmeTimeoutWarning: React.FC = (props: AlmeTimeoutWarningProps) => {\n const intl = useIntl();\n const Focusclasses = useFocusStyles();\n const config = AlmeEnvironmentConfig.getInstance().getConfig();\n\n const defaultTime = config.ClientSessionTimeout ?? \"20\";\n const timeoutValue = defaultTime as string;\n\n const message = intl.formatMessage({\n id: \"timeout.timeoutWarningMessage\",\n defaultMessage: 'Default timeout warning message',\n description: 'AlmeTimeoutWarning text',\n });\n const timeoutMessage = getTextWithConfigs([timeoutValue], message);\n\n const handleTimeoutClose = (event?: React.SyntheticEvent, reason?: string): void => {\n if (reason === 'clickaway') {\n return;\n }\n\n props.setShouldDisplayTimeout(false);\n };\n\n\n if (!props.almeConfig.history.timeout.isEnabled || !props.shouldDisplayTimeout) {\n return null;\n }\n else {\n return (\n \n \n \n \n \n \n }\n />\n \n );\n }\n};\n\nexport default withStyles(timeoutWarningStyles)(AlmeTimeoutWarning);\n","import { Theme } from '@material-ui/core';\nimport { styledBy } from '../../models/AlmeColorMapping';\n\n// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\nexport default function componentWithStyles({ palette, spacing }: Theme) {\n return {\n snackBar: {\n position: 'absolute' as const,\n bottom: spacing(1),\n width: '100%',\n },\n snackBarMessage:{width:'80%'},\n snackBarAction:{display:'inline'},\n snackBarContent: {\n color: styledBy('color', {\n default: palette.text.primary,\n divider: palette.text.primary,\n initial: palette.text.primary,\n paper: palette.text.primary,\n primary: palette.primary.contrastText,\n secondary: palette.secondary.contrastText,\n }),\n backgroundColor: styledBy('color', {\n default: palette.background.default,\n divider: palette.divider,\n initial: 'initial',\n paper: palette.background.paper,\n primary: palette.primary.main,\n secondary: palette.secondary.main,\n }),\n },\n };\n}","export function getTextWithConfigs(values: unknown[], ogText: string): string {\n return ogText.replace(/\\{([0-9]+)\\}/g,\n function (_, index: number) { return values[index] as string; });\n}","import React, { ReactElement, useState, useRef, useEffect, MutableRefObject } from 'react';\n\nimport historyStyles from './AlmeHistory.styles';\nimport { debounce } from '@material-ui/core/utils';\nimport { Theme, Container } from '@material-ui/core';\nimport { createStyles, makeStyles, WithStyles, withStyles, useTheme } from '@material-ui/core/styles';\nimport { FocusComponentProps } from '../../models/ComponentProps';\nimport AlmeScrollButton from '../AlmeScrollButton/AlmeScrollButton';\nimport { AlmeHistoryConfig } from './AlmeHistoryConfig';\nimport { AlmeResponseNext } from '@alme/types/response/AlmeResponse';\nimport AlmeResponse from '../AlmeResponse/AlmeResponse';\nimport AlmeLiveChatResponse from '../LiveChat/AlmeLiveChatResponse/AlmeLiveChatResponse';\nimport { DisplayLink } from '@alme/types/response/DisplayLink';\nimport { CtaResponseAction } from '../ResponseActions/AlmeCtaButton/AlmeCtaButton';\nimport { isLiveChatResponse } from '@alme/api-ts/Utils';\nimport { inIframe } from '../../utils/iFrameUtils';\nimport { FireSurveyAppEvent } from \"../ResponseActions/AlmeSurvey/AlmeSurvey\";\nimport AlmeTimeoutWarning from '../AlmeTimeoutWarning/AlmeTimeoutWarning';\nimport { NavUrl } from '@alme/types/response/NavUrl';\nimport { LiveChatNotificationResponseNext } from '@alme/types/response/LiveChatNotificationResponse';\nimport { useIntl } from 'react-intl';\nimport { CookieLoader } from '@alme/storage-ts/CookieLoader';\n\nconst scrollPositionLoader = new CookieLoader('scrollPosition');\n\n\nconst useStyles = makeStyles((theme: Theme) =>\n createStyles({\n root: {\n overflow: 'auto',\n padding: theme.spacing(1),\n marginBottom: '2px',\n },\n }),\n);\n\ninterface AlmeHistoryProps extends WithStyles {\n scrollToResponse: MutableRefObject;\n history: AlmeResponseNext[];\n onDisplayLinkClick: (link: DisplayLink | NavUrl) => void;\n onSurveySubmitClick: FireSurveyAppEvent;\n setIsDialogOpen: (isOpen: boolean) => void;\n onCtaClick: (action: CtaResponseAction, entryId: string) => Promise;\n almeConfig: AlmeHistoryConfig;\n shouldDisplayTimeout: boolean;\n isDialogOpen?: boolean;\n focusProps?: FocusComponentProps;\n isLiveChatConnected?: boolean;\n setShouldDisplayTimeout: (newState: boolean) => void;\n}\n\nfunction AlmeHistory(props: AlmeHistoryProps): ReactElement {\n const intl = useIntl();\n const classes = useStyles();\n const theme = useTheme();\n const [isVisible, setIsVisible] = useState(false);\n const [renderControlFlag, setRenderControlFlag] = useState(false);\n const historyRef = useRef(null);\n\n const handleScroll = (): void => {\n if (!historyRef?.current?.children || historyRef.current.children.length <= 0) {\n return;\n }\n const scrollHeight = historyRef.current.scrollHeight;\n const scrollTop = historyRef.current.scrollTop;\n const offsetHeight = historyRef.current.offsetHeight;\n if (renderControlFlag) {\n props.scrollToResponse.current = false;\n scrollPositionLoader.UpdateStorage(scrollTop.toString());\n }\n\n\n const lastChild = historyRef.current.children[historyRef.current.children.length - 1];\n let childHeight = lastChild.clientHeight - theme.spacing(2);\n if (childHeight < 1) {\n childHeight = 0;\n }\n\n const shouldBeVisible = scrollTop < (scrollHeight - offsetHeight - childHeight);\n if ((isVisible && shouldBeVisible) || (!isVisible && !shouldBeVisible)) {\n return;\n }\n setIsVisible(shouldBeVisible);\n };\n\n const onScrollClick = () => {\n if (!historyRef?.current?.children || historyRef.current.children.length <= 0) {\n return;\n }\n setIsVisible(false);\n\n const scrollHeight = historyRef.current.scrollHeight;\n const lastChild = historyRef.current.children[historyRef.current.children.length - 1] as HTMLDivElement;\n\n if (!lastChild.clientHeight) {\n return;\n }\n let childHeight = lastChild.offsetHeight + theme.spacing(1);\n if (childHeight < 1) {\n childHeight = 0;\n }\n\n const childTop = (scrollHeight - childHeight);\n\n if (props.scrollToResponse.current) {\n if (inIframe()) {\n historyRef.current.scrollTop = childTop;\n }\n else if (lastChild?.scrollIntoView) {\n lastChild.scrollIntoView({ behavior: 'smooth' });\n }\n props.scrollToResponse.current = false;\n scrollPositionLoader.UpdateStorage(childTop.toString());\n }\n };\n\n useEffect(() => {\n handleScroll();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n if (historyRef.current) {\n //Navigation, scroll to bottom\n if (props.scrollToResponse.current) {\n onScrollClick();\n setRenderControlFlag(true);\n }\n else {\n const scroll = scrollPositionLoader.GetStorage();\n\n historyRef.current.scrollTop = scroll ? parseInt(scroll) : historyRef.current.scrollTop;\n\n setRenderControlFlag(true);\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [historyRef.current, setRenderControlFlag]);\n\n const accessibilityAttrs = {\n \"aria-live\": 'polite' as const,\n role: 'dialog',\n };\n return (\n \n
\n {props.history?.length > 0 && props.history.map((response, i) => {\n if (isLiveChatResponse(response)) {\n const prevResponse = (i > 0) ? props.history[i - 1] as LiveChatNotificationResponseNext : undefined;\n return (\n \n );\n }\n return (\n \n );\n })}\n \n {isVisible &&\n
}\n
\n
\n );\n}\n\nexport default withStyles(historyStyles)(AlmeHistory);\n\nexport function isNewResponse(response: LiveChatNotificationResponseNext, prevResponse?: LiveChatNotificationResponseNext): boolean {\n const isTransient = response?.extensionData?.Transient === true;\n const isPrevTransient = prevResponse?.extensionData?.Transient === true;\n return !isTransient || !isPrevTransient || response.text !== prevResponse?.text;\n}\n","export function inIframe(): boolean {\n try {\n return window.self !== window.top;\n } catch (e) {\n return true;\n }\n}\n\n","import { Theme } from '@material-ui/core';\n\n// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\nexport default function componentWithStyles({ palette, spacing }: Theme) {\n const backgroundColor = palette.divider;\n const dividerColor = palette.divider;\n const dividerBorder = `1px solid ${dividerColor}`;\n const darkBorder = `1px solid ${palette.action.disabledBackground}`;\n\n return {\n root: {\n paddingBottom: spacing(1),\n },\n colorBackground: {\n backgroundColor,\n },\n divider: {\n borderTop: dividerBorder,\n '&$colorBackground': {\n borderTop: darkBorder,\n },\n },\n };\n}","import { Theme } from '@material-ui/core';\n\n// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\nexport default function componentWithStyles({ spacing, palette }: Theme) {\n return {\n submitIcon: {},\n submitButton: {\n marginRight: spacing(1),\n marginTop: `-${spacing(1)}px`,\n },\n almeInputBox: {\n marginLeft: spacing(1),\n marginRight: spacing(1),\n marginBottom: 0,\n },\n textField: {\n color: palette.text.primary,\n },\n inputLabel: {},\n };\n}","import React, { useState, useRef, useCallback, useEffect, ReactElement, MutableRefObject } from 'react';\n\nimport inputStyles from './AlmeInput.styles';\n\nimport { Button, IconButton, TextField, InputAdornment } from '@material-ui/core';\nimport { withStyles, WithStyles, makeStyles, createStyles } from '@material-ui/core/styles';\n\nimport { FocusComponentProps } from '../../models/ComponentProps';\n\nimport { AlmeApiUtils } from '../../api/AlmeApiUtils';\n\nimport { EntryOriginTypes } from '@alme/api-ts/Alme/V6/EntryOriginTypes';\nimport { AlmeInputConfig } from './AlmeInputConfig';\nimport { useIntl } from 'react-intl';\nimport { useFocusStyles } from '../FocusVisibleStyle';\n\nconst useStyles = makeStyles(() =>\n createStyles({\n root: {\n display: 'flex',\n alignItems: 'center',\n margin: 0,\n padding: 0,\n },\n customInputLabel: {\n zIndex: 'unset',\n },\n }),\n);\n\ninterface AlmeInputProps extends WithStyles {\n inputId: string;\n submitId: string;\n submitFunc: (input: string) => void;\n almeConfig: AlmeInputConfig;\n isLiveChatConnected?: boolean;\n onSendTypingEvent?: () => void;\n focusProps?: FocusComponentProps;\n}\n\nconst AlmeInput: React.FC = (props: AlmeInputProps) => {\n const intl = useIntl();\n const classes = useStyles();\n const Focusclasses = useFocusStyles();\n const [userInput, setInput] = useState('');\n const [hasError, setError] = useState(false);\n const [isTyping, setIsTyping] = useState(false);\n\n const userTypingEventDelay = props.almeConfig.input.userTypingEventDelay || 10000;\n useEffect(() => {\n let timer: NodeJS.Timeout | null = null;\n if (isTyping) {\n timer = setTimeout(() => {\n setIsTyping(false);\n }, userTypingEventDelay);\n }\n\n return () => {\n if (timer !== null) {\n clearTimeout(timer);\n }\n };\n }, [isTyping, userTypingEventDelay]);\n\n const useAlmeInputRef = useRef(null);\n\n useEffect(() => {\n useAlmeInputRef?.current?.focus();\n }, []);\n\n /**\n * Handles input box change using the setInput state function.\n * This function handles max input length and will not allow\n * text longer than props.maxInputLength to be added.\n */\n const textChanged = (event: React.ChangeEvent): void => {\n let inputText = event.target.value;\n\n if (inputText?.length) {\n setError(false);\n }\n\n if (inputText.length > props.almeConfig.input.maxInputLength) {\n inputText = inputText.substring(0, props.almeConfig.input.maxInputLength);\n }\n\n if (props.isLiveChatConnected && typeof props.onSendTypingEvent === 'function' && !isTyping) {\n setIsTyping(true);\n props.onSendTypingEvent();\n }\n\n setInput(inputText);\n };\n\n /**\n * Submits the inputText using supplied submitFunc function\n * if the text is not empty or whitespace and then clears\n * the input box using the setInput state function.\n */\n const onSubmitInput = (): void => {\n const inputText = userInput.trim();\n\n if (inputText) {\n props.submitFunc(inputText);\n setInput('');\n // ? TODO: should we reset typing when the input is submitted?\n //setIsTyping(false);\n } else {\n setError(true);\n useAlmeInputRef?.current?.focus();\n }\n };\n\n const onKeyDown = (event: React.KeyboardEvent): void => {\n // 'keypress' event misbehaves on mobile so we track 'Enter' key via 'keydown' event\n if (event.key === 'Enter') {\n event.preventDefault();\n event.stopPropagation();\n onSubmitInput();\n }\n };\n\n const getHelperText = (almeConfig: AlmeInputConfig): string => {\n if (hasError && almeConfig.input.displayError) {\n return intl.formatMessage({\n id: \"input.inputErrorMessage\",\n defaultMessage: 'Default input error message',\n description: 'AlmeInput error helper text',\n });\n }\n return `${userInput.length}/${props.almeConfig.input.maxInputLength}`;\n };\n\n const accessibilityProps= getAccessibilityProps();\n\n const sharedInputProps = {\n id: props.inputId,\n error: props.almeConfig.input.displayError && hasError,\n className: props.classes.almeInputBox,\n value: userInput,\n label:\n intl.formatMessage({\n id: \"input.inputPlaceHolderText\",\n defaultMessage: 'Default input placeholderText',\n description: 'Alme Input placeholder text',\n }),\n 'aria-label':\n intl.formatMessage({\n id: \"input.inputTextAriaLabel\",\n defaultMessage: 'Default input text aria label',\n description: 'Alme Input text aria label',\n }),\n inputProps: { className: props.classes.textField },\n margin: 'normal' as const,\n onChange: textChanged,\n onKeyDown,\n fullWidth: true,\n inputRef: useAlmeInputRef,\n helperText: getHelperText(props.almeConfig),\n FormHelperTextProps: accessibilityProps,\n variant: props.almeConfig.input.styles.textField.variant,\n color: props.almeConfig.input.styles.textField.color,\n size: props.almeConfig.input.styles.textField.size,\n InputLabelProps: { className: `${props.classes.inputLabel}` },\n autoFocus: props.almeConfig.input.shouldAutoFocus,\n };\n\n const sharedButtonProps = {\n id: props.submitId,\n onClick: onSubmitInput,\n 'aria-label': intl.formatMessage({\n id: \"input.sendButtonAriaLabel\",\n defaultMessage: 'Default input send button aria label',\n description: 'AlmeInput send button aria label',\n }),\n color: props.almeConfig.input.styles.button.color,\n };\n\n const sharedFormProps = {\n className: classes.root,\n noValidate: true,\n autoComplete: \"off\",\n };\n\n const sharedTextInputProps = {\n };\n\n const getAlmeInput = (): ReactElement => {\n if (props.almeConfig.input.submitButtonText) {\n return (\n <>\n \n \n >\n );\n } else {\n return (\n \n {props.almeConfig.input.sendIcon}\n ),\n }}\n InputLabelProps={{classes: {outlined: classes.customInputLabel}}}\n />\n );\n }\n };\n\n return (\n \n );\n\n function getAccessibilityProps() {\n return hasError ? {\n className: `${props.classes.textField}`,\n role: 'alert',\n 'aria-atomic': true,\n } : {className: `${props.classes.textField}`};\n }\n};\n\nexport default withStyles(inputStyles)(AlmeInput);\n\nexport function OnSubmitAlmeInput(apiUtils: React.MutableRefObject, scrollToResponse: MutableRefObject): (input: string, origin?: EntryOriginTypes, displayText?: string, parameters?: Record) => void {\n return useCallback(async (input: string, origin?: EntryOriginTypes, displayText?: string, parameters?: Record) => {\n scrollToResponse.current = true;\n await apiUtils.current.submitAlmeInput(input, origin, displayText, parameters);\n }, [apiUtils, scrollToResponse]);\n}\n\nexport function OnSendTypingEvent(apiUtils: React.MutableRefObject): () => Promise {\n return useCallback(async () => {\n await apiUtils.current.submitReportingEvent('Typing');\n }, [apiUtils]);\n}\n","// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\nexport default function componentWithStyles() {\n return {\n linkContainer: {\n width: '100%',\n textAlign: 'center' as const,\n },\n };\n}","import React from \"react\";\nimport poweredByLinkStyles from './AlmePoweredByLink.styles';\nimport { Button, Container } from \"@material-ui/core\";\nimport { WithStyles, withStyles } from '@material-ui/core/styles';\nimport { FocusComponentProps } from \"../../models/ComponentProps\";\nimport { AlmePoweredByLinkConfig } from \"./AlmePoweredByLInkConfig\";\n\ninterface AlmePoweredByLinkProps extends WithStyles {\n almeConfig: AlmePoweredByLinkConfig;\n focusProps?: FocusComponentProps;\n}\n\nconst AlmePoweredByLink: React.FC | null = (props: AlmePoweredByLinkProps) => {\n if (props.almeConfig.poweredByLink.isEnabled) {\n return (\n \n \n \n );\n }\n else\n return null;\n\n};\nexport default withStyles(poweredByLinkStyles)(AlmePoweredByLink);","import React from \"react\";\nimport disclaimerStyles from './AlmeDisclaimer.styles';\nimport { Container, Typography } from \"@material-ui/core\";\nimport { WithStyles, withStyles } from '@material-ui/core/styles';\nimport { FocusComponentProps } from \"../../models/ComponentProps\";\nimport { AlmeDisclaimerConfig } from \"./AlmeDisclaimerConfig\";\nimport { FormattedMessage } from \"react-intl\";\n\ninterface AlmeDisclaimerProps extends WithStyles {\n almeConfig: AlmeDisclaimerConfig;\n focusProps?: FocusComponentProps;\n}\n\nconst AlmeDisclaimer: React.FC | null = (props: AlmeDisclaimerProps) => {\n if (props.almeConfig.disclaimer.isEnabled) {\n return (\n \n \n \n \n \n );\n }\n else {\n return null;\n }\n\n};\nexport default withStyles(disclaimerStyles)(AlmeDisclaimer);\n","// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\nexport default function componentWithStyles() {\n return {\n disclaimer: {\n width: '100%',\n textAlign: 'center' as const,\n },\n };\n}\n","import React from 'react';\n\nimport footerStyles from './AlmeFooter.styles';\nimport AlmeInput from '../AlmeInput/AlmeInput';\nimport AlmePoweredByLink from '../AlmePoweredByLink/AlmePoweredByLink';\n\nimport { Container } from '@material-ui/core';\nimport { withStyles, WithStyles } from '@material-ui/core/styles';\nimport { FocusComponentProps } from '../../models/ComponentProps';\nimport { AlmeFooterConfig } from './AlmeFooterConfig';\nimport AlmeDisclaimer from \"../AlmeDisclaimer/AlmeDisclaimer\";\n\ninterface AlmeFooterProps extends WithStyles {\n submitFunc: (input: string) => void;\n almeConfig: AlmeFooterConfig;\n isLiveChatConnected?: boolean;\n onSendTypingEvent?: () => void;\n focusProps?: FocusComponentProps;\n}\n\nconst AlmeFooter: React.FC = (props: AlmeFooterProps) => {\n let footerClasses = `${props.classes.root}`;\n if (props.almeConfig.footer.styles.hasBorder) {\n footerClasses += ` ${props.classes.divider}`;\n }\n if (props.almeConfig.footer.styles.hasBackgroundColor) {\n footerClasses += ` ${props.classes.colorBackground}`;\n }\n\n return (\n \n );\n};\n\nexport default withStyles(footerStyles)(AlmeFooter);\n","import { Theme } from '@material-ui/core';\nimport { styledBy } from '../../models/AlmeColorMapping';\n\n// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\nexport default function componentWithStyles({ breakpoints, palette, shadows, spacing }: Theme) {\n const mainShadow = shadows[5];\n const gutterPx = spacing(2);\n const gutterPxSm = spacing(1);\n const dividerColor = palette.divider;\n const dividerBorder = `1px solid ${dividerColor}`;\n\n const augmentPrimaryColor = { main: palette.primary.dark };\n const augmentSecondaryColor = { main: palette.secondary.dark };\n const augmentPrimary = palette.augmentColor(augmentPrimaryColor);\n const augmentSecondary = palette.augmentColor(augmentSecondaryColor);\n\n return {\n root: {\n position: 'fixed' as const,\n right: gutterPx + gutterPxSm,\n bottom: gutterPx * 2 + gutterPxSm,\n borderRadius: spacing(0),\n display: 'flex',\n alignItems: 'flex-end',\n [breakpoints.down('sm')]: {\n right: gutterPxSm,\n bottom: gutterPx + gutterPxSm,\n },\n },\n icon: {\n color: styledBy('color', {\n default: palette.getContrastText(palette.background.default),\n divider: palette.getContrastText(palette.divider),\n initial: palette.primary.contrastText,\n paper: palette.getContrastText(palette.background.paper),\n primary: palette.primary.contrastText,\n secondary: palette.secondary.contrastText,\n }),\n backgroundColor: styledBy('color', {\n default: palette.background.default,\n divider: palette.divider,\n initial: 'initial',\n paper: palette.background.paper,\n primary: palette.primary.main,\n secondary: palette.secondary.main,\n }),\n '&:hover': {\n backgroundColor: styledBy('color', {\n default: palette.background.default,\n divider: palette.divider,\n initial: 'initial',\n paper: palette.background.paper,\n primary: augmentPrimary.main,\n secondary: augmentSecondary.main,\n }),\n color: styledBy('color', {\n default: palette.getContrastText(palette.background.default),\n divider: palette.getContrastText(palette.divider),\n initial: palette.primary.contrastText,\n paper: palette.getContrastText(palette.background.paper),\n primary: augmentPrimary.contrastText,\n secondary: augmentSecondary.contrastText,\n }),\n },\n boxShadow: mainShadow,\n zIndex: 2,\n },\n button: {\n color: styledBy('color', {\n default: palette.getContrastText(palette.background.default),\n divider: palette.getContrastText(palette.divider),\n initial: palette.primary.contrastText,\n paper: palette.getContrastText(palette.background.paper),\n primary: palette.primary.contrastText,\n secondary: palette.secondary.contrastText,\n }),\n backgroundColor: styledBy('color', {\n default: palette.background.default,\n divider: palette.divider,\n initial: 'initial',\n paper: palette.background.paper,\n primary: palette.primary.main,\n secondary: palette.secondary.main,\n }),\n '&:hover': {\n backgroundColor: styledBy('color', {\n default: palette.getContrastText(palette.background.default),\n divider: palette.getContrastText(palette.divider),\n initial: palette.primary.contrastText,\n paper: palette.getContrastText(palette.background.paper),\n primary: augmentPrimary.main,\n secondary: augmentSecondary.main,\n }),\n color: styledBy('color', {\n default: palette.getContrastText(palette.background.default),\n divider: palette.getContrastText(palette.divider),\n initial: palette.primary.contrastText,\n paper: palette.getContrastText(palette.background.paper),\n primary: augmentPrimary.contrastText,\n secondary: augmentSecondary.contrastText,\n }),\n },\n zIndex: 1,\n marginLeft: spacing(0) - gutterPxSm,\n },\n card: {\n border: dividerBorder,\n backgroundColor: palette.background.default,\n minWidth: 350,\n },\n cardHeader: {\n backgroundColor: palette.primary.main,\n color: palette.primary.contrastText,\n padding: spacing(1),\n },\n cardActions: {\n padding: spacing(0),\n paddingBottom: spacing(1),\n },\n };\n}","import MinimizeIcon from '@material-ui/icons/Remove';\n\nimport React, { Dispatch, SetStateAction, useEffect } from 'react';\n\nimport { useTheme, withStyles, WithStyles } from '@material-ui/core/styles';\nimport {\n Button,\n Box,\n IconButton,\n Fade,\n Card,\n CardHeader,\n CardActions,\n useMediaQuery,\n} from '@material-ui/core';\n\nimport launchPointStyles from './AlmeLaunchPoint.styles';\nimport AlmeInput from '../AlmeInput/AlmeInput';\nimport { AlmeLaunchPointConfig } from './AlmeLaunchPointConfig';\nimport { AlmeLaunchIvaMessageEventData } from '@alme/post-message-api/AlmeLaunchIvaMessageEventData';\nimport { defaultAccessKey, getAccessKeyFromQuery } from '../../utils/PersonaUtils';\nimport { AlmeIvaTypeProp } from '../../models/AlmeIvaTypeMapping';\nimport { useIntl } from 'react-intl';\nimport { CookieLoader } from '@alme/storage-ts/CookieLoader';\n\nconst savedAccessKey = new CookieLoader('verintAccessKey');\n\ninterface AlmeLaunchPointProps extends WithStyles {\n isOpen?: boolean;\n ivaType?: AlmeIvaTypeProp;\n almeConfig: AlmeLaunchPointConfig;\n hasHistory: boolean;\n}\n\nfunction shouldShowLaunchPoint(ivaType?: AlmeIvaTypeProp) {\n return !ivaType || ivaType === 'embedded';\n}\n\nconst sendLaunchPostMessage = (hasHistory: boolean, inputPlaceholderText: string, setLaunchQuestion: Dispatch>, inputFieldText?: string): void => {\n if (inputFieldText && !hasHistory && inputPlaceholderText) {\n setLaunchQuestion(true);\n }\n else {\n const accessKey = getAccessKeyFromQuery() || savedAccessKey.GetStorage() || defaultAccessKey;\n\n const data = {\n message: {\n type: \"LaunchIva\",\n source: \"client\",\n launchPointName: \"Global\",\n launchParameter: \"MainPage\",\n accessKey,\n },\n } as AlmeLaunchIvaMessageEventData;\n\n window.postMessage(data, window.location.origin);\n }\n};\n\nconst sendInputPostMessage = (setLaunchQuestion: Dispatch>, input?: string): void => {\n const accessKey = getAccessKeyFromQuery() || savedAccessKey.GetStorage() || defaultAccessKey;\n\n const data = {\n message: {\n type: \"LaunchIva\",\n source: \"client\",\n launchPointName: \"Global\",\n launchParameter: \"MainPage\",\n launchQuestion: input,\n accessKey,\n },\n } as AlmeLaunchIvaMessageEventData;\n\n if (input) {\n setLaunchQuestion(true);\n }\n\n window.postMessage(data, window.location.origin);\n};\n\nconst AlmeLaunchPoint: React.FC = (props: AlmeLaunchPointProps) => {\n /** State variable controls if launch with question form will render, initially set to false. */\n const [launchQuestion, setLaunchQuestion] = React.useState(false);\n const intl = useIntl();\n const theme = useTheme();\n const isSmall = useMediaQuery(theme.breakpoints.down('sm')) && !props.almeConfig.launchPoint.inputFieldText;\n\n useEffect(() => {\n if (!props.isOpen) {\n setLaunchQuestion(false);\n }\n }, [props.isOpen]);\n\n /**\n * Upon clicking the launch icon, check if launch with question config is set, and is start of a new session\n * If conditions pass, set state variable to render launch with question.\n */\n const handleClick = (): void => {\n sendLaunchPostMessage(props.hasHistory, intl.formatMessage({\n id: \"launchpoint.inputPlaceHolderText\",\n defaultMessage: 'Default launchpoint input placeholderText',\n description: 'AlmeLaunchPoint input placeholder text',\n }), setLaunchQuestion, props.almeConfig.launchPoint.inputFieldText);\n };\n\n const handleInputClick = (input?: string): void => {\n sendInputPostMessage(setLaunchQuestion, input);\n };\n\n const handleMinClick = (): void => {\n if (props.almeConfig.launchPoint.inputFieldText)\n setLaunchQuestion(false);\n };\n\n if (!shouldShowLaunchPoint(props.ivaType)) {\n return null;\n }\n\n const sharedIconProps = {\n id: 'launchIvaBtn',\n onClick: handleClick,\n title: props.almeConfig.launchPoint.titleText || \"Launch IVA\",\n };\n\n return (\n \n \n {\n launchQuestion &&\n !props.hasHistory &&\n \n \n }>\n \n \n \n \n \n }\n {\n !launchQuestion &&\n props.almeConfig.launchPoint.launchIcon &&\n {props.almeConfig.launchPoint.launchIcon}\n }\n {\n !launchQuestion &&\n props.almeConfig.launchPoint.buttonText &&\n \n }\n \n \n );\n};\n\nexport default withStyles(launchPointStyles)(AlmeLaunchPoint);\n","export type AlmeIvaTypeProp = 'embedded' | 'popup' | 'inline';\n\ninterface AlmeIvaTypeStyles extends Record {\n ivaType?: AlmeIvaTypeProp;\n children?: React.ReactNode;\n}\n\ntype AlmeIvaTypeMapping = Record;\n\nexport const styledBy = (property: string, mapping: AlmeIvaTypeMapping) => (props: AlmeIvaTypeStyles): string | undefined => {\n return mapping[props[property] as string || 'embedded'] as string | undefined;\n};\n","import { Theme } from '@material-ui/core';\nimport { createStyles } from '@material-ui/core/styles';\nimport { fade } from '@material-ui/core/styles';\nimport { styledBy } from '../../models/AlmeIvaTypeMapping';\n\n// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\nexport default function componentWithStyles({ breakpoints, palette, shadows, spacing }: Theme) {\n const mainShadow = shadows[5];\n const backgroundColor = palette.background.default;\n const gutterPx = spacing(2);\n const dividerColor = palette.divider;\n const dividerBorder = `1px solid ${dividerColor}`;\n const maxHeight = 650;\n const width = 450;\n return createStyles({\n root: {\n display: 'flex',\n flexDirection: 'column',\n backgroundColor,\n border: dividerBorder,\n boxShadow: mainShadow,\n },\n rootIvaType: {\n [breakpoints.down('sm')]: {\n width: styledBy('ivaType', {\n embedded: '100%',\n popup: '100%',\n inline: '100%',\n }),\n height: styledBy('ivaType', {\n embedded: `100%`,\n popup: '100%',\n inline: '100%',\n }),\n bottom: styledBy('ivaType', {\n embedded: `0`,\n popup: 'auto',\n inline: 'auto',\n }),\n right: styledBy('ivaType', {\n embedded: `0`,\n popup: 'auto',\n inline: 'auto',\n }),\n },\n [breakpoints.up(768)]: {\n width: styledBy('ivaType', {\n embedded: `${width}px`,\n popup: '100%',\n inline: '100%',\n }),\n height: styledBy('ivaType', {\n embedded: `calc(100% - ${gutterPx * 2}px)`,\n popup: '100%',\n inline: '100%',\n }),\n maxHeight: styledBy('ivaType', {\n embedded: `${maxHeight}px`,\n popup: 'none',\n inline: 'none',\n }),\n bottom: styledBy('ivaType', {\n embedded: `${gutterPx}px`,\n popup: 'auto',\n inline: 'auto',\n }),\n right: styledBy('ivaType', {\n embedded: `${gutterPx}px`,\n popup: 'auto',\n inline: 'auto',\n }),\n },\n margin: styledBy('ivaType', {\n embedded: 0,\n popup: 'auto auto',\n inline: 'auto auto',\n }),\n position: styledBy('ivaType', {\n embedded: 'fixed',\n popup: 'static',\n inline: 'static',\n }) as unknown as 'static' | 'fixed',\n },\n rootPulse: {\n boxShadow: `0 0 0 ${fade(palette.primary.main, .75)}`,\n animation: `$pulse 2s infinite`,\n '&:hover': {\n animation: 'none',\n },\n },\n '@keyframes pulse': {\n '0%': {\n boxShadow: `0 0 0 0 ${fade(palette.primary.main, .75)}`,\n },\n '70%': {\n boxShadow: `0 0 0 10px ${fade(palette.primary.main, 0)}`,\n },\n '100%': {\n boxShadow: `0 0 0 0 ${fade(palette.primary.main, 0)}`,\n },\n },\n });\n}\n","import React from 'react';\nimport { DialogTitle, DialogContent, DialogActions, Dialog } from '../../AlmeDialog/AlmeDialog';\nimport { FocusComponentProps } from '../../../models/ComponentProps';\n\nimport { createStyles, makeStyles } from '@material-ui/core/styles';\nimport { Button, Typography } from '@material-ui/core';\nimport { AlmeLiveChatControlsConfig } from './AlmeLiveChatControlsConfig';\nimport { FormattedMessage } from 'react-intl';\n\nconst useStyles = makeStyles(() =>\n createStyles({\n closeContainer: {\n display: 'inline-block',\n },\n }),\n);\n\ninterface AlmeEndLiveChatDialogProps {\n onEndClick: () => void;\n onCancelClick: () => void;\n almeConfig: AlmeLiveChatControlsConfig;\n setIsDialogOpen: (isOpen: boolean) => void;\n focusProps?: FocusComponentProps;\n isOpen: boolean;\n}\n\nconst AlmeEndLiveChatDialog: React.FC = (props: AlmeEndLiveChatDialogProps) => {\n\n const classes = useStyles();\n const handleEndClick = () => {\n props.onEndClick();\n props.onCancelClick();\n };\n\n return (\n \n \n
\n );\n};\nexport default AlmeEndLiveChatDialog;","import React, { useCallback, useState, useEffect, useRef } from 'react';\n\nimport { Theme, Box, Button, Typography } from '@material-ui/core';\nimport { withStyles, WithStyles, createStyles, makeStyles } from '@material-ui/core/styles';\n\nimport { LiveChatNotificationResponseNext } from '@alme/types/response/LiveChatNotificationResponse';\n\nimport liveChatControlsStyles from './AlmeLiveChatControls.styles';\nimport { FocusComponentProps } from '../../../models/ComponentProps';\nimport { AlmeResponseNext } from '@alme/types/response/AlmeResponse';\nimport { AlmeApiUtils } from '../../../api/AlmeApiUtils';\nimport { AlmeLiveChatControlsConfig } from './AlmeLiveChatControlsConfig';\nimport AlmeEndLiveChatDialog from './AlmeEndLiveChatDialog';\n\nconst useStyles = makeStyles(({ spacing }: Theme) =>\n createStyles({\n root: {\n display: 'flex',\n flexWrap: 'nowrap',\n marginBottom: `-${spacing(1)}px`,\n alignItems: 'center',\n },\n endChatButton: {\n },\n typingNotification: {\n padding: `${spacing(.75)}px ${spacing(1)}`,\n flexGrow: 1,\n textAlign: 'right',\n marginRight: spacing(1),\n },\n }),\n);\n\nfunction HandleIsDialogOpen(props: AlmeLiveChatControlsProps, shouldFocus: boolean,\n closeRef: React.RefObject, setShouldFocus: React.Dispatch>): void {\n useEffect(() => {\n if (!props.isDialogOpen && shouldFocus) {\n closeRef.current?.focus();\n setShouldFocus(false);\n }\n }, [props.isDialogOpen, shouldFocus, setShouldFocus, closeRef]);\n}\n\nfunction OperatorTyping(props: AlmeLiveChatControlsProps, typingMessage: React.MutableRefObject, isTyping: boolean,\n typingId: number, setIsTyping: React.Dispatch>, setTypingId: React.Dispatch>): void {\n useEffect(() => {\n if ((props.response as LiveChatNotificationResponseNext)?.extensionData?.MessageOrigin === 'Operator-Typing') {\n const operatorName = (props.response as LiveChatNotificationResponseNext)?.extensionData?.OperatorData?.Name || props.almeConfig.liveChatControls.defaultOperatorName;\n typingMessage.current = `${operatorName} ${props.almeConfig.liveChatControls.typingMessageText}`;\n if (!isTyping && typingId < props.responseId) {\n setIsTyping(true);\n setTypingId(props.responseId);\n }\n }\n }, [isTyping, props.almeConfig.liveChatControls.defaultOperatorName, props.almeConfig.liveChatControls.typingMessageText, props.response, props.responseId, setIsTyping, setTypingId, typingId, typingMessage]);\n}\n\ninterface AlmeLiveChatControlsProps extends WithStyles {\n almeConfig: AlmeLiveChatControlsConfig;\n isLiveChatConnected: boolean;\n onEndChat: () => void;\n setIsDialogOpen: (isOpen: boolean) => void;\n responseId: number;\n response?: LiveChatNotificationResponseNext | AlmeResponseNext;\n focusProps?: FocusComponentProps;\n isDialogOpen?: boolean;\n}\n\nconst AlmeLiveChatControls: React.FC = (props: AlmeLiveChatControlsProps) => {\n const classes = useStyles();\n const [isTyping, setIsTyping] = useState(false);\n const [typingId, setTypingId] = useState(0);\n const typingMessage = useRef('');\n const [showDialog, setShowDialog] = useState(false);\n const [shouldFocus, setShouldFocus] = useState(false);\n const closeRef = useRef(null);\n\n HandleIsDialogOpen(props, shouldFocus, closeRef, setShouldFocus);\n\n const handleEndChatClick = () => {\n setShowDialog(true);\n };\n\n const handleCancelDialogClick = () => {\n setShowDialog(false);\n setShouldFocus(true);\n };\n\n useEffect(() => {\n let timer: NodeJS.Timeout | null = null;\n if (isTyping) {\n timer = setTimeout(() => {\n setIsTyping(false);\n }, props.almeConfig.liveChatControls.typingMessageDelay);\n }\n\n return () => {\n if (timer !== null) {\n clearTimeout(timer);\n }\n };\n }, [isTyping, props.almeConfig.liveChatControls.typingMessageDelay]);\n\n OperatorTyping(props, typingMessage, isTyping, typingId, setIsTyping, setTypingId);\n\n if (!props.isLiveChatConnected) {\n return null;\n }\n\n return (\n \n \n {isTyping &&\n \n {typingMessage.current}...\n \n }\n \n \n );\n};\n\nexport default withStyles(liveChatControlsStyles)(AlmeLiveChatControls);\n\nexport function OnEndChat(apiUtils: React.MutableRefObject): () => void {\n return useCallback((): void => {\n /**\n * If the user is in a live chat session, whether waiting in a queue or actively talking with one or more operators,\n * this event will terminate the session. The user will subsequently be deescalated back to the IVA.\n */\n void apiUtils.current.submitReportingEvent('TerminateSession', 'User');\n }, [apiUtils]);\n}\n","import { Theme } from '@material-ui/core';\n\n// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\nexport default function componentWithStyles({ palette, spacing, typography }: Theme) {\n return {\n root: {},\n typingNotification: {\n '& span': {\n animationName: `$ellipsis`,\n animationDuration: '1.4s',\n animationIterationCount: 'infinite',\n animationFillMode: 'both',\n },\n '& span:nth-child(2)': {\n animationDelay: '.2s',\n },\n '& span:nth-child(3)': {\n animationDelay: '.4s',\n },\n },\n '@keyframes ellipsis': {\n /**\n * At the start of the animation the dot\n * has an opacity of .2\n */\n '0%': {\n opacity: .2,\n },\n /**\n * At 20% the dot is fully visible and\n * then fades out slowly\n */\n '20%': {\n opacity: 1,\n },\n /**\n * Until it reaches an opacity of .2 and\n * the animation can start again\n */\n '100%': {\n opacity: .2,\n },\n },\n };\n}","import { AlmeSessionData } from \"@alme/session-ts/AlmeSessionData\";\nimport { AlmeSessionDataLoaderBase } from \"@alme/session-ts/AlmeSessionDataLoaderBase\";\nimport { AlmeSessionDataUtils } from \"@alme/session-ts/AlmeSessionDataUtils\";\nimport { StorageLoaderBase } from \"@alme/storage-ts/StorageLoaderBase\";\nimport { AlmeResponseNext } from \"@alme/types/response/AlmeResponse\";\nimport { useCallback, useEffect } from \"react\";\nimport { AlmeSavedAppState } from \"../components/AlmeApp/AlmeAppState\";\n\nimport defaultLogger from '../Logger';\n\nexport function loadAlmeSessionData(sessionDataLoader: AlmeSessionDataLoaderBase): AlmeSessionData {\n let sessionData: AlmeSessionData | null = null;\n try {\n sessionData = sessionDataLoader.GetAlmeSessionData();\n } catch (ex) {\n defaultLogger.error(ex);\n }\n if (!sessionData) {\n sessionData = AlmeSessionDataUtils.CreateAlmeSessionData();\n }\n return sessionData;\n}\n\nexport function useSaveAlmeSessionData(almeSessionData: AlmeSessionData, sessionDataLoader: AlmeSessionDataLoaderBase): void {\n useEffect(() => {\n let sessionData: AlmeSessionData | null = null;\n try {\n sessionData = sessionDataLoader.GetAlmeSessionData();\n } catch (ex) {\n defaultLogger.error(ex);\n }\n if (sessionData && (sessionData?.userId !== almeSessionData.userId ||\n sessionData?.sessionId !== almeSessionData?.sessionId ||\n sessionData.lastActivityTime !== almeSessionData?.lastActivityTime)\n ) {\n try {\n sessionDataLoader.UpdateAlmeSessionData(almeSessionData);\n } catch (ex) {\n defaultLogger.error(ex);\n }\n }\n }, [almeSessionData, sessionDataLoader]);\n}\n\nexport function useAlmeSessionDataCallback(\n almeSessionData: AlmeSessionData,\n setAlmeSessionData: (almeSessionData: AlmeSessionData) => void,\n): (response: AlmeResponseNext | AlmeResponseNext[]) => void {\n return useCallback((response: AlmeResponseNext | AlmeResponseNext[]): void => {\n let almeResponse: AlmeResponseNext;\n if (response && (response as AlmeResponseNext[]).length) {\n // get the last history item to set alme session data.\n almeResponse = (response as AlmeResponseNext[])[(response as AlmeResponseNext[]).length - 1];\n } else {\n almeResponse = response as AlmeResponseNext;\n }\n\n if (almeResponse && almeResponse.userId && almeSessionData.userId !== almeResponse.userId) {\n const newSessionData: AlmeSessionData = {\n sessionId: almeSessionData.sessionId,\n userId: almeResponse.userId,\n lastActivityTime: almeSessionData.lastActivityTime,\n };\n setAlmeSessionData(newSessionData);\n }\n }, [almeSessionData.sessionId, almeSessionData.userId, almeSessionData.lastActivityTime, setAlmeSessionData]);\n}\n\nexport function useSaveLastActivityTime(\n almeSessionData: AlmeSessionData,\n setAlmeSessionData: (almeSessionData: AlmeSessionData) => void,\n): (lastActivityTime: string) => void {\n\n return useCallback((lastActivityTime) => {\n\n if (lastActivityTime !== almeSessionData.lastActivityTime) {\n setAlmeSessionData({ ...almeSessionData, lastActivityTime });\n }\n }, [almeSessionData, setAlmeSessionData]);\n}\n\nexport function useClearSessionData(\n sessionDataLoader: AlmeSessionDataLoaderBase,\n savedAppState: StorageLoaderBase,\n setAlmeSessionData: (almeSessionData: AlmeSessionData) => void,\n clearAppData: () => void,\n): () => void {\n return useCallback((): void => {\n try {\n clearAppData();\n } catch (ex) {\n defaultLogger.error(ex);\n }\n try {\n sessionDataLoader.ClearAlmeSessionData();\n } catch (ex) {\n defaultLogger.error(ex);\n }\n try {\n savedAppState.ClearStorage();\n } catch (ex) {\n defaultLogger.error(ex);\n }\n setAlmeSessionData(AlmeSessionDataUtils.CreateAlmeSessionData());\n }, [clearAppData, savedAppState, sessionDataLoader, setAlmeSessionData]);\n}\n","import { AlmeResponseNext } from \"@alme/types/response/AlmeResponse\";\nimport { AlmeApiUtils } from \"../../api/AlmeApiUtils\";\nimport { AlmeAppState } from \"./AlmeAppState\";\nimport defaultLogger from '../../Logger';\nimport { Dispatch, SetStateAction } from \"react\";\nimport { CookieLoader } from \"@alme/storage-ts/CookieLoader\";\nimport { ChangeTenantAppEvent } from \"../ResponseActions/ChangeTenantConfirmation/ChangeTenantConfirmation\";\nimport { processLiveChatStartAction } from \"../ResponseActions/AlmeLiveChatStart/AlmeLiveChatStartUtils\";\nimport { processLiveChatEndAction } from \"../ResponseActions/AlmeLiveChatEnd/AlmeLiveChatEndUtils\";\n\nexport async function loadAlmeHistory(almeApiUtils: AlmeApiUtils, almeState: AlmeAppState, setShouldDisplayTimeout?: Dispatch>): Promise {\n let curHistory: AlmeResponseNext[] = [];\n const changeTenantAppEvent = new CookieLoader('changeTenantAppEvent');\n const inputToResend = new CookieLoader('inputToResend');\n\n try {\n curHistory = await almeApiUtils.loadAlmeHistory();\n if (!curHistory.length) {\n try {\n curHistory = [];\n const params = {\n launchPointName: almeState.launchData?.message?.launchPointName,\n launchParameter: almeState.launchData?.message?.launchParameter,\n launchType: almeState.launchData?.message?.launchType,\n };\n const welcomeResponse = await almeApiUtils.sendAppEvent('WelcomeUser', 'Internal', params);\n if (welcomeResponse) {\n curHistory.push(welcomeResponse);\n if (setShouldDisplayTimeout)\n setShouldDisplayTimeout(true);\n }\n } catch (error) {\n defaultLogger.error(error);\n }\n } else {\n curHistory = await handleTenantChange(almeApiUtils, curHistory, changeTenantAppEvent, inputToResend);\n curHistory = disableAutoNav(curHistory);\n }\n } catch (error) {\n defaultLogger.error(error);\n }\n\n return curHistory;\n}\n\nexport function processLastResponse(almeState: AlmeAppState, setAlmeStateWrapper: (state: AlmeAppState) => void): AlmeResponseNext | undefined {\n let lastResponse;\n if (almeState?.history?.length) {\n lastResponse = almeState.history[almeState.history.length - 1];\n }\n\n if (lastResponse) {\n // Process last history item item for LiveChatStart Response Action.\n processLiveChatStartAction(lastResponse, almeState, setAlmeStateWrapper);\n processLiveChatEndAction(lastResponse, almeState, setAlmeStateWrapper);\n }\n return lastResponse;\n}\n\nfunction disableAutoNav(curHistory: AlmeResponseNext[]) {\n // Disable all auto navigation on history load.\n for (const response of curHistory) {\n if (response?.navUrl) {\n response.navUrl.AutoNavigationType = 'None';\n }\n }\n return curHistory;\n}\n\nasync function handleTenantChange(almeApiUtils: AlmeApiUtils, curHistory: AlmeResponseNext[], changeTenantAppEvent: CookieLoader, inputToResend: CookieLoader) {\n const tenantAppEvent = changeTenantAppEvent.GetStorage();\n const inputResend = inputToResend.GetStorage();\n\n if (tenantAppEvent && inputResend) {\n const tenantResponse = await almeApiUtils.sendAppEvent(tenantAppEvent.Name, 'Internal', tenantAppEvent.parameters);\n if (tenantResponse) {\n curHistory.push(tenantResponse);\n }\n changeTenantAppEvent.ClearStorage();\n const resendResponse = await almeApiUtils.sendAlmeInput(inputResend);\n if (resendResponse) {\n curHistory.push(resendResponse);\n }\n inputToResend.ClearStorage();\n }\n\n return curHistory;\n}\n","import { ResponseAction } from \"@alme/types/response/ResponseAction\";\n\nimport { AlmeResponseNext } from '@alme/types/response/AlmeResponse';\nimport { AlmeAppState } from \"../../AlmeApp/AlmeAppState\";\n\nexport interface LiveChatStartAction extends ResponseAction {\n Name: \"LiveChatStart\";\n}\n\nexport function processLiveChatStartAction(response: AlmeResponseNext, almeState: AlmeAppState, setAlmeState: (almeState: AlmeAppState) => void): void {\n if (!almeState.isLiveChatConnected && response?.responseActions) {\n for (const action of response.responseActions) {\n if (action.Name === \"LiveChatStart\") {\n setAlmeState({\n ...almeState,\n isLiveChatConnected: true,\n } as AlmeAppState);\n return;\n }\n }\n }\n}\n","import { ResponseAction } from \"@alme/types/response/ResponseAction\";\n\nimport { AlmeResponseNext } from '@alme/types/response/AlmeResponse';\nimport { AlmeAppState } from \"../../AlmeApp/AlmeAppState\";\n\nexport interface LiveChatEndAction extends ResponseAction {\n Name: \"LiveChatEnd\";\n}\n\nexport function processLiveChatEndAction(response: AlmeResponseNext, almeState: AlmeAppState, setAlmeState: (almeState: AlmeAppState) => void): void {\n if (almeState.isLiveChatConnected && response?.responseActions) {\n for (const action of response.responseActions) {\n if (action.Name === \"LiveChatEnd\") {\n setAlmeState({\n ...almeState,\n isLiveChatConnected: false,\n } as AlmeAppState);\n return;\n }\n }\n }\n}\n","import { isLiveChatResponse } from \"@alme/api-ts/Utils\";\nimport { ConfigurationKeyValuePair } from \"@alme/types/response/ConfigurationResponse\";\nimport { LiveChatNotificationResponseNext } from \"@alme/types/response/LiveChatNotificationResponse\";\nimport { Dispatch, SetStateAction, useEffect } from \"react\";\nimport { AlmeApiUtils } from \"../api/AlmeApiUtils\";\nimport { AlmeAppState } from \"../components/AlmeApp/AlmeAppState\";\nimport { loadAlmeHistory } from \"../components/AlmeApp/AlmeAppUtils\";\nimport { AlmeResponseNext } from '@alme/types/response/AlmeResponse';\nimport defaultLogger from '../Logger';\nimport { AlmeEnvironmentConfig } from \"../api/AlmeEnvironmentConfig\";\nimport { AlmeSessionUtils } from \"../api/AlmeSessionUtils\";\n\nexport function useAlmeInitialization(apiUtils: React.MutableRefObject,\n almeState: AlmeAppState,\n isInitialized: React.MutableRefObject,\n setAlmeStateWrapper: (state: AlmeAppState) => void,\n personaMapped: boolean,\n scrollToResponse: React.MutableRefObject,\n shouldScrollOnNav?: boolean,\n setShouldDisplayTimeout?: Dispatch>,\n isIvaProLaunch?: React.MutableRefObject,\n almeSessionUtils?: React.MutableRefObject): void {\n useEffect(() => {\n /**\n * Alme app initialization sequence.\n * 1. Send a GetConfiguration Alme request convert from Key/value pair to property/value and save the results in the almeState.config.\n * 2. Send a GetHistory Alme request and if history exists:\n * - Disable all auto navigation by setting response.navUrl.AutoNavigationType = 'None'.\n * - Save the history in the almeState.history.\n * 3. Else if no history exists Send a \"WelcomeUser\" AppEvent request and save the response in the almeState.history.\n */\n const initializeAlmeApp = async (): Promise => {\n // Example setting config settings.\n await setAlmeConfigSettings(apiUtils);\n\n const curHistory = await loadAlmeHistory(apiUtils.current, almeState, setShouldDisplayTimeout);\n\n await handleLaunchQuestion(almeState, apiUtils, curHistory);\n\n let isLiveChatConnected = false;\n if (curHistory?.length) {\n const lastHistoryItem = curHistory[curHistory.length - 1] as LiveChatNotificationResponseNext;\n isLiveChatConnected = isLiveChatResponse(lastHistoryItem);\n }\n\n const chatResponses = curHistory.filter((value) => {\n return isLiveChatResponse(value) && typeof value?.extensionData?.MessageIndex === 'number';\n });\n let liveChatIndex = -1;\n\n if (chatResponses.length) {\n const lastChatResponse = chatResponses[chatResponses.length - 1] as LiveChatNotificationResponseNext;\n liveChatIndex = lastChatResponse.extensionData.MessageIndex;\n }\n\n if (shouldScrollOnNav) {\n scrollToResponse.current = true;\n }\n\n setAlmeStateWrapper({\n ...almeState,\n isOpen: true,\n history: curHistory,\n launchQuestion: undefined,\n isLiveChatConnected,\n liveChatIndex,\n launchData: undefined,\n } as AlmeAppState);\n\n if (almeSessionUtils?.current)\n almeSessionUtils?.current.setOnSessionTimeout();\n };\n\n if (isInitialized.current || !personaMapped || almeState.isMinimized || isIvaProLaunch?.current) {\n return;\n } else {\n isInitialized.current = true;\n void initializeAlmeApp();\n }\n }, [almeState, apiUtils, isInitialized, setAlmeStateWrapper, setShouldDisplayTimeout, personaMapped, isIvaProLaunch, almeSessionUtils, shouldScrollOnNav, scrollToResponse]);\n}\n\nasync function setAlmeConfigSettings(apiUtils: React.MutableRefObject): Promise {\n try {\n const almeConfigs = await apiUtils.current.getAlmeConfiguration();\n if (almeConfigs) {\n const configSettings = almeConfigs.configurationSettings.reduce>((prev: Record, x: ConfigurationKeyValuePair) => {\n return { ...prev, [x.Key]: x.Value };\n }, {});\n AlmeEnvironmentConfig.getInstance().setConfig(configSettings);\n }\n } catch (error) {\n defaultLogger.error(error);\n }\n}\n\nasync function handleLaunchQuestion(almeState: AlmeAppState, apiUtils: React.MutableRefObject, curHistory: AlmeResponseNext[]): Promise {\n if (almeState.launchQuestion) {\n try {\n const questionResponse = await apiUtils.current.sendAlmeInput(almeState.launchQuestion);\n curHistory.push(questionResponse);\n } catch (ex) {\n defaultLogger.error(ex);\n }\n }\n}\n","import { useEffect, useRef } from \"react\";\n\nfunction useRecursiveTimeout(\n callback: () => Promise | (() => void),\n delay: number | null,\n): void {\n const savedCallback = useRef(callback);\n\n // Remember the latest callback.\n useEffect(() => {\n savedCallback.current = callback;\n }, [callback]);\n\n // Set up the timeout loop.\n useEffect(() => {\n let id: NodeJS.Timeout;\n let cancel = false;\n function tick() {\n const ret = savedCallback.current();\n\n if (ret instanceof Promise) {\n void ret.then(() => {\n if (delay !== null && !cancel) {\n id = setTimeout(tick, delay);\n }\n });\n } else if (delay !== null && !cancel) {\n id = setTimeout(tick, delay);\n }\n }\n if (delay !== null) {\n id = setTimeout(tick, delay);\n return () => {\n if (id) { clearTimeout(id); }\n cancel = true;\n };\n }\n }, [delay]);\n}\n\nexport default useRecursiveTimeout;","import { NotificationResponseNext } from \"@alme/types/response/NotificationResponse\";\nimport { AlmeEnvironmentConfig } from \"./AlmeEnvironmentConfig\";\nimport { AlmeSessionData } from \"@alme/session-ts/AlmeSessionData\";\nimport { AlmeAppState } from \"../components/AlmeApp/AlmeAppState\";\nimport { createIntlCache, createIntl } from \"react-intl\";\nimport { getCurrentIntlConfig } from \"../utils/PersonaUtils\";\n\n\nfunction getSessionExpiredResponse(entryId: string): NotificationResponseNext {\n const currentIntlConfig = getCurrentIntlConfig();\n const cache = createIntlCache();\n const intl = createIntl({ locale: currentIntlConfig.locale, messages: currentIntlConfig.messages }, cache);\n\n return {\n id: \"\",\n userId: \"\",\n responseRevision: 0,\n maskedInput: \"\",\n text: intl.formatMessage({\n id: \"sessionUtils.timeoutMessage\",\n defaultMessage: 'Default session timeout message',\n description: 'AlmeSessionUtils session timeout message',\n }),\n navUrl: null,\n responseActions: [],\n displayLinkCollection: null,\n deferredAppCall: null,\n isReverted: false,\n extensionData: {\n MessageIndex: 0,\n entryId,\n isSessionTimeoutResponse: true,\n },\n };\n}\n\n\nexport class AlmeSessionUtils {\n\n private almeSessionData: AlmeSessionData;\n private almeState: AlmeAppState;\n private getAlmeSessionFromStorage: () => AlmeSessionData;\n private clearSessionData: () => void;\n private setAlmeState: (state: AlmeAppState) => void;\n private saveLastActivityTime: (lastActivityTime: string) => void;\n public sessionTimer: NodeJS.Timeout | undefined;\n\n\n constructor(\n almeSessionData: AlmeSessionData,\n almeState: AlmeAppState,\n getAlmeSessionFromStorage: () => AlmeSessionData,\n clearSessionData: () => void,\n setAlmeState: (state: AlmeAppState) => void,\n saveLastActivityTime: (lastActivityTime: string) => void,\n sessionTimer: NodeJS.Timeout | undefined = undefined,\n ) {\n this.almeSessionData = almeSessionData;\n this.almeState = almeState;\n this.getAlmeSessionFromStorage = getAlmeSessionFromStorage;\n this.clearSessionData = clearSessionData;\n this.setAlmeState = setAlmeState;\n this.saveLastActivityTime = saveLastActivityTime;\n this.sessionTimer = sessionTimer;\n }\n\n startSessionTimer(): void {\n const sessionLength = this.minutestoMs(Number(AlmeEnvironmentConfig.getInstance().getConfig().ClientSessionTimeout));\n\n if (!sessionLength || this.sessionTimer) { return; }\n\n const remainingTime = this.getRemainingTime(sessionLength);\n\n this.sessionTimer = setTimeout(() => { this.onSessionTimeout(this.almeState.isOpen, this.almeState.isMinimized); }, remainingTime);\n }\n\n refreshSessionTimer(): void {\n const sessionLength = this.minutestoMs(Number(AlmeEnvironmentConfig.getInstance().getConfig().ClientSessionTimeout));\n\n if (!sessionLength) { return; }\n\n this.saveLastActivityTime(new Date().toISOString());\n\n if (this.sessionTimer) { clearTimeout(this.sessionTimer); }\n\n this.sessionTimer = setTimeout(() => { this.onSessionTimeout(this.almeState.isOpen, this.almeState.isMinimized); }, sessionLength);\n }\n\n setOnSessionTimeout(): void {\n const sessionLength = this.minutestoMs(Number(AlmeEnvironmentConfig.getInstance().getConfig().ClientSessionTimeout));\n\n if (!sessionLength) { return; }\n\n const remainingTime = this.getRemainingTime(sessionLength);\n\n if (this.sessionTimer) { clearTimeout(this.sessionTimer); }\n\n this.sessionTimer = setTimeout(() => { this.onSessionTimeout(this.almeState.isOpen, this.almeState.isMinimized); }, remainingTime);\n }\n\n private onSessionTimeout(isOpen?: boolean, isMinimized?: boolean): void {\n\n const almeSessionData = this.getAlmeSessionFromStorage();\n\n const sessionLength = this.minutestoMs(Number(AlmeEnvironmentConfig.getInstance().getConfig().ClientSessionTimeout) || 0);\n const currentTime = new Date().getTime();\n const lastActivityTime = new Date(almeSessionData.lastActivityTime ?? (currentTime - sessionLength)).getTime();\n const inactivityTime = currentTime - lastActivityTime;\n\n if (inactivityTime >= sessionLength) {\n const lastResponse = this.almeState.history.slice(-1)[0];\n const lastEntryId = lastResponse?.extensionData?.entryId as string ?? \"\";\n\n this.clearSessionData();\n this.setAlmeState({\n ...this.almeState,\n isMinimized,\n isOpen,\n history: [getSessionExpiredResponse(lastEntryId)],\n });\n }\n else {\n const remainingTime = sessionLength - inactivityTime;\n if (this.sessionTimer) { clearTimeout(this.sessionTimer); }\n\n this.sessionTimer = setTimeout(() => { this.onSessionTimeout(this.almeState.isOpen, this.almeState.isMinimized); }, remainingTime);\n }\n }\n\n private minutestoMs(minutes: number): number {\n return minutes * 6e4;\n }\n\n private getRemainingTime(sessionLength: number): number {\n const currentTime = new Date().getTime();\n const lastActivityTime = new Date(this.almeSessionData.lastActivityTime ?? currentTime).getTime();\n const inactivityTime = currentTime - lastActivityTime;\n return (sessionLength - inactivityTime);\n }\n}\n","import React, { useState, useEffect, useRef, useMemo } from 'react';\n\nimport { AlmeAppState, AlmeSavedAppState, createChannelTypes } from './AlmeAppState';\nimport { AlmeApiUtils } from '../../api/AlmeApiUtils';\nimport { createLiveChatNotificationApi, createAlmeApi } from '../../api/AlmeApi';\n\nimport { AlmeConfig } from '../../models/AlmeConfig';\n\nimport AlmeHeader from '../AlmeHeader/AlmeHeader';\nimport AlmeHistory from '../AlmeHistory/AlmeHistory';\nimport AlmeFooter from '../AlmeFooter/AlmeFooter';\nimport { OnDisplayLinkClick } from '../AlmeResponse/AlmeResponse';\nimport AlmeLaunchPoint from '../AlmeLaunchPoint/AlmeLaunchPoint';\nimport { OnMinimizeClick } from '../AlmeMinimize/AlmeMinimize';\n\nimport { WithStyles, withStyles } from '@material-ui/core/styles';\nimport { Container, Grow } from '@material-ui/core';\n\nimport { CookieLoader } from '@alme/storage-ts/CookieLoader';\n\nimport appStyles from './AlmeApp.styles';\nimport { AlmeIvaTypeProp } from '../../models/AlmeIvaTypeMapping';\nimport { OnSurveySubmitClick } from '../ResponseActions/AlmeSurvey/AlmeSurvey';\nimport { SetIsDialogOpen } from '../AlmeDialog/AlmeDialog';\nimport { OnCtaClickCallback } from '../ResponseActions/AlmeCtaButton/AlmeCtaButton';\n\n\nimport { ClientCommunicator } from '@alme/post-message-api/ClientCommunicator';\nimport AlmeLiveChatControls, { OnEndChat } from '../LiveChat/AlmeLiveChatControls/AlmeLiveChatControls';\nimport { OnSendTypingEvent, OnSubmitAlmeInput } from '../AlmeInput/AlmeInput';\nimport { useLaunchIvaCallback } from '../../hooks/useLaunchIvaCallback';\nimport { useResetIvaCallback } from '../../hooks/useResetIvaCallback';\nimport { UseLiveChatConnected } from '../../hooks/useLiveChatConnected';\n\nimport defaultLogger from '../../Logger';\n\nimport { CookieAlmeSessionDataLoader, almeSessionCookieName } from '@alme/session-ts/CookieAlmeSessionDataLoader';\nimport { AlmeSessionData } from '@alme/session-ts/AlmeSessionData';\nimport { loadAlmeSessionData, useAlmeSessionDataCallback, useClearSessionData, useSaveAlmeSessionData, useSaveLastActivityTime } from '../../hooks/useAlmeSessionData';\nimport { loadAlmeHistory, processLastResponse } from './AlmeAppUtils';\nimport { useAlmeInitialization } from '../../hooks/useAlmeInitialization';\nimport { useSetDefaultLoggingLevel } from '../../hooks/useSetDefaultLoggingLevel';\nimport { useAlmeMessageListener } from '../../hooks/useAlmeMessageListener';\nimport { useSyncLastEntry } from '../../hooks/useSyncLastEntry';\nimport { usePollForNotifications } from '../../hooks/usePollForNotifications';\nimport { usePollForChat } from '../../hooks/usePollForChat';\nimport { useInlineIvaStyles } from '../../hooks/useInlineIvaStyles';\nimport { LiveChatNotificationApiSettings } from '@alme/api-ts/LiveChat/LiveChatNotificationApiSettings';\nimport { AlmeSessionUtils } from '../../api/AlmeSessionUtils';\nimport { useSetAlmeStateCallback } from '../../hooks/useSetAlmeStateCallback';\n\nconst sessionLoader = new CookieLoader(almeSessionCookieName);\nconst sessionDataLoader = new CookieAlmeSessionDataLoader(sessionLoader);\nconst savedAppState = new CookieLoader('almeUiState');\nconst clientCommunicator = new ClientCommunicator();\n\nfunction getCurrentAppState(): AlmeSavedAppState | null {\n let currentAppState: AlmeSavedAppState | null = null;\n try {\n currentAppState = savedAppState.GetStorage();\n } catch (ex) {\n defaultLogger.error(ex);\n }\n return currentAppState;\n}\n\ninterface AlmeAppProps extends WithStyles {\n ivaType?: AlmeIvaTypeProp;\n almeConfig: AlmeConfig;\n verintAccessKey?: React.MutableRefObject;\n apiSettings?: LiveChatNotificationApiSettings;\n personaMapped: React.MutableRefObject;\n isIvaProLaunch?: React.MutableRefObject;\n clearAppData: () => void;\n}\n\nconst AlmeApp: React.FC = (props: AlmeAppProps) => {\n const currentAppState = getCurrentAppState();\n const initialAlmeState: AlmeAppState = {\n history: [],\n isOpen: currentAppState?.isOpen || props.ivaType === 'popup',\n isMinimized: currentAppState?.isMinimized || false,\n liveChatIndex: -1,\n isLiveChatConnected: false,\n agentChannel: createChannelTypes(),\n };\n const [almeState, setAlmeState] = useState(initialAlmeState);\n const scrollToResponse = useRef(false);\n const [pulse, setPulse] = useState(false);\n const isInitialized = useRef(false);\n const [almeConfig, setAlmeConfig] = useState(props.almeConfig);\n const [almeSessionData, setAlmeSessionData] = useState(loadAlmeSessionData(sessionDataLoader));\n const sessionUtils = useRef();\n const [shouldDisplayTimeout, setShouldDisplayTimeout] = useState(false);\n // Prevents iva from opening with default skinning on navigation\n useEffect(() => {\n if (almeState.isOpen) {\n setAlmeStateWrapper({\n ...almeState,\n isOpen: false,\n });\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const setAlmeStateWrapper = useSetAlmeStateCallback(setAlmeState, savedAppState);\n const getAlmeSessionFromStorage = () => loadAlmeSessionData(sessionDataLoader);\n\n useMemo(() => {\n setAlmeConfig(props.almeConfig);\n }, [props.almeConfig]);\n\n const onUpdateAlmeSessionData = useAlmeSessionDataCallback(almeSessionData, setAlmeSessionData);\n const clearSessionData = useClearSessionData(sessionDataLoader, savedAppState, setAlmeSessionData, props.clearAppData);\n const clearSessionWithoutTenant = useClearSessionData(sessionDataLoader, savedAppState, setAlmeSessionData, props.clearAppData);\n const saveActivityTIme = useSaveLastActivityTime(almeSessionData, setAlmeSessionData);\n\n useEffect(() => {\n sessionUtils.current = new AlmeSessionUtils(almeSessionData, almeState, getAlmeSessionFromStorage,\n clearSessionWithoutTenant, setAlmeStateWrapper, saveActivityTIme, sessionUtils.current?.sessionTimer);\n\n }, [almeSessionData, almeState, clearSessionWithoutTenant, setAlmeStateWrapper, saveActivityTIme]);\n\n const almeApi = createAlmeApi(props.apiSettings);\n const liveChatNotificationApi = createLiveChatNotificationApi(props.apiSettings);\n\n const apiUtils = useRef(new AlmeApiUtils(almeState, setAlmeStateWrapper, almeSessionData, sessionUtils.current, onUpdateAlmeSessionData, almeConfig, almeApi, liveChatNotificationApi, props.verintAccessKey?.current));\n useEffect(() => {\n const apiConfig = {\n apiUtils: almeConfig.apiUtils,\n };\n\n apiUtils.current = new AlmeApiUtils(almeState, setAlmeStateWrapper, almeSessionData, sessionUtils.current,\n onUpdateAlmeSessionData, apiConfig, almeApi, liveChatNotificationApi, props.verintAccessKey?.current);\n }, [almeState, almeConfig.apiUtils, setAlmeStateWrapper, almeSessionData, onUpdateAlmeSessionData, props.verintAccessKey, almeApi, liveChatNotificationApi]);\n\n useSetDefaultLoggingLevel();\n useSaveAlmeSessionData(almeSessionData, sessionDataLoader);\n UseLiveChatConnected(almeState.isLiveChatConnected, almeConfig, setAlmeConfig);\n useAlmeInitialization(apiUtils, almeState, isInitialized, setAlmeStateWrapper, props.personaMapped.current, scrollToResponse, props.almeConfig.history.shouldScrollOnNav, setShouldDisplayTimeout, props.isIvaProLaunch, sessionUtils);\n useInlineIvaStyles(almeState.isOpen, props.ivaType);\n\n const onDisplayLinkClick = OnDisplayLinkClick(apiUtils, scrollToResponse);\n const onSubmitAlmeInput = OnSubmitAlmeInput(apiUtils, scrollToResponse);\n const onMinimizeClick = OnMinimizeClick(almeState, setAlmeStateWrapper, apiUtils);\n const setIsDialogOpen = SetIsDialogOpen(almeState, setAlmeStateWrapper);\n const onSurveySubmitClick = OnSurveySubmitClick(apiUtils);\n const onCtaClick = OnCtaClickCallback(apiUtils);\n const onEndChat = OnEndChat(apiUtils);\n const onSendTypingEvent = OnSendTypingEvent(apiUtils);\n const onResetIva = useResetIvaCallback(almeState, apiUtils, setAlmeState, clearSessionData, isInitialized, props.personaMapped, props.verintAccessKey);\n const onLaunchIva = useLaunchIvaCallback(almeState, isInitialized, setAlmeStateWrapper, apiUtils, setPulse, scrollToResponse, props.isIvaProLaunch);\n\n useAlmeMessageListener(clientCommunicator, onLaunchIva);\n usePollForNotifications(almeConfig, isInitialized, almeState, apiUtils);\n usePollForChat(almeConfig, isInitialized, almeState, apiUtils);\n useSyncLastEntry(almeState, apiUtils, almeConfig, setAlmeStateWrapper, getCurrentAppState, loadAlmeHistory);\n\n useEffect(() => {\n if (almeState.isMinimized)\n sessionUtils.current?.setOnSessionTimeout();\n }, [almeState.isMinimized]);\n\n useEffect(() => {\n if (window.top)\n window.top.postMessage({ ivaAvailability: true }, \"*\");\n }, []);\n\n const hasHistory: boolean = almeState.history.length > 0;\n const tabIndex = almeState.isDialogOpen ? -1 : 0;\n const disablePulse = () => {\n if (pulse) {\n setPulse(false);\n }\n };\n\n const lastResponse = processLastResponse(almeState, setAlmeStateWrapper);\n\n return (\n <>\n \n \n \n \n \n \n \n \n \n >\n );\n};\n\nexport default withStyles(appStyles)(AlmeApp);\n","import { useCallback } from \"react\";\nimport { EntryExtensionData } from '@alme/types/response/EntryExtensionData';\nimport defaultLogger from '../Logger';\nimport { AlmeAppState, AlmeSavedAppState } from \"../components/AlmeApp/AlmeAppState\";\nimport { CookieLoader } from \"@alme/storage-ts/CookieLoader\";\n\nexport function useSetAlmeStateCallback(\n setAlmeState: (state: AlmeAppState) => void,\n savedAppState: CookieLoader,\n): (state: AlmeAppState) => void {\n return useCallback((state: AlmeAppState): void => {\n state.lastEntryId = (state.history.length &&\n (state.history[state.history.length - 1].extensionData as EntryExtensionData).entryId) || undefined;\n\n try {\n savedAppState.UpdateStorage({\n isOpen: state.isOpen,\n lastEntryId: state.lastEntryId,\n isMinimized: state.isMinimized,\n });\n } catch (ex) {\n defaultLogger.error(ex);\n }\n return setAlmeState(state);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n}","import { useEffect } from \"react\";\nimport { getRootIdByIvaType } from \"../indexConfig\";\nimport { AlmeIvaTypeProp } from \"../models/AlmeIvaTypeMapping\";\nimport defaultLogger from '../Logger';\n\nexport function useInlineIvaStyles(\n isOpen?: boolean,\n ivaType?: AlmeIvaTypeProp,\n): void {\n useEffect(() => {\n const ivaRootId = getRootIdByIvaType(ivaType);\n const ivaRootElement = document.getElementById(ivaRootId);\n if (!ivaRootElement) {\n defaultLogger.warn('Failed to get IVA root element: ');\n return;\n }\n\n if (!ivaType || ivaType !== 'inline') {\n ivaRootElement.style.display = '';\n } else {\n ivaRootElement.style.display = isOpen ? 'block' : 'none';\n }\n }, [isOpen, ivaType]);\n}","import {useEffect} from \"react\";\nimport defaultLogger from \"../Logger\";\nimport { AlmeEnvironmentConfig } from \"../api/AlmeEnvironmentConfig\";\n\nexport function useSetDefaultLoggingLevel(): void {\n const config = AlmeEnvironmentConfig.getInstance().getConfig();\n useEffect(() => {\n if (config && config.UiLogLevel && typeof config.UiLogLevel === 'string') {\n try {\n defaultLogger.loggingLevel = parseInt(config.UiLogLevel);\n } catch (ex) {\n // do nothing.\n }\n }\n }, [config]);\n}\n","import { useEffect } from \"react\";\nimport { AlmeConfig } from \"../models/AlmeConfig\";\n\nexport function UseLiveChatConnected(isLiveChatConnected: boolean, almeConfig: AlmeConfig, setAlmeConfig: (almeConfig: AlmeConfig) => void): void {\n useEffect(() => {\n if (isLiveChatConnected && !almeConfig.notificationApi.pollForLiveChat) {\n setAlmeConfig({\n ...almeConfig,\n notificationApi: {\n ...almeConfig.notificationApi,\n pollForLiveChat: true,\n },\n });\n } else if (!isLiveChatConnected && almeConfig.notificationApi.pollForLiveChat) {\n setAlmeConfig({\n ...almeConfig,\n notificationApi: {\n ...almeConfig.notificationApi,\n pollForLiveChat: false,\n },\n });\n }\n\n }, [almeConfig, isLiveChatConnected, setAlmeConfig]);\n}","import React, { useCallback } from 'react';\nimport { AlmeAppState, createChannelTypes } from '../components/AlmeApp/AlmeAppState';\nimport { AlmeApiUtils } from \"../api/AlmeApiUtils\";\n\nexport function useResetIvaCallback(\n almeState: AlmeAppState,\n apiUtils: React.MutableRefObject,\n setAlmeState: (state: AlmeAppState) => void,\n clearSessionData: () => void,\n isInitialized: React.MutableRefObject,\n personaMapped: React.MutableRefObject,\n verintAccessKey?: React.MutableRefObject,\n isIvaProLaunch?: React.MutableRefObject,\n): (isOpen: boolean, sessionEndReason: string, shouldResetIsInitialized?: boolean, resetSessionData?: boolean) => Promise {\n return useCallback(async (isOpen: boolean, sessionEndReason: string, shouldResetIsInitialized?: boolean, resetSessionData = true) => {\n await apiUtils.current.submitReportingEvent('SessionEnd', sessionEndReason);\n if (resetSessionData) { clearSessionData(); }\n const newState: AlmeAppState = {\n ...almeState,\n isOpen,\n history: [],\n liveChatIndex: -1,\n isLiveChatConnected: false,\n agentChannel: createChannelTypes(),\n };\n if (almeState.isDialogOpen) {\n newState.isDialogOpen = false;\n }\n personaMapped.current = false;\n if (verintAccessKey)\n verintAccessKey.current = '';\n if (isIvaProLaunch)\n isIvaProLaunch.current = false;\n setAlmeState(newState);\n if (shouldResetIsInitialized && isInitialized.current) {\n isInitialized.current = false;\n }\n }, [almeState, apiUtils, clearSessionData, isInitialized, isIvaProLaunch, personaMapped, setAlmeState, verintAccessKey]);\n}\n","import { useCallback } from \"react\";\nimport { AlmeMessageEventData } from \"@alme/post-message-api/AlmeMessageEventData\";\nimport { AlmeLaunchIvaMessageEventData } from \"@alme/post-message-api/AlmeLaunchIvaMessageEventData\";\nimport { AlmeAppState } from \"../components/AlmeApp/AlmeAppState\";\nimport { AlmeApiUtils } from \"../api/AlmeApiUtils\";\nimport defaultLogger from '../Logger';\n\nexport function useLaunchIvaCallback(almeState: AlmeAppState,\n isInitialized: React.MutableRefObject,\n setAlmeState: (state: AlmeAppState) => void,\n apiUtils: React.MutableRefObject,\n setPulse: (value: boolean) => void,\n scrollToResponse: { current: boolean },\n isIvaProLaunch?: React.MutableRefObject,\n): (payload: AlmeMessageEventData) => void {\n // eslint-disable-next-line sonarjs/cognitive-complexity\n return useCallback((payload: AlmeMessageEventData): void => {\n const launchPayload = payload as AlmeLaunchIvaMessageEventData;\n try {\n if (typeof launchPayload?.message?.type === \"string\" && launchPayload?.message?.type?.toLowerCase() === 'launchiva') {\n defaultLogger.log('useLaunchIvaCallback: ', launchPayload);\n const launchQuestion = launchPayload?.message?.launchQuestion?.trim();\n if (!almeState.isOpen) {\n if (!isInitialized.current) {\n // if not open and not initialized add this to state to happen on init.\n scrollToResponse.current = true;\n if (isIvaProLaunch)\n isIvaProLaunch.current = false;\n setAlmeState({\n ...almeState,\n launchQuestion: launchPayload.message.launchQuestion || undefined,\n launchData: launchPayload,\n isMinimized: false,\n });\n } else if (launchQuestion && typeof launchQuestion === \"string\" && almeState.isMinimized) {\n // IVA was minimized. Open and send question.\n setAlmeState({\n ...almeState,\n isOpen: true,\n isMinimized: false,\n });\n void apiUtils.current.submitAlmeInput(launchQuestion);\n } else {\n // Iva was minimized. Just open.\n setAlmeState({\n ...almeState,\n isOpen: true,\n isMinimized: false,\n });\n }\n } else if (launchQuestion) {\n // IVA is already open. Send question.\n void apiUtils.current.submitAlmeInput(launchQuestion);\n } else {\n // IVA is already open\n setPulse(true);\n }\n }\n } catch (error) {\n defaultLogger.error('Error executing launchIva callback', error);\n }\n }, [almeState, isInitialized, scrollToResponse, isIvaProLaunch, setAlmeState, apiUtils, setPulse]);\n}","import { AlmeConfig } from \"../models/AlmeConfig\";\nimport { AlmeApiUtils } from \"../api/AlmeApiUtils\";\nimport { AlmeAppState } from \"../components/AlmeApp/AlmeAppState\";\nimport defaultLogger from \"../Logger\";\nimport useRecursiveTimeout from \"./useRecursiveTimeout\";\n\nexport function usePollForNotifications(almeConfig: AlmeConfig, isInitialized: React.MutableRefObject, almeState: AlmeAppState, apiUtils: React.MutableRefObject): void {\n useRecursiveTimeout(async () => {\n const shouldPoll = almeConfig.notificationApi.pollForNotifications && isInitialized.current && almeState.isOpen;\n if (shouldPoll) {\n try {\n await apiUtils.current.getNotifications();\n } catch (ex) {\n defaultLogger.error('getNotifications error: ', ex);\n }\n }\n }, almeConfig.notificationApi.notificationInterval);\n}","import { AlmeConfig } from \"../models/AlmeConfig\";\nimport { AlmeApiUtils } from \"../api/AlmeApiUtils\";\nimport { AlmeAppState } from \"../components/AlmeApp/AlmeAppState\";\nimport defaultLogger from \"../Logger\";\nimport useRecursiveTimeout from \"./useRecursiveTimeout\";\n\nexport function usePollForChat(almeConfig: AlmeConfig, isInitialized: React.MutableRefObject, almeState: AlmeAppState, apiUtils: React.MutableRefObject): void {\n useRecursiveTimeout(async () => {\n const shouldPoll = almeConfig.notificationApi.pollForLiveChat && isInitialized.current && almeState.isOpen;\n if (shouldPoll) {\n try {\n await apiUtils.current.getLiveChatNotifications();\n } catch (ex) {\n defaultLogger.error('getLiveChatNotifications error: ', ex);\n }\n }\n }, almeState.isLiveChatConnected ? almeConfig.notificationApi.liveChatInterval.connected : almeConfig.notificationApi.liveChatInterval.disconnected);\n}","import { AlmeResponseNext } from \"@alme/types/response/AlmeResponse\";\nimport { AlmeConfig } from \"../models/AlmeConfig\";\nimport { AlmeApiUtils } from \"../api/AlmeApiUtils\";\nimport { AlmeAppState, AlmeSavedAppState } from \"../components/AlmeApp/AlmeAppState\";\nimport useRecursiveTimeout from \"./useRecursiveTimeout\";\nimport { MutableRefObject } from \"react\";\n\nexport function useSyncLastEntry(almeState: AlmeAppState,\n apiUtils: MutableRefObject,\n almeConfig: AlmeConfig,\n setAlmeStateWrapper: (state: AlmeAppState) => void,\n getCurrentAppState: () => AlmeSavedAppState | null,\n loadAlmeHistory: (almeApiUtils: AlmeApiUtils, almeState: AlmeAppState) => Promise,\n): void {\n useRecursiveTimeout(async () => {\n if (almeState.history.length) {\n const curAppState = getCurrentAppState();\n const lastResponse = almeState.history.slice(-1)[0];\n const lastEntryId = lastResponse?.extensionData?.entryId as string | undefined;\n const currentLastEntryId = curAppState?.lastEntryId;\n\n if (lastEntryId !== currentLastEntryId && lastResponse.responseRevision > 0) {\n const history = await loadAlmeHistory(apiUtils.current, almeState);\n\n setAlmeStateWrapper({\n ...almeState,\n history,\n launchQuestion: undefined,\n });\n }\n }\n }, almeConfig.lastEntryIdInterval);\n}\n","import React from 'react';\n\nimport { jssPreset, StylesProvider, ThemeProvider } from '@material-ui/core/styles';\n\nimport AlmeApp from './components/AlmeApp/AlmeApp';\nimport { createTheme } from './utils/PersonaUtils';\nimport { defaultTheme, defaultDarkTheme } from './models/AlmeTheme';\n\nimport { create } from 'jss';\n\nimport increaseSpecificity from '@alme/jss-increase-specificity-plugin';\nimport { LiveChatNotificationApiSettings } from '@alme/api-ts/LiveChat/LiveChatNotificationApiSettings';\nimport { AlmeIvaTypeProp } from './models/AlmeIvaTypeMapping';\nimport { PersonaState } from './models/AlmePersona';\n\nexport interface AppProps {\n personaState: PersonaState;\n apiSettings: LiveChatNotificationApiSettings;\n ivaType: AlmeIvaTypeProp;\n personaMapped: React.MutableRefObject;\n isIvaProLaunch?: React.MutableRefObject;\n clearAppData: () => void;\n}\n\nconst jssPlugin = increaseSpecificity();\n\nconst jss = create({\n plugins: [...jssPreset().plugins, jssPlugin],\n});\n\nconst VerintIVAUI: React.FC = (props: AppProps) => {\n\n const theme = props.personaState.themeMode === 'dark' ?\n createTheme([defaultDarkTheme, props.personaState.theme.darkTheme]) :\n createTheme([defaultTheme, props.personaState.theme.lightTheme]);\n\n return (\n \n \n \n \n \n );\n};\n\nexport default VerintIVAUI;\n","import React from 'react';\n\nimport { AlmeThemeMode } from '../../models/AlmeTheme';\n\nimport { Switch, FormControlLabel } from '@material-ui/core';\nimport { createStyles, makeStyles } from '@material-ui/core/styles';\nimport { AlmeConfig } from '../../models/AlmeConfig';\n\nconst useStyles = makeStyles(() =>\n createStyles({\n almeThemeModeToggleContainer: {\n marginLeft: '0',\n },\n }),\n);\n\ninterface AlmeThemeModeToggleProps {\n setThemeMode: (themeMode: AlmeThemeMode) => void;\n themeMode: AlmeThemeMode;\n almeConfig: AlmeConfig;\n}\n\nconst AlmeThemeModeToggle: React.FC = (props: AlmeThemeModeToggleProps) => {\n const classes = useStyles();\n\n const isDarkChecked = props.themeMode === 'dark' ? true : false;\n\n const handleChange = () => (event: React.ChangeEvent): void => {\n const themeMode: AlmeThemeMode = event.target.checked ? 'dark' : 'light';\n props.setThemeMode(themeMode);\n updateTestPageTheme(themeMode);\n };\n\n const toggleLabel = props.almeConfig.themeModeToggle.label || '';\n\n return (\n \n }\n label={toggleLabel} />\n );\n};\n\nexport default AlmeThemeModeToggle;\n\nfunction updateTestPageTheme(themeMode: AlmeThemeMode): void {\n const textColor = themeMode === 'dark' ? 'white' : 'black';\n const backgroundColor = themeMode === 'dark' ? 'black' : 'white';\n document.body.style.backgroundColor = backgroundColor;\n document.body.style.color = textColor;\n}","import React, { useState, ReactElement } from 'react';\n\nimport appThemeMain from '../../personas/mainPersona/theme';\nimport { appThemeMain as verintTheme } from '../../personas/examples/verintTheme';\nimport { appThemeMain as rainbowTheme } from '../../personas/examples/ColorExamples/rainbow';\nimport { appThemeMain as badContrastTheme } from '../../personas/examples/ColorExamples/badContrast';\nimport { appThemeMain as blackAndWhiteTheme } from '../../personas/examples/ColorExamples/blackAndWhite';\nimport { appThemeMain as shadesTheme } from '../../personas/examples/ColorExamples/shades';\nimport { appThemeMain as brushScriptTheme } from '../../personas/examples/TypographyExamples/brushScript';\nimport { appThemeMain as CourierNewSriptTheme } from '../../personas/examples/TypographyExamples/courierNewScript';\nimport { appThemeMain as adjustedTypeComfortableTheme } from '../../personas/examples/TypographyExamples/adjustedTypeSpaced';\nimport { appThemeMain as adjustedTypeCompressionTheme } from '../../personas/examples/TypographyExamples/adjustedTypeCompression';\nimport { appThemeMain as timesNewRomanScriptTheme } from '../../personas/examples/TypographyExamples/timesNewRomanScript';\nimport { appThemeMain as customShapeTheme } from '../../personas/examples/ShapeExamples/customShapes';\nimport { appThemeMain as everythingIsRoundedTheme } from '../../personas/examples/ShapeExamples/everythingIsRounded';\nimport { appThemeMain as everythingIsSharpTheme } from '../../personas/examples/ShapeExamples/everythingIsSharp';\n\nimport { FormControl, InputLabel, NativeSelect, ThemeOptions } from '@material-ui/core';\nimport { AlmeTheme } from '../../models/AlmeTheme';\n\ninterface ThemeSelectorThemeData {\n lightTheme: ThemeOptions;\n darkTheme: ThemeOptions;\n name: string;\n}\n\nconst appThemes: ThemeSelectorThemeData[] = [\n {\n lightTheme: appThemeMain.lightTheme,\n darkTheme: appThemeMain.darkTheme,\n name: 'Main',\n },\n {\n lightTheme: verintTheme.lightTheme,\n darkTheme: verintTheme.darkTheme,\n name: 'Verint',\n },\n {\n lightTheme: rainbowTheme.lightTheme,\n darkTheme: rainbowTheme.darkTheme,\n name: 'Rainbow',\n },\n {\n lightTheme: badContrastTheme.lightTheme,\n darkTheme: badContrastTheme.darkTheme,\n name: 'Bad Contrast',\n },\n {\n lightTheme: blackAndWhiteTheme.lightTheme,\n darkTheme: blackAndWhiteTheme.darkTheme,\n name: 'Black and White Theme',\n },\n {\n lightTheme: shadesTheme.lightTheme,\n darkTheme: shadesTheme.darkTheme,\n name: 'shades Theme',\n },\n {\n lightTheme: brushScriptTheme.lightTheme,\n darkTheme: brushScriptTheme.darkTheme,\n name: 'Brush Script Theme',\n },\n {\n lightTheme: CourierNewSriptTheme.lightTheme,\n darkTheme: CourierNewSriptTheme.darkTheme,\n name: 'Courier Script Theme',\n },\n {\n lightTheme: timesNewRomanScriptTheme.lightTheme,\n darkTheme: timesNewRomanScriptTheme.darkTheme,\n name: 'Times New Roman Theme',\n },\n {\n lightTheme: adjustedTypeComfortableTheme.lightTheme,\n darkTheme: adjustedTypeComfortableTheme.darkTheme,\n name: 'Spaced Text Theme',\n },\n {\n lightTheme: adjustedTypeCompressionTheme.lightTheme,\n darkTheme: adjustedTypeCompressionTheme.darkTheme,\n name: 'Compressed Text Theme',\n },\n {\n lightTheme: customShapeTheme.lightTheme,\n darkTheme: customShapeTheme.darkTheme,\n name: 'Custom Shape Theme',\n },\n {\n lightTheme: everythingIsRoundedTheme.lightTheme,\n darkTheme: everythingIsRoundedTheme.darkTheme,\n name: 'rounded corners Theme',\n },\n {\n lightTheme: everythingIsSharpTheme.lightTheme,\n darkTheme: everythingIsSharpTheme.darkTheme,\n name: 'sharp corners Theme',\n },\n];\n\ninterface AlmeThemeSelectorProps {\n setTheme: (theme: AlmeTheme) => void;\n}\n\ninterface ThemeSelectorState {\n themeNumber: number;\n name: string;\n}\n\nexport default function AlmeThemeSelector(props: AlmeThemeSelectorProps): ReactElement {\n const [state, setState] = useState({\n themeNumber: 0,\n name: appThemes[0].name,\n });\n\n const handleChange = (name: keyof typeof state) => (\n event: React.ChangeEvent<{ value: unknown }>,\n ): void => {\n setState({\n ...state,\n [name]: event.target.value,\n });\n if (name === 'themeNumber') {\n const lightTheme = appThemes[event.target.value as number].lightTheme;\n const darkTheme = appThemes[event.target.value as number].darkTheme;\n const theme = {\n lightTheme: lightTheme,\n darkTheme: darkTheme,\n } as AlmeTheme;\n\n props.setTheme(theme);\n } else {\n const currentTheme: ThemeSelectorThemeData = appThemes.find((value) => {\n return value.name === event.target.value as string;\n }) || appThemes[0];\n\n const theme = {\n lightTheme: currentTheme.lightTheme,\n darkTheme: currentTheme.darkTheme,\n } as AlmeTheme;\n\n props.setTheme(theme);\n }\n };\n\n return (\n \n Theme\n \n {\n appThemes.map((value, index) => {\n return (\n \n );\n })\n }\n \n \n );\n}","import React, { ReactElement, useState, useRef, useEffect } from 'react';\n\nimport { Theme, Accordion, AccordionDetails, AccordionSummary, Typography, Container, Avatar, Box, TextField, AccordionActions, Button, ThemeOptions } from '@material-ui/core';\nimport { createStyles, makeStyles, createMuiTheme } from '@material-ui/core/styles';\n\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\n\n// eslint-disable-next-line no-restricted-imports\nimport createPalette, { PaletteColor } from '@material-ui/core/styles/createPalette';\nimport { AlmeTheme, AlmeThemeMode } from '../../../models/AlmeTheme';\n\nconst { augmentColor } = createPalette({});\n\nconst useStyles = makeStyles((theme: Theme) => createStyles(\n {\n root: {\n width: '60%',\n },\n colorContainer: {\n boxShadow: theme.shadows[1],\n paddingTop: theme.spacing(1),\n paddingBottom: theme.spacing(1),\n '&:hover': {\n boxShadow: theme.shadows[4],\n },\n },\n mainPanel: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexDirection: 'column',\n height: '204px',\n border: `solid 1px ${theme.palette.divider}`,\n padding: theme.spacing(2),\n },\n augmentContainer: {\n display: 'flex',\n },\n augmentPanel: {\n height: '115px',\n flexGrow: 1,\n padding: theme.spacing(1),\n border: `solid 1px ${theme.palette.divider}`,\n },\n splitPanel: {\n height: '151px',\n border: `solid 1px ${theme.palette.divider}`,\n padding: theme.spacing(2),\n },\n // TODO: add divider color to controls below.\n dividerColor: {\n backgroundColor: theme.palette.divider,\n },\n },\n));\n\nfunction validateColor(color: string) {\n return /^#[0-9A-F]{3,6}$/i.test(color);\n}\n\ninterface AlmeBasicColorControlProps {\n color: string;\n useColorRef: React.RefObject;\n title: string;\n idVal: string;\n}\n\nfunction AlmeBasicColorControl(props: AlmeBasicColorControlProps): ReactElement {\n const classes = useStyles();\n\n const [color, setColor] = useState(props.color);\n\n useEffect(() => {\n setColor(props.color);\n }, [props.color]);\n\n const handleColorChange = (event: React.ChangeEvent) => {\n const newColor = event.target.value;\n if (newColor?.length && validateColor(newColor)) {\n setColor(newColor);\n }\n };\n\n const colorStyles = {\n backgroundColor: color,\n color: augmentColor({ main: color }).contrastText,\n };\n\n return (\n \n {props.title}\n \n \n );\n}\n\ninterface AlmePaletteColorControlProps {\n paletteColor: PaletteColor;\n useMainRef: React.RefObject;\n useLightRef: React.RefObject;\n useDarkRef: React.RefObject;\n title: string;\n idVal: string;\n avatarValue: string;\n}\n\nfunction AlmePaletteColorControl(props: AlmePaletteColorControlProps): ReactElement {\n const classes = useStyles();\n\n const [main, setMain] = useState(props.paletteColor.main);\n const [light, setLight] = useState(props.paletteColor.light);\n const [dark, setDark] = useState(props.paletteColor.dark);\n\n useEffect(() => {\n setMain(props.paletteColor.main);\n setLight(props.paletteColor.light);\n setDark(props.paletteColor.dark);\n }, [props.paletteColor]);\n\n const handleColorChange = (event: React.ChangeEvent) => {\n const primary = event.target.value;\n if (primary?.length && validateColor(primary)) {\n const augmented = augmentColor({ main: primary.toString() });\n setMain(primary);\n setLight(augmented.light);\n setDark(augmented.dark);\n }\n };\n\n const mainStyles = {\n backgroundColor: main,\n color: augmentColor({ main }).contrastText,\n };\n\n const lightStyles = {\n backgroundColor: light,\n color: augmentColor({ main: light }).contrastText,\n };\n\n const darkStyles = {\n backgroundColor: dark,\n color: augmentColor({ main: dark }).contrastText,\n };\n\n const avatarStyles = {\n backgroundColor: props.paletteColor.contrastText,\n color: props.paletteColor.main,\n };\n\n return (\n \n {props.title}\n \n {props.avatarValue}\n \n \n \n \n P - Light\n \n \n \n P - Dark\n \n \n \n \n );\n}\n\nfunction getAppTheme(props: AlmePaletteToolProps): Theme {\n const theme = props.themeMode === 'dark' ? props.theme.darkTheme : props.theme.lightTheme;\n return createMuiTheme(theme);\n}\n\ninterface AlmePaletteToolProps {\n theme: AlmeTheme;\n themeMode: AlmeThemeMode;\n setTheme: (theme: AlmeTheme) => void;\n}\n\nexport default function AlmePaletteTool(props: AlmePaletteToolProps): ReactElement {\n const classes = useStyles();\n\n const appTheme = getAppTheme(props);\n\n const useMainPrimaryRef = useRef(null);\n const useLightPrimaryRef = useRef(null);\n const useDarkPrimaryRef = useRef(null);\n const useMainSecondaryRef = useRef(null);\n const useLightSecondaryRef = useRef(null);\n const useDarkSecondaryRef = useRef(null);\n const useDefaultRef = useRef(null);\n const usePaperRef = useRef(null);\n\n useEffect(() => {\n useMainPrimaryRef.current && (useMainPrimaryRef.current.value = appTheme.palette.primary.main);\n useLightPrimaryRef.current && (useLightPrimaryRef.current.value = appTheme.palette.primary.light);\n useDarkPrimaryRef.current && (useDarkPrimaryRef.current.value = appTheme.palette.primary.dark);\n\n useMainSecondaryRef.current && (useMainSecondaryRef.current.value = appTheme.palette.secondary.main);\n useLightSecondaryRef.current && (useLightSecondaryRef.current.value = appTheme.palette.secondary.light);\n useDarkSecondaryRef.current && (useDarkSecondaryRef.current.value = appTheme.palette.secondary.dark);\n\n useDefaultRef.current && (useDefaultRef.current.value = appTheme.palette.background.default);\n usePaperRef.current && (usePaperRef.current.value = appTheme.palette.background.paper);\n }, [\n appTheme.palette.background.default, appTheme.palette.background.paper,\n appTheme.palette.primary.dark, appTheme.palette.primary.light, appTheme.palette.primary.main,\n appTheme.palette.secondary.dark, appTheme.palette.secondary.light, appTheme.palette.secondary.main,\n ]);\n\n const applyThemeClick = () => {\n const customTheme: ThemeOptions = {\n ...appTheme,\n palette: {\n ...appTheme.palette,\n type: props.themeMode === 'dark' ? 'dark' : 'light' as const,\n primary: {\n ...appTheme.palette.primary,\n main: useMainPrimaryRef.current?.value || appTheme.palette.primary.main,\n light: useLightPrimaryRef.current?.value || appTheme.palette.primary.light,\n dark: useDarkPrimaryRef.current?.value || appTheme.palette.primary.dark,\n },\n secondary: {\n ...appTheme.palette.secondary,\n main: useMainSecondaryRef.current?.value || appTheme.palette.secondary.main,\n light: useLightSecondaryRef.current?.value || appTheme.palette.secondary.light,\n dark: useDarkSecondaryRef.current?.value || appTheme.palette.secondary.dark,\n },\n background: {\n ...appTheme.palette.background,\n default: useDefaultRef.current?.value || appTheme.palette.background.default,\n paper: usePaperRef.current?.value || appTheme.palette.background.paper,\n },\n },\n };\n const customMuiTheme = {\n lightTheme: customTheme.palette?.type === 'light' ? customTheme : props.theme.lightTheme,\n darkTheme: customTheme.palette?.type === 'dark' ? customTheme : props.theme.darkTheme,\n } as AlmeTheme;\n\n props.setTheme(customMuiTheme);\n };\n\n return (\n \n \n }\n aria-controls=\"panel-color-tool-content\"\n id=\"panel-color-tool-header\"\n >\n Color Tool\n \n \n \n \n \n Background\n \n \n \n \n \n \n \n \n \n );\n}","import React, { ReactElement, useRef } from 'react';\n\nimport { Theme, Button, Accordion, AccordionDetails, AccordionSummary, Typography, AccordionActions, TextField } from '@material-ui/core';\nimport { createStyles, makeStyles, ThemeOptions } from '@material-ui/core/styles';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport { AlmeTheme, AlmeThemeMode } from '../../../models/AlmeTheme';\n\nconst useStyles = makeStyles(({ spacing }: Theme) =>\n createStyles({\n root: {\n width: '60%',\n },\n themeTextarea: {\n width: '100%',\n },\n }),\n);\n\ninterface AlmeThemeToolProps {\n setTheme: (theme: AlmeTheme) => void;\n theme: AlmeTheme;\n themeMode: AlmeThemeMode;\n}\n\nexport default function AlmeThemeTool(props: AlmeThemeToolProps): ReactElement {\n const classes = useStyles();\n const appTheme = props.themeMode === 'dark' ? props.theme.darkTheme : props.theme.lightTheme;\n\n const textareaValue = JSON.stringify(appTheme, null, 4);\n const useThemeTextareaRef = useRef(null);\n\n const applyThemeClick = () => {\n const themeOptions = useThemeTextareaRef.current?.value;\n if (!themeOptions) {\n return;\n }\n const customTheme = JSON.parse(themeOptions) as ThemeOptions;\n\n const customMuiTheme = {\n lightTheme: customTheme.palette?.type === 'light' ? customTheme : props.theme.lightTheme,\n darkTheme: customTheme.palette?.type === 'dark' ? customTheme : props.theme.darkTheme,\n } as AlmeTheme;\n\n props.setTheme(customMuiTheme);\n };\n\n return (\n \n
\n }\n aria-controls=\"panel-custom-theme-content\"\n id=\"panel-custom-theme-header\"\n >\n Custom Theme\n \n \n \n \n \n \n \n \n
\n );\n}","import React from 'react';\n\nimport { makeStyles, createStyles } from '@material-ui/core/styles';\n\nimport {\n Theme, Box, FormControl, FormLabel, Typography,\n FormControlLabel, Radio, RadioGroup, SvgIconProps,\n ButtonProps, TextFieldProps, TypographyProps, Container, TextField,\n} from '@material-ui/core';\n\nconst useStyles = makeStyles((theme: Theme) =>\n createStyles({\n root: {\n display: 'flex',\n flexFlow: 'row wrap',\n border: `solid 1px ${theme.palette.divider}`,\n justifyContent: 'space-around',\n },\n controlGroup: {\n display: 'flex',\n flexFlow: 'row wrap',\n boxShadow: theme.shadows[1],\n padding: theme.spacing(2),\n '&:hover': {\n boxShadow: theme.shadows[4],\n },\n },\n controls: {\n padding: theme.spacing(1),\n },\n }),\n);\n\nexport type BoolPropTypes = 'true' | 'false';\n\nexport type ControlPropTypes = ButtonProps['color'] | ButtonProps['variant'] | TextFieldProps['color'] | TextFieldProps['variant'] |\n TextFieldProps['size'] | BoolPropTypes | TypographyProps['variant'] | SvgIconProps['fontSize'] | string;\n\nexport interface ConfigControlGroups {\n name: string;\n controlGroups: (RadioButtonControl | TextControl)[];\n}\n\nexport interface RadioButtonControl {\n label: string;\n name: string;\n value: ControlPropTypes;\n changeHandler: (event: React.ChangeEvent) => void;\n radioButtons: string[];\n}\n\nexport interface TextControl {\n id: string;\n label: string;\n name: string;\n value: ControlPropTypes;\n changeHandler: (event: React.ChangeEvent) => void;\n}\n\ninterface AlmeConfigFormControlProps {\n title: string;\n configControls: ConfigControlGroups[];\n}\n\nconst AlmeConfigFormControl: React.FC = (props: AlmeConfigFormControlProps) => {\n const classes = useStyles();\n\n const isRadioControl = (control: RadioButtonControl | TextControl): boolean => {\n return (control as RadioButtonControl).radioButtons && (control as RadioButtonControl).radioButtons.length > 0;\n };\n\n const isTextControl = (control: RadioButtonControl | TextControl): boolean => {\n return !!(control as TextControl).id && !isRadioControl(control);\n };\n\n return (\n \n {props.title}\n {props.configControls.map((controls, i1) => (\n \n {controls.controlGroups.map((control, i2) => (\n