\n );\n }\n}\n\nexport default Support;\n","// Component\nimport Support from './Support';\nimport { connect } from 'react-redux';\nimport _get from 'lodash/get';\n\nimport {\n toggleSupportDrawer,\n createSupportMessage,\n updateSupportMessage,\n addAttachments,\n} from 'store/support';\n\nconst mapDispatchToProps = {\n toggleSupportDrawer,\n createSupportMessage,\n updateSupportMessage,\n addAttachments,\n};\n\nconst mapStateToProps = (state, ownProps) => {\n return {\n status: state.support.status,\n supportTicket: state.support.supportTicket,\n clientName: state.client.displayName,\n userEmail: state.authentication.person.email,\n openDrawer: state.support.supportDrawerIsOpen,\n supportEnabled: _get(state.client.settings, 'support.enabled', false),\n };\n};\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps,\n)(Support);\n","import React from 'react';\nimport { useLocation } from 'react-router-dom';\nimport queryString from 'query-string';\nimport bowser from 'bowser';\nimport Button from '@mui/material/Button';\nimport Paper from '@mui/material/Paper';\nimport Android from '@mui/icons-material/Android';\nimport Apple from 'components/Icons/Apple2';\nimport getIsInMsTeams from 'helpers/getIsInMsTeams';\n\nimport { makeStyles } from 'tss-react/mui';\n\nconst mobilePlatforms = {\n android: {\n displayText: 'Android',\n icon: ,\n href:\n 'https://play.google.com/store/apps/details?id=com.justsift.connect&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1',\n },\n ios: {\n displayName: 'iOS',\n icon: ,\n href:\n 'https://itunes.apple.com/app/apple-store/id1213640928?pt=118583916&ct=Sift-Connect-Nav&mt=8',\n },\n};\n\nconst useStyles = makeStyles()(theme => {\n return {\n banner: {\n display: 'flex',\n height: 50,\n alignItems: 'center',\n padding: '0 18px',\n flex: '0 0 auto',\n },\n spacer: {\n flex: '1 1 auto',\n },\n logo: {\n width: 27,\n height: 27,\n marginRight: theme.spacing(2),\n },\n deviceIcon: {\n marginLeft: 10,\n display: 'inline-flex',\n alignItems: 'center',\n },\n text: {\n flex: '1 1 auto',\n textAlign: 'center',\n display: 'inline-flex',\n alignItems: 'center',\n },\n };\n});\n\nfunction MobileAppGetter() {\n const { classes } = useStyles();\n const location = useLocation();\n const queryParams = queryString.parse(location.search);\n const isMobileView = !!queryParams['mobileView'];\n const isInMsTeams = getIsInMsTeams();\n\n if (isMobileView || isInMsTeams) {\n return null;\n }\n\n if (!bowser.mobile && !bowser.tablet) {\n return null;\n }\n\n let mobDevice = null;\n\n if (bowser.android) {\n mobDevice = 'android';\n } else {\n return null;\n }\n\n const deviceDisplay = mobilePlatforms[mobDevice];\n\n return (\n \n \n \n Get Sift for {deviceDisplay.displayText}\n {deviceDisplay.icon}\n \n \n \n \n );\n}\nexport default MobileAppGetter;\n","import React from 'react';\nimport PropTypes from 'prop-types';\n\n// Components\nimport Snackbar from '@mui/material/Snackbar';\nimport Button from '@mui/material/Button';\n\n// Styles\nimport ThemeOverrideComponent from 'styles/skipper/ThemeOverrideComponent';\nimport createPaletteColor from 'styles/createPaletteColor';\n\nexport default class ThemeSnackbar extends React.PureComponent {\n static propTypes = {\n open: PropTypes.bool.isRequired,\n onClose: PropTypes.func.isRequired,\n };\n\n render() {\n const { open, onClose } = this.props;\n const primary = createPaletteColor('primary', '#F5A800');\n\n return (\n \n \n \n }\n />\n );\n }\n}\n","import { connect } from 'react-redux';\nimport ThemeSnackbar from './ThemeSnackbar';\nimport { removePreviewTheme } from 'store/theme';\n\nconst mapDispatchToProps = dispatch => ({\n onClose: () => {\n dispatch(removePreviewTheme());\n setTimeout(() => {\n window.location.reload();\n }, 1000);\n },\n});\n\nconst mapStateToProps = state => {\n return {\n open: !!state.theme.previewThemeId,\n };\n};\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps,\n)(ThemeSnackbar);\n","import React, { useCallback, useState, useEffect } from 'react';\nimport moment from 'moment';\nimport { useHistory, useLocation } from 'react-router-dom';\nimport queryString from 'query-string';\nimport _throttle from 'lodash/throttle';\nimport Button from '@mui/material/Button';\nimport Paper from '@mui/material/Paper';\nimport { makeStyles } from 'tss-react/mui';\nimport { Theme } from '@mui/material/styles';\nimport { useSnackbar } from 'notistack';\nimport { useData } from 'resift';\nimport getAppVersionStatus from 'fetches/getAppVersionStatus';\nimport typography from 'helpers/typography';\n\nexport const VERSION_UPDATE_QUERY = 'versionUpdate';\n\nfunction UpdateBanner() {\n const { classes } = useStyles();\n const { enqueueSnackbar } = useSnackbar();\n const location = useLocation();\n const history = useHistory();\n const queryParams = queryString.parse(location.search);\n const { isCompatible, recommendUpdate } = useData(getAppVersionStatus) || {};\n const [inactiveTime, setInactiveTime] = useState(moment());\n\n const resetTimer = useCallback(\n () =>\n _throttle(function() {\n setInactiveTime(moment());\n }, 5000),\n [],\n );\n\n useEffect(() => {\n document.addEventListener('mousemove', resetTimer, false);\n document.addEventListener('keypress', resetTimer, false);\n document.addEventListener('touchmove', resetTimer, false);\n\n if (queryParams.versionUpdate) {\n const _queryParams = { ...queryParams } as any;\n delete _queryParams[VERSION_UPDATE_QUERY];\n\n history.push({\n search: queryString.stringify(_queryParams),\n });\n\n enqueueSnackbar(`You've updated to the latest version of Sift!`, {\n variant: 'success',\n });\n }\n\n return () => {\n document.removeEventListener('mousemove', resetTimer, false);\n document.removeEventListener('keypress', resetTimer, false);\n document.removeEventListener('touchmove', resetTimer, false);\n };\n }, [resetTimer]); // eslint-disable-line\n // Jules: to prevent the use effect from re-running, history, location, enqueueSnackbar and queryParams are not passed into this useEffect\n\n useEffect(() => {\n const secondsFromLastReset = moment().diff(inactiveTime, 'seconds');\n if (isCompatible === false && secondsFromLastReset >= 30) {\n handleRefresh(history);\n }\n }, [isCompatible, inactiveTime, history]);\n\n return (\n \n \n 🎉\n \n There is a new version of Sift available!\n \n \n 🎉\n \n \n );\n}\n\nfunction handleRefresh(history: any) {\n history.push({\n search: `?${VERSION_UPDATE_QUERY}=true`,\n });\n window.location.reload();\n}\n\nconst useStyles = makeStyles()((theme: Theme) => {\n return {\n root: {\n background: theme.palette.originalSecondary.main,\n color: theme.palette.originalSecondary.contrastText,\n display: 'flex',\n height: 50,\n justifyContent: 'center',\n alignItems: 'center',\n padding: '0 18px',\n flex: '0 0 auto',\n overflow: 'hidden',\n transition: 'height .4s',\n ...typography(theme, 'h6'),\n '& > span': {\n marginLeft: theme.spacing(3),\n marginRight: theme.spacing(3),\n },\n [theme.breakpoints.down('md')]: {\n ...typography(theme, 'subtitle2'),\n height: 70,\n '& > span': {\n display: 'none',\n },\n },\n },\n button: {\n marginLeft: theme.spacing(3),\n },\n rootButton: {\n color: theme.palette.common.white,\n borderColor: theme.palette.common.white,\n },\n };\n});\n\nexport default UpdateBanner;\n","import React from 'react';\n\nexport const BANNER_TEXT = [\n <>\n Today's the day! Final hours of your trial—join us!\n >,\n <>\n Last chance! 1 day left in your trial. Get started now!\n >,\n <>\n Hurry! Just 2 days left in your trial. Sign up and thrive with us.\n >,\n <>\n 3 days left in your trial to discover what we offer. Click to begin.\n >,\n <>\n 4 days left in your trial. Don't delay, subscribe now!\n >,\n <>\n 5 days remaining in your trial. Start now to make the most of it.\n >,\n <>\n Only 6 days left in your trial. Maximize your trial by signing up\n today.\n >,\n <>\n One week in! You still have 7 days left in your trial to seize the\n opportunity.\n >,\n <>\n 8 days left to go in your trial. Unlock the full potential of Sift now!\n >,\n <>\n Just 9 days to go in your trial. Sign up today.\n >,\n <>\n 10 days left in your trial. Join us and elevate your productivity.\n >,\n <>\n 11 days remaining in your trial. Sign up now and supercharge your work.\n >,\n <>\n 12 days left in your trial to experience all our tools. Start today!\n >,\n <>\n 13 days left in your trial! Don't miss out on exploring our features.\n >,\n <>\n You have 14 days left in your trial. Begin your journey now!\n >,\n];\n","import React from 'react';\nimport brandColors from 'styles/skipper/brandColors';\nimport classNames from 'classnames';\nimport { makeStyles } from 'tss-react/mui';\n\nimport ErrorRoundedIcon from '@mui/icons-material/ErrorRounded';\nimport CloseIcon from '@mui/icons-material/Close';\nimport Divider from '@mui/material/Divider';\nimport Button from '@mui/material/Button';\nimport IconButton from '@mui/material/IconButton';\n\nimport { BANNER_TEXT } from './consts';\n\ninterface Props {\n onClose: () => void;\n trialDaysRemaining: number;\n}\n\nfunction FreeTrialBanner({ onClose, trialDaysRemaining }: Props) {\n const { classes } = useStyles();\n const over14DaysRemainingMessage = (\n <>\n There are {trialDaysRemaining} days left in your trial. Contact our sales\n team to purchase Sift or learn more.\n >\n );\n\n return (\n
\n \n \n\n {client.isTrial && bannerOpen && trialDaysRemaining >= 0 && (\n \n )}\n \n \n IE11 users will experience\n \n partial loss of functionality\n \n after September 29, 2022.\n
\n }\n />\n {!isMobileView &&\n // Pearl TODO: this can be deleted once our new app is approved by the store\n (location.pathname !== MS_TEAMS_START_URL || location.pathname !== MS_TEAMS_LOGIN_URL) && (\n \n )}\n \n
;\n }\n\n return ;\n}\n\nexport default Container;\n","import React, { useContext } from 'react';\nimport { Route, Switch, Redirect, useLocation } from 'react-router';\nimport { TransitionGroup, CSSTransition } from 'react-transition-group';\nimport { useDispatch } from 'resift';\nimport usePrevious from '@sift/resift/usePrevious';\nimport getPathDepth from 'helpers/getPathDepth';\nimport getClearAuthToken from 'fetches/getClearAuthToken';\n\nimport { Layout, Button } from '@fluentui/react-northstar';\nimport { TeamsThemeContext, Theme } from 'components/msTeams/MsTeamsMainTheme';\nimport { MS_TEAMS_MEETING_MAIN_URL, MS_TEAMS_PARTICIPANTS_URL } from 'components/msTeams/consts';\nimport ParticipantsList from 'components/msTeams/embed/ParticipantsList';\nimport PersonProfile from 'components/msTeams/embed/profile/PersonProfile';\n\nimport 'styles/msTeams.css';\n\nfunction MsTeamsMain() {\n const themeContext = useContext(TeamsThemeContext);\n const styles = useStyles(themeContext);\n const timeout = { enter: 1200 };\n const location = useLocation();\n const prevLocation = usePrevious(location);\n const dispatch = useDispatch();\n const flipToChildPage =\n getPathDepth(location.pathname) - getPathDepth(prevLocation.pathname) >= 0;\n\n const handleSignOut = () => {\n dispatch(getClearAuthToken());\n window.location.reload();\n };\n\n return (\n \n \n \n \n \n \n \n \n \n \n \n \n \n );\n}\n\nconst useStyles = (theme: Theme) => ({\n root: {\n paddingTop: theme.siteVariables.spacing.unit,\n maxWidth: 500,\n },\n button: {\n marginLeft: 'auto',\n marginBottom: theme.siteVariables.spacing.unit,\n textDecoration: 'underline',\n },\n});\n\nexport default MsTeamsMain;\n","import * as React from 'react';\n\nconst SiftLogo = (props: any) => (\n \n);\n\nexport default SiftLogo;\n","import React, { useContext } from 'react';\nimport SiftLogo from 'components/msTeams/embed/icons/SiftLogo';\n\nimport MsTeamsMainTheme, { TeamsThemeContext, Theme } from 'components/msTeams/MsTeamsMainTheme';\nimport { Flex } from '@fluentui/react-northstar';\n\ninterface Props {\n children: React.ReactNode;\n}\n\nfunction InfoPageLayout({ children }: Props) {\n const themeContext = useContext(TeamsThemeContext);\n const styles = useStyles(themeContext);\n\n return (\n \n \n \n \n {children}\n \n \n \n );\n}\n\nconst useStyles = (theme: Theme) => ({\n root: {\n textAlign: 'center',\n maxWidth: 500,\n },\n section: {\n marginTop: theme.siteVariables.spacing.unit * 4,\n marginBottom: theme.siteVariables.spacing.unit * 4,\n },\n});\n\nexport default InfoPageLayout;\n","import React, { useContext } from 'react';\nimport { Flex, Button, Text, Loader } from '@fluentui/react-northstar';\nimport { TeamsThemeContext, Theme } from 'components/msTeams/MsTeamsMainTheme';\n\nimport InfoPageLayout from 'components/msTeams/embed/molecules/InfoPageLayout';\n\ninterface Props {\n onRefresh: () => void;\n isFetching: boolean;\n}\n\nfunction InsufficientPermission({ onRefresh, isFetching }: Props) {\n const themeContext = useContext(TeamsThemeContext);\n const styles = useStyles(themeContext);\n\n return (\n \n \n Sift does not have sufficient permissions to retrieve the list of meeting participants.\n Please ask the organizer of the meeting to sign into Sift, then refresh to try again.\n \n \n \n \n \n );\n}\n\nconst useStyles = (theme: Theme) => ({\n root: {},\n info: {\n marginBottom: theme.siteVariables.spacing.unit * 4,\n },\n});\n\nexport default InsufficientPermission;\n","import React, { useEffect, createContext } from 'react';\nimport { useData, useDispatch, useStatus, isLoading } from 'resift';\nimport useInterval from 'helpers/useInterval';\nimport { Loader } from '@fluentui/react-northstar';\nimport getIsInMsTeamsEmbed from 'helpers/getIsInMsTeamsEmbed';\nimport * as microsoftTeams from '@microsoft/teams-js';\nimport makeGetMeetingAttendeesFetch from 'components/msTeams/fetches/makeGetMeetingAttendeesFetch';\nimport MsTeamsMainTheme from 'components/msTeams/MsTeamsMainTheme';\nimport MsTeamsMain from './MsTeamsMain';\nimport InsufficientPermission from 'components/msTeams/embed/InsufficientPermission';\nimport { Person, meetingAttendee } from 'components/msTeams/embed/typing';\n\nconst attendeeData: Array = [];\n\nexport const AttendeesDataContext = createContext(attendeeData);\n\nfunction Container() {\n const isInMsTeamsEmbed = getIsInMsTeamsEmbed();\n const meetingAttendeesFetch = makeGetMeetingAttendeesFetch();\n const dispatch = useDispatch();\n const attendeesFetchData = useData(meetingAttendeesFetch);\n const oneMinute = 60000;\n const fetchStatus = useStatus(meetingAttendeesFetch);\n\n useEffect(() => {\n if (isInMsTeamsEmbed) {\n microsoftTeams.initialize();\n\n microsoftTeams.getContext(context => {\n dispatch(meetingAttendeesFetch(context.chatId!));\n });\n }\n }, [dispatch, isInMsTeamsEmbed, meetingAttendeesFetch]);\n\n useInterval(() => {\n if (isInMsTeamsEmbed) {\n microsoftTeams.getContext(context => {\n dispatch(meetingAttendeesFetch(context.chatId!));\n });\n }\n }, oneMinute);\n\n const handleRefresh = () => {\n microsoftTeams.getContext(context => {\n dispatch(meetingAttendeesFetch(context.chatId!));\n });\n };\n\n if (!isInMsTeamsEmbed) {\n return (\n
\n This page is only available in Microsoft Teams panel.\n
\n );\n }\n\n if (!attendeesFetchData) {\n return (\n \n \n \n );\n }\n\n const { participantFetchStatus } = attendeesFetchData;\n\n if (participantFetchStatus !== 'SUCCESS') {\n return ;\n }\n\n const attendeeList = attendeesFetchData.results.map(\n (attendee: meetingAttendee) => attendee.profile,\n );\n\n return (\n \n \n \n \n \n );\n}\n\nexport default Container;\n","import React, { useEffect, Suspense } from 'react';\nimport { useSelector } from 'react-redux';\nimport { LOADING } from 'resift';\nimport useRouter from '@sift/resift/useRouter';\nimport useAnalytics from 'helpers/useAnalytics';\nimport routes from './helpers/routes';\nimport useOnboardingRedirectEffect from 'helpers/useOnboardingRedirectEffect';\nimport Loader from 'components/skipper/Loader';\n\n// Components\nimport CoreLayout from 'components/CoreLayout';\nimport SearchOverlay from 'components/BigSearch/SearchOverlay';\nimport MsTeamsMain from 'components/msTeams/embed/MsTeamsMain';\nimport { Route, Switch, Redirect } from 'react-router';\nimport * as microsoftTeams from '@microsoft/teams-js';\nimport getIsInMsTeams from 'helpers/getIsInMsTeams';\n\nimport { MS_TEAMS_MEETING_MAIN_URL } from 'components/msTeams/consts';\n\nfunction SiftRouter() {\n const client = useSelector(state => state.client);\n const user = useSelector(state => state.authentication.person);\n const analytics = useAnalytics();\n const { history } = useRouter();\n const isInMsTeams = getIsInMsTeams();\n\n useEffect(() => {\n history.listen(({ pathname }) => {\n // TODO: this causes the analytics/segment to have the wrong page title\n // because the `document.title` is usually set after the router history event\n analytics.page(pathname);\n });\n\n if (isInMsTeams) {\n microsoftTeams.initialize();\n microsoftTeams.getContext(context => {\n /** This is for deep linking in ms teams apps from the url that has subEntityId encoded\n * e.g. https://teams.microsoft.com/l/entity/19cdebca-d354-45a6-a701-a7cde03457a8/search?context=%7B%22subEntityId%22%3A%22%2Fprofile%2FxM0uI0Dh5xRr%22%7D\n * The above url gets interpreted by getContext and returns\n * context.subEntityId = \"/profile/xM0uI0Dh5xRr\"\n */\n if (context.subEntityId) {\n history.push(context.subEntityId);\n }\n });\n }\n }, [analytics, history, isInMsTeams]);\n\n useOnboardingRedirectEffect();\n\n const WrapperComponent = window.location.href.includes(MS_TEAMS_MEETING_MAIN_URL)\n ? React.Fragment\n : CoreLayout;\n\n return (\n \n }>\n \n {routes(client, user).map((route, index) => (\n \n ))}\n \n \n \n \n \n \n );\n}\n\nexport default SiftRouter;\n","import React from 'react';\n\nimport ErrorBoundary from 'components/skipper/ErrorBoundary';\nimport AppProvider from 'components/AppProvider';\nimport AppFetcher from 'components/AppFetcher';\nimport Router from 'components/Router';\n\nfunction App() {\n return (\n \n \n {() => }\n \n \n );\n}\n\nexport default App;\n","import React from 'react';\nimport createTheme from 'styles/skipper/createSkipperTheme';\nimport { ThemeProvider } from '@mui/material/styles';\nimport createCache from '@emotion/cache';\nimport { CacheProvider } from '@emotion/react';\n\nconst theme = createTheme({\n palette: {\n secondary: {\n main: '#F88D2B',\n contrastText: '#fff',\n },\n },\n});\n\nexport const muiCache = createCache({\n key: 'mui',\n prepend: true,\n});\n\ninterface Props {\n children: React.ReactNode;\n}\n\nfunction MsTeamsThemeWrapper({ children }: Props) {\n return (\n \n {children}\n \n );\n}\n\nexport default MsTeamsThemeWrapper;\n","import React, { useEffect } from 'react';\nimport typography from 'helpers/typography';\nimport * as microsoftTeams from '@microsoft/teams-js';\nimport config from 'config';\nimport queryString from 'query-string';\nimport classNames from 'classnames';\n\n// Components\nimport SiftLogo from 'components/skipper/SiftLogo';\nimport Typography from '@mui/material/Typography';\nimport Button from '@mui/material/Button';\nimport LaunchIcon from '@mui/icons-material/Launch';\n\n// Styles\nimport { makeStyles } from 'tss-react/mui';\nimport { Theme } from '@mui/material/styles';\n\nimport MsTeamsThemeWrapper from '../MsTeamsThemeWrapper';\n\nimport { SIFT_MARKETING_SITE } from 'helpers/consts';\nimport { MS_TEAMS_LOGIN_DONE_URL } from 'components/msTeams/consts';\n\nconst useStyles = makeStyles()((theme: Theme) => ({\n root: {\n height: '100%',\n width: '100%',\n overflow: 'auto',\n backgroundColor: theme.palette.background.paper,\n },\n content: {\n display: 'flex',\n minHeight: '100%',\n maxWidth: '100%',\n },\n aside: {\n flex: '0 0 auto',\n width: '40%',\n background: 'linear-gradient(180deg, rgba(61, 0, 145, 0) 0%, #3D0091 100%), #7512FF',\n overflow: 'hidden',\n display: 'flex',\n [theme.breakpoints.down('md')]: {\n display: 'none',\n },\n },\n img: {\n display: 'block',\n margin: 'auto',\n objectFit: 'contain',\n width: '100%',\n height: '100%',\n maxWidth: 640,\n padding: theme.spacing(4),\n },\n body: {\n flex: '1 1 auto',\n padding: theme.spacing(2),\n margin: theme.spacing(0, 6),\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n marginBottom: theme.spacing(6),\n [theme.breakpoints.down('md')]: {\n margin: 0,\n },\n },\n logo: {\n marginBottom: theme.spacing(3),\n },\n title: {\n display: 'flex',\n flexDirection: 'column',\n color: '#7512FF',\n marginBottom: theme.spacing(2),\n },\n workBetter: {\n ...typography(theme, 'h4'),\n lineHeight: 1,\n },\n together: {\n ...typography(theme, 'h2'),\n fontWeight: theme.typography.fontWeightBold,\n },\n main: {\n margin: 'auto 0',\n maxWidth: '100%',\n [theme.breakpoints.down('md')]: {\n margin: 'auto',\n },\n },\n paragraph: {\n marginBottom: theme.spacing(2),\n maxWidth: 330,\n [theme.breakpoints.down('md')]: {\n maxWidth: 400,\n },\n },\n buttons: {\n display: 'flex',\n '& > *:not(:last-child)': {\n marginRight: theme.spacing(2),\n },\n marginBottom: theme.spacing(2),\n },\n button: {\n width: 150,\n marginTop: theme.spacing(2),\n },\n signupInfo: {\n ...typography(theme, 'caption'),\n },\n}));\n\nfunction MsTeamsLogin() {\n const { classes } = useStyles();\n\n useEffect(() => {\n microsoftTeams.initialize();\n }, []);\n\n const handleSignIn = () => {\n microsoftTeams.authentication.authenticate({\n successCallback: () => {\n // (pearl) Note: Console log showed that this never fires, but putting it here in case it is needed.\n const parsed = queryString.parse(window.location.search);\n const path = (parsed.redirectTo as string) || '/';\n window.location.href = path;\n },\n failureCallback: () => {\n /** This failure callback fires when the login window is closed.\n * We don't have a way of calling the successCallback because we're not following the\n * microsoft teams login flow. Instead, we have our own cookie authentication system.\n * Therefore, we always redirect people to where they came from, and auth checking is always\n * handled in `dataServiceActionHandlers`*/\n const parsed = queryString.parse(window.location.search);\n const path = (parsed.redirectTo as string) || '/';\n window.location.href = path;\n },\n height: 700,\n width: 400,\n url: `${config.LOGIN}?inTeams=true&redirectTo=${encodeURIComponent(MS_TEAMS_LOGIN_DONE_URL)}`,\n });\n };\n\n return (\n
\n
\n \n
\n \n \n
\n \n Work Better,\n \n \n Together\n \n
\n \n Sift is the fastest way to find and connect with anyone in your company.\n \n \n Empowering everyone with the ability to discover and leverage the expertise of all\n your colleagues.\n \n
\n \n Note: A Sift account is required to sign in. To get your company\n started with Sift, please click “Learn More” or try our 30-day trial through the store\n page.\n \n \n
\n
\n
\n );\n}\n\nfunction WrappedComponent() {\n return (\n \n \n \n );\n}\n\nexport default WrappedComponent;\n","// This screen shows up after the user successfully logged in to Sift\n// in ms teams mobile app\nimport React from 'react';\nimport { makeStyles } from 'tss-react/mui';\nimport { Theme } from '@mui/material/styles';\n\nimport SiftLogo from 'components/skipper/SiftLogo';\nimport Typography from '@mui/material/Typography';\nimport MsTeamsThemeWrapper from '../MsTeamsThemeWrapper';\n\nconst useStyles = makeStyles()((theme: Theme) => ({\n root: {\n height: '100%',\n width: '100%',\n overflow: 'auto',\n backgroundColor: theme.palette.background.paper,\n padding: theme.spacing(2),\n },\n body: {\n margin: '0 auto',\n maxWidth: 600,\n },\n logo: {\n marginBottom: theme.spacing(3),\n marginTop: theme.spacing(3),\n },\n text: {\n margin: '0 auto',\n },\n}));\n\nfunction MsTeamsLoginSuccess() {\n const { classes } = useStyles();\n\n return (\n
\n
\n \n \n You have been signed in. You may close this window.\n \n
\n
\n );\n}\n\nfunction WrappedComponent() {\n return (\n \n \n \n );\n}\n\nexport default WrappedComponent;\n","import React, { useEffect, useContext } from 'react';\nimport * as microsoftTeams from '@microsoft/teams-js';\nimport { Text, Button, Flex } from '@fluentui/react-northstar';\nimport { OpenOutsideIcon } from '@fluentui/react-icons-northstar';\nimport config from 'config';\nimport { SIFT_MARKETING_SITE } from 'helpers/consts';\nimport { TeamsThemeContext, Theme } from 'components/msTeams/MsTeamsMainTheme';\nimport InfoPageLayout from 'components/msTeams/embed/molecules/InfoPageLayout';\nimport { MS_TEAMS_MEETING_MAIN_URL, MS_TEAMS_LOGIN_DONE_URL } from 'components/msTeams/consts';\n\nfunction TeamsLogin() {\n const themeContext = useContext(TeamsThemeContext);\n const styles = useStyles(themeContext);\n\n useEffect(() => {\n microsoftTeams.initialize();\n }, []);\n\n const handleSignIn = () => {\n microsoftTeams.authentication.authenticate({\n successCallback: () => {\n // (pearl) Note: Console log showed that this never fires, but putting it here in case it is needed.\n const path = MS_TEAMS_MEETING_MAIN_URL;\n window.location.assign(path);\n },\n failureCallback: () => {\n /** This failure callback fires when the login window is closed.\n * We don't have a way of calling the successCallback because we're not following the\n * microsoft teams login flow. Instead, we have our own cookie authentication system.\n * Therefore, we always load the MS_TEAMS_MEETING_MAIN_URL, and auth checking is always handled in `dataServiceActionHandlers`\n */\n const path = MS_TEAMS_MEETING_MAIN_URL;\n window.location.assign(path);\n },\n height: 700,\n width: 400,\n url: `${config.LOGIN}?initAzureADLogin=true&inTeams=true&redirectTo=${encodeURIComponent(\n MS_TEAMS_LOGIN_DONE_URL,\n )}`,\n });\n };\n\n return (\n \n \n Sift is the fastest way to find and connect with anyone in your company.\n \n \n \n }\n iconPosition=\"before\"\n data-testid=\"learn-more-button\"\n />\n \n \n Note: A Sift account is required. To get your company started with Sift,{' '}\n \n learn more\n \n {' '}\n or try our 30-day trial through the store page.\n \n Consent to access meeting information through your Microsoft account is required. If you\n have trouble granting these permissions during sign-in, please contact your administrator.\n \n \n \n );\n}\n\nconst useStyles = (theme: Theme) => ({\n root: {},\n intro: {\n marginBottom: theme.siteVariables.spacing.unit * 4,\n },\n footer: {\n marginTop: theme.siteVariables.spacing.unit * 4,\n },\n link: {\n color: 'inherit',\n textDecoration: 'underline',\n display: 'inline-flex',\n },\n consent: {\n display: 'block',\n marginTop: theme.siteVariables.spacing.unit * 2,\n },\n});\n\nexport default TeamsLogin;\n","import 'core-js';\nimport 'react-app-polyfill/ie11';\nimport 'fullscreen-api-polyfill';\nimport './prependPolyfill';\nimport React from 'react';\nimport { createRoot } from 'react-dom/client';\nimport config from './config';\nimport { init, captureException } from '@sentry/browser';\n\n// Styles\nimport 'styles/core.css';\nimport 'styles/bodyFullscreen.css';\n\n// Components\nimport App from 'components/App';\nimport MsTeamsLogin from 'components/msTeams/MsTeamsLogin';\nimport MsTeamsLoginSuccess from 'components/msTeams/MsTeamsLoginSuccess';\nimport TeamsLogin from 'components/msTeams/embed/TeamsLogin';\nimport {\n MS_TEAMS_LOGIN_URL,\n MS_TEAMS_MEETING_LOGIN_URL,\n MS_TEAMS_LOGIN_DONE_URL,\n} from 'components/msTeams/consts';\n\n(window as any)._captureException = captureException;\n\nif (config.ENV === 'production') {\n // Hotjar Tracking Code for https://foc.justsift.com/\n (function(h: any, o: any, t: any, j: any, a?: any, r?: any) {\n h.hj =\n h.hj ||\n function() {\n (h.hj.q = h.hj.q || []).push(arguments);\n };\n h._hjSettings = { hjid: 2029747, hjsv: 6 };\n a = o.getElementsByTagName('head')[0];\n r = o.createElement('script');\n r.async = 1;\n r.src = t + h._hjSettings.hjid + j + h._hjSettings.hjsv;\n a.appendChild(r);\n })(window, document, 'https://static.hotjar.com/c/hotjar-', '.js?sv=');\n}\n\nasync function main() {\n if (window.location.href.includes(MS_TEAMS_LOGIN_URL)) {\n const loadingContainer = document.querySelector('.loading_container');\n loadingContainer?.parentNode?.removeChild(loadingContainer);\n const container = document.getElementById('root');\n const root = createRoot(container!);\n root.render();\n return;\n }\n\n if (window.location.href.includes(MS_TEAMS_MEETING_LOGIN_URL)) {\n const loadingContainer = document.querySelector('.loading_container');\n loadingContainer?.parentNode?.removeChild(loadingContainer);\n const container = document.getElementById('root');\n const root = createRoot(container!);\n root.render();\n return;\n }\n\n if (window.location.href.includes(MS_TEAMS_LOGIN_DONE_URL)) {\n const loadingContainer = document.querySelector('.loading_container');\n loadingContainer?.parentNode?.removeChild(loadingContainer);\n const container = document.getElementById('root');\n const root = createRoot(container!);\n root.render();\n window.close();\n return;\n }\n\n if (config.SENTRY) {\n init({\n dsn: config.SENTRY,\n environment: config.ENV,\n ignoreErrors: ['ResizeObserver loop limit exceeded'],\n });\n }\n const container = document.getElementById('root');\n const root = createRoot(container!);\n root.render();\n}\n\nmain().catch(e => {\n return captureException(e);\n});\n","import { deepmerge } from '@mui/utils';\nimport { createTheme } from '@mui/material/styles';\nimport skipperDefaultTheme from './skipperDefaultTheme';\n\n// removes a warning. this is just to resolve a TS thing\nexport const Theme = undefined;\n\nexport default function createSkipperTheme(theme = {}) {\n return createTheme(deepmerge(skipperDefaultTheme, theme));\n}\n","// Models\nimport Fetch from '@sift/resift/models/Fetch';\nimport { mergeLoading, mergeError } from '@sift/resift/redux/FetchUtils';\nimport { Status, States } from '@sift/resift/models/Status';\n\nconst initialState = {\n objects: {},\n fetches: {},\n types: {},\n};\n\n// ------------------------------------\n// Constants\n// ------------------------------------\nexport const FETCH_ENTITY = 'FETCH_ENTITY';\nexport const FETCH_ENTITY_SUCCESS = 'FETCH_ENTITY_SUCCESS';\nexport const FETCH_ENTITY_ERROR = 'FETCH_ENTITY_ERROR';\n\nexport const FETCH_ENTITY_TYPES = 'FETCH_ENTITY_TYPES';\nexport const FETCH_ENTITY_TYPES_SUCCESS = 'FETCH_ENTITY_TYPES_SUCCESS';\nexport const FETCH_ENTITY_TYPES_ERROR = 'FETCH_ENTITY_TYPES_SUCCESS_ERROR';\n\nexport const UPDATE_ENTITY = 'UPDATE_ENTITY';\nexport const UPDATE_ENTITY_SUCCESS = 'UPDATE_ENTITY_SUCCESS';\nexport const UPDATE_ENTITY_ERROR = 'UPDATE_ENTITY_ERROR';\n\nexport const UPDATE_ENTITY_RELATIONSHIP = 'UPDATE_ENTITY_RELATIONSHIP';\nexport const UPDATE_ENTITY_RELATIONSHIP_SUCCESS = 'UPDATE_ENTITY_RELATIONSHIP_SUCCESS';\nexport const UPDATE_ENTITY_RELATIONSHIP_ERROR = 'UPDATE_ENTITY_RELATIONSHIP_ERROR';\n\nexport const PERSON_ENTITY_TYPE_KEY = 'user';\n\n// ------------------------------------\n// Actions\n// ------------------------------------\n\n/**\n * Get a set of entity types\n *\n * @param {string} status\n */\nexport const fetchEntityTypes = () => {\n return {\n type: FETCH_ENTITY_TYPES,\n identifier: 'entityTypes',\n payload: {},\n };\n};\n\nexport const fetchEntity = entityId => {\n return {\n type: FETCH_ENTITY,\n identifier: 'entity',\n payload: {\n entityId,\n },\n };\n};\n\nexport const updateEntity = (entity, fields, { retainDialogState }) => {\n return {\n type: UPDATE_ENTITY,\n identifier: 'entity',\n payload: {\n retainDialogState,\n data: {\n id: entity.id,\n entityField: entity.entityField,\n ...fields,\n },\n },\n };\n};\n\nexport const updateEntityRelationship = (\n entity,\n relationships,\n { relationship, retainDialogState },\n) => {\n return {\n type: UPDATE_ENTITY_RELATIONSHIP,\n identifier: entity.id,\n payload: {\n data: relationships,\n relationship,\n retainDialogState,\n },\n };\n};\n\n// ------------------------------------\n// Action Handlers\n// ------------------------------------\nconst ACTION_HANDLERS = {\n [FETCH_ENTITY_TYPES]: mergeLoading,\n [FETCH_ENTITY_TYPES_SUCCESS]: (state, action) => {\n const identifier = action.identifier;\n const objectsAndIds = action.payload.data.results.reduce(\n (acc, cur, index) => {\n const id = cur.id;\n acc.objects[id] = cur;\n acc.ids.push(id);\n return acc;\n },\n {\n objects: {},\n ids: [],\n },\n );\n return {\n ...state,\n types: {\n ...state.types,\n ...objectsAndIds.objects,\n },\n fetches: {\n ...state.fetches,\n [identifier]: new Fetch(\n identifier,\n objectsAndIds.ids,\n [],\n action.payload.data,\n new Status(States.NORMAL),\n ),\n },\n };\n },\n [FETCH_ENTITY_TYPES_ERROR]: mergeError,\n};\n\n// ------------------------------------\n// Reducer\n// ------------------------------------\nexport default function EntityReducer(state = initialState, action) {\n const handler = ACTION_HANDLERS[action.type];\n\n return handler ? handler(state, action) : state;\n}\n","import createPortal from 'helpers/createPortal';\n\nexport default createPortal('OnboardingAppBarPortal') as any;\n","import { createTheme } from '@mui/material/styles';\n\nconst defaultTheme = createTheme();\n\nexport default function createPaletteColor(paletteColorName: string, mainColor: string) {\n return defaultTheme.palette.augmentColor({\n color: { main: mainColor },\n name: paletteColorName,\n });\n}\n","import { LOADING } from 'resift';\nimport { LegacyStatus } from '@sift/resift/models/Status';\n\nfunction getLoading(status: number | LegacyStatus) {\n if (typeof status === 'number') return (status & LOADING) !== 0;\n return status.isLoading();\n}\n\nexport default function isLoading(...statuses: (number | LegacyStatus)[]) {\n return statuses.some(getLoading);\n}\n","import { NORMAL } from 'resift';\nimport { LegacyStatus } from '@sift/resift/models/Status';\n\nfunction getNormal(status: number | LegacyStatus) {\n if (typeof status === 'number') return (status & NORMAL) !== 0;\n return status.isNormal();\n}\n\nexport default function isNormal(...statuses: (number | LegacyStatus)[]) {\n return statuses.every(getNormal);\n}\n","import { ERROR } from 'resift';\nimport { LegacyStatus } from '@sift/resift/models/Status';\n\nfunction getError(status: number | LegacyStatus) {\n if (typeof status === 'number') return (status & ERROR) !== 0;\n return status.hasError();\n}\n\nexport default function isError(...statuses: (number | LegacyStatus)[]) {\n return statuses.some(getError);\n}\n","import React, { forwardRef, useMemo } from 'react';\nimport { DivProps } from 'helpers/typings';\nimport { CSSTransition } from 'react-transition-group';\n\n// these are different because they support the legacy status\nimport isLoading from './helpers/isLoading';\nimport isNormal from './helpers/isNormal';\nimport isError from './helpers/isError';\n\n// Components\nimport CircularProgress from '@mui/material/CircularProgress';\nimport ErrorView from '../views/Error';\n\n// Styles\nimport classNames from 'classnames';\nimport { makeStyles } from 'tss-react/mui';\nimport { Theme } from '@mui/material/styles';\n\nconst TRANSITION_TIMING = 250;\n\nfunction isEmpty(status: any) {\n if (typeof status === 'number') return false;\n return status.isEmpty();\n}\n\nconst useStyles = makeStyles()((theme: Theme) => {\n return {\n root: {},\n overlay: {\n backgroundColor: 'none',\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n transition: `opacity ${TRANSITION_TIMING}ms`,\n zIndex: theme.zIndex.loader,\n },\n overlayTransparent: {\n backgroundColor: 'rgba(255, 255, 255, 0.7)',\n },\n appear: {\n opacity: 0,\n },\n appearActive: {\n opacity: 1,\n },\n enter: {\n opacity: 0,\n },\n enterActive: {\n opacity: 1,\n },\n exit: {\n opacity: 1,\n },\n exitActive: {\n opacity: 0,\n },\n };\n});\n\ninterface Props extends DivProps {\n /**\n * Classnames for each of the three components.\n */\n classes?: Partial['classes']>;\n className?: string;\n /**\n * Transition timeouts.\n */\n transitionTimeout?: {\n enter?: number;\n exit?: number;\n };\n /**\n * The content to be rendered when normal.\n */\n children?: React.ReactNode;\n /**\n * Loading State Model\n */\n status: any;\n /**\n * Loading indicator view\n */\n isLoadingView?: React.ReactNode;\n /**\n * Empty View\n */\n isEmptyView?: React.ReactNode;\n /**\n * Error View\n */\n isErrorView?: React.ReactNode;\n /**\n * pass in a `ref` to the inner `div` element\n */\n}\n\nexport default forwardRef((props: Props, ref: React.Ref) => {\n const {\n classes: classesFromProps,\n className,\n transitionTimeout,\n children,\n status,\n isLoadingView,\n isEmptyView,\n isErrorView,\n ...rest\n } = props;\n const { classes } = useStyles(undefined, { props: { classes: classesFromProps } });\n\n const transitionClasses = {\n appear: classes.appear,\n appearActive: classes.appearActive,\n enter: classes.enter,\n enterActive: classes.enterActive,\n exit: classes.exit,\n exitActive: classes.exitActive,\n };\n\n const getTransitionTimeout = () => {\n return (\n transitionTimeout || {\n enter: TRANSITION_TIMING,\n exit: TRANSITION_TIMING,\n active: TRANSITION_TIMING,\n }\n );\n };\n\n /**\n * Renders the proper state view from the current status. See contents of function to\n * determine priority order of each state.\n */\n\n const stateView = useMemo(() => {\n if (isLoading(status)) return isLoadingView || ;\n if (isEmpty(status)) return isEmptyView || null;\n if (isError(status)) return isErrorView || ;\n\n return null;\n }, [isEmptyView, isErrorView, isLoadingView, status]);\n\n const visible = !!stateView;\n\n return (\n
\n We recommend uploading your LinkedIn resume to make this process easier. You’ll have full\n control over what information you add and can always change it later.\n
\n );\n }}\n theme={{\n container: classNames(classes.root, className),\n suggestionsContainer: classes.suggestionsContainer,\n suggestionsContainerOpen: classes.suggestionsContainerOpen,\n suggestionsList: classes.suggestionsList,\n suggestion: classes.suggestion,\n }}\n {...AutosuggestProps}\n onSuggestionSelected={handleSuggestionSelection}\n />\n );\n}\n\nexport default Autocomplete;\n","import { defineFetch } from 'resift';\n\nexport default defineFetch({\n displayName: 'profile fetch',\n share: {\n namespace: 'person',\n merge: (previous, next) => ({\n ...previous,\n ...next,\n }),\n },\n make: (personId: string) => ({\n request: () => ({ http }: any) =>\n http({\n method: 'GET',\n route: `/people/${personId}`,\n query: {\n relationships: true,\n configuration: true,\n pageSize: 1,\n },\n }),\n }),\n});\n","/**\n * takes in a file and returns a base 64 string\n */\nexport default function getBase64(file: File) {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.readAsDataURL(file);\n\n reader.addEventListener('load', () => {\n resolve(reader.result as string);\n });\n reader.addEventListener('error', err => {\n reject(err);\n });\n });\n}\n","import { Theme } from '@mui/material/styles';\nimport createPictureUrlFromColor from 'helpers/createPictureUrlFromColor';\n\nexport default function ensurePictureUrl(src: string, theme: Theme) {\n if (!src || src.includes('/placeholders')) {\n return theme.pictureUrl || createPictureUrlFromColor(theme.palette.primary.dark);\n }\n\n return src;\n}\n","import _get from 'lodash/get';\nimport usePassiveRedux from '@sift/resift/usePassiveRedux';\nimport createPictureUrlFromColor from 'helpers/createPictureUrlFromColor';\nimport { Theme } from '@mui/material/styles';\n\ninterface Person {\n pictureUrl: string;\n officialPictureUrl: string;\n customPictureUrl?: string;\n}\n\nexport default function usePictureUrlFromPerson(person: Person | undefined | null, theme: Theme) {\n const preferOfficialPhotos = usePassiveRedux(state =>\n _get(state, ['settings', 'directory', 'preferOfficialPhotos'], false),\n );\n\n if (!person) {\n return '';\n }\n\n const { pictureUrl, officialPictureUrl = '', customPictureUrl } = person;\n const specialPictureUrl = officialPictureUrl.includes('/placeholders')\n ? theme.pictureUrl ||\n createPictureUrlFromColor(theme.palette.primary.dark) ||\n officialPictureUrl\n : officialPictureUrl || theme.pictureUrl;\n\n if (preferOfficialPhotos) {\n return specialPictureUrl || pictureUrl;\n }\n\n // if customPictureUrl exists, then user has uploaded their own picture and it should default to pictureUrl\n // if not, pictureUrl is not defined and themePictureUrl needs to have priority\n\n return customPictureUrl || specialPictureUrl || pictureUrl;\n}\n","import React, { forwardRef } from 'react';\nimport classNames from 'classnames';\nimport { makeStyles } from 'tss-react/mui';\nimport { Theme } from '@mui/material/styles';\nimport getIsInMsTeams from 'helpers/getIsInMsTeams';\nimport Link, { LinkProps } from '@mui/material/Link';\nimport LaunchIcon from '@mui/icons-material/Launch';\n\ninterface Props extends LinkProps {\n href: string;\n children?: React.ReactNode;\n className?: string;\n}\n\nconst NewTabLink = forwardRef((props: Props, ref: React.Ref) => {\n const isInMsTeams = getIsInMsTeams();\n const { children, className, ...restOfProps } = props;\n const { classes } = useStyles();\n\n return (\n \n {children}\n {isInMsTeams && }\n \n );\n});\n\nconst useStyles = makeStyles()((theme: Theme) => ({\n root: {\n display: 'inline-flex',\n },\n}));\n\nexport default NewTabLink;\n","import createReducer from '@sift/resift/createReducer';\n\nconst ON_TYPE_LIST_SEARCH = 'ON_TYPE_LIST_SEARCH';\nconst SELECT_LIST_SEARCH_TYPE = 'SELECT_LIST_SEARCH_TYPE';\nconst SELECT_MENU_ITEM = 'SELECT_MENU_ITEM';\n\ninterface State {\n searchValue: string;\n currentSearchOption: string;\n selectedItems: any[];\n}\n\nconst initialState = {\n searchValue: '',\n currentSearchOption: 'List Name',\n selectedItems: [],\n} as State;\n\nexport function handleListSearchType(value: any) {\n return {\n type: ON_TYPE_LIST_SEARCH,\n payload: {\n value,\n },\n };\n}\n\nexport function handleSelectSearchType(value: string) {\n return {\n type: SELECT_LIST_SEARCH_TYPE,\n payload: {\n value,\n },\n };\n}\n\nexport function handleSelectMenuItem(value: any) {\n return {\n type: SELECT_MENU_ITEM,\n payload: {\n value,\n },\n };\n}\n\nconst actionHandlers = {\n [ON_TYPE_LIST_SEARCH]: (state: any, action: any) => {\n return {\n ...state,\n searchValue: action.payload.value,\n };\n },\n [SELECT_LIST_SEARCH_TYPE]: (state: any, action: any) => {\n return {\n ...state,\n currentSearchOption: action.payload.value,\n searchValue: '',\n selectedItems: [],\n };\n },\n [SELECT_MENU_ITEM]: (state: any, action: any) => {\n return {\n ...state,\n searchValue: '',\n selectedItems: action.payload.value,\n };\n },\n};\n\nexport default createReducer(initialState, actionHandlers);\n","import { defineFetch, typedFetchFactory } from 'resift';\nimport _get from 'lodash/get';\nimport * as Sift from '@sift/types';\n\nconst makeOrgChartSettingsFetch = defineFetch({\n displayName: 'Get Org Chart Settings',\n share: { namespace: 'orgChartSettings' },\n make: () => ({\n request: () => async ({ http, getState }) => {\n const state = getState();\n const personId = _get(state, ['authentication', 'person', 'id']);\n\n if (!personId) {\n throw new Error('[orgChartSettingsFetch] could not get logged in user id');\n }\n\n const settings = await http({\n method: 'GET',\n route: `/people/${personId}/settings`,\n });\n\n const { orgChart } = settings.directory;\n\n return orgChart;\n },\n }),\n});\n\nconst typedMakeOrgChartSettingsFetch = typedFetchFactory()(\n makeOrgChartSettingsFetch,\n);\nconst orgChartSettingsFetch = typedMakeOrgChartSettingsFetch();\nexport default orgChartSettingsFetch;\n","import { defineFetch } from 'resift';\nimport removeKeys from 'helpers/removeKeys';\n\nexport default defineFetch({\n displayName: 'upload picture fetch',\n share: {\n namespace: 'person',\n merge: (previous, next) => ({\n ...removeKeys(previous, ['customPictureUrl']),\n ...next,\n }),\n },\n make: (personId: string) => ({\n request: (dataUrl: string) => ({ http }: any) => {\n if (!dataUrl) {\n // delete\n return http({\n method: 'DELETE',\n route: `/people/${personId}/upload?imageType=pictureUrl`,\n });\n } else {\n // post\n const formData = new FormData();\n\n formData.append('file[pictureUrl]', dataUrl);\n\n return http({\n method: 'POST',\n route: `/people/${personId}/upload`,\n data: formData,\n });\n }\n },\n }),\n});\n","import { HttpParams, defineFetch } from 'resift';\n\ninterface FetcherParams {\n http: (httpParams: HttpParams) => Promise;\n}\n\nexport default defineFetch({\n displayName: 'autocomplete fetch',\n make: (instanceId: string) => ({\n key: [instanceId],\n request: (httpParams: HttpParams) => ({ http }: FetcherParams) => http(httpParams),\n }),\n});\n","import React from 'react';\nimport { makeStyles } from 'tss-react/mui';\nimport getIsInMsTeams from 'helpers/getIsInMsTeams';\n\nimport LaunchIcon from '@mui/icons-material/Launch';\n\ninterface Props {\n text: string;\n}\n\nfunction NewTabDiv({ text }: Props) {\n const isInMsTeams = getIsInMsTeams();\n const classes = useStyles();\n\n return (\n
\n {text}\n {isInMsTeams && }\n
\n );\n}\n\nconst useStyles = makeStyles(theme => ({\n root: {\n display: 'inline-flex',\n },\n}));\n\nexport default NewTabDiv;\n","import React from 'react';\nimport TextField from '@mui/material/TextField';\n\n/**\n * react-autosuggest/whatever assumes that `inputProps` will be applied\n * to a standard input element. We need to pick off and apply certain props\n * to the root `TextField` component and pass the rest down through the\n * `InputProps` of the `TextField`.\n *\n * To make sure we remain in control of the props available on `TextField`\n * use the `textField` key when creating `inputProps`.\n */\nexport default function renderDefaultTextInput(inputProps: any) {\n const { value, placeholder, autoFocus, textFieldProps, ...otherInputProps } = inputProps;\n\n return (\n \n );\n}\n","import React from 'react';\nimport MenuItem from '@mui/material/MenuItem';\nimport match from 'autosuggest-highlight/match';\nimport parse from 'autosuggest-highlight/parse';\n\nexport default function renderDefaultSuggestion(\n suggestion: string,\n { query, isHighlighted }: { query: string; isHighlighted: boolean },\n) {\n const matches = match(suggestion, query);\n const parts = parse(suggestion, matches);\n\n return (\n \n );\n}\n","import React from 'react';\nimport PropTypes from 'prop-types';\n\n// Components\nimport Paper, { PaperProps } from '@mui/material/Paper';\n\nconst DefaultSuggestionsContainer = ({ containerProps, children }: any, paperProps: PaperProps) => {\n return (\n \n
{children}
\n \n );\n};\n\nDefaultSuggestionsContainer.propTypes = {\n containerProps: PropTypes.object,\n children: PropTypes.node,\n};\n\nexport default DefaultSuggestionsContainer;\n","import React from 'react';\nimport classnames from 'classnames';\n\n// Components\nimport ReactAutosuggest, { AutosuggestProps as ReactAutosuggestProps } from 'react-autosuggest';\nimport DefaultTextField from './DefaultTextField';\nimport DefaultSuggestion from './DefaultSuggestion';\nimport DefaultSuggestionsContainer from './DefaultSuggestionContainer';\n\n// Styles\nimport { makeStyles } from 'tss-react/mui';\nimport { Theme, useTheme } from '@mui/material/styles';\n\n// @ts-ignore\ninterface Props extends ReactAutosuggestProps {\n className?: string;\n suggestions: any[];\n inputProps: any;\n renderInputComponent?: any;\n renderSuggestion: any;\n onSuggestionsClearRequested: () => void;\n}\n\n/**\n * Skippers Autosuggest is derived from react-autosuggest. This is\n * a convenience wrapper that gives us some simple defaults.\n *\n * For more details on input / component overrides...\n * Look here https://github.com/moroshko/react-autosuggest#inputPropsProp\n */\nconst Autosuggest = (props: Props) => {\n const theme = useTheme();\n const {\n className,\n inputProps = {\n value: '',\n onChange: () => {},\n placeholder: 'Enter a value',\n },\n suggestions = [],\n renderInputComponent = DefaultTextField,\n renderSuggestion = DefaultSuggestion,\n ...other\n } = props;\n const { classes } = useStyles();\n\n const handleSuggestionsClearRequested = () => {\n if (props.onSuggestionsClearRequested) {\n props.onSuggestionsClearRequested();\n }\n };\n\n const getSuggestionValue = (suggestion: T) => {\n return suggestion;\n };\n\n const renderSuggestionsContainer = (containerProps: any) => {\n return DefaultSuggestionsContainer(containerProps, {\n classes: {\n root: classes.suggestionsPaper,\n },\n });\n };\n\n return (\n \n );\n};\n\nconst useStyles = makeStyles()((theme: Theme) => ({\n root: {\n width: '100%',\n position: 'relative',\n },\n suggestionsPaper: {\n position: 'absolute',\n width: '100%',\n minWidth: 200,\n zIndex: theme.zIndex.max,\n },\n suggestionsContainer: {\n width: '100%',\n },\n suggestionsContainerOpen: {},\n suggestionsList: {\n margin: 0,\n padding: 0,\n listStyleType: 'none',\n },\n suggestion: {\n display: 'block',\n },\n}));\n\nexport default Autosuggest;\n","import hasBadContrast from './hasBadContrast';\nimport ensureColors from './ensureColors';\n\n/**\n * this function requires it be ran through `ensureLightDark`\n */\nexport default function createReadablePalette(palette, onColor) {\n const { originalPrimary, originalSecondary, defaultAccentColor } = ensureColors(palette);\n\n return {\n ...palette,\n primary:\n hasBadContrast(originalPrimary.main, onColor) &&\n hasBadContrast(originalSecondary.main, onColor)\n ? defaultAccentColor\n : hasBadContrast(originalPrimary.main, onColor)\n ? originalSecondary\n : originalPrimary,\n secondary:\n hasBadContrast(originalPrimary.main, onColor) &&\n hasBadContrast(originalSecondary.main, onColor)\n ? defaultAccentColor\n : hasBadContrast(originalSecondary.main, onColor)\n ? originalPrimary\n : originalSecondary,\n originalPrimary: palette.originalPrimary || palette.primary,\n originalSecondary: palette.originalSecondary || palette.secondary,\n };\n}\n","import React from 'react';\nimport { useTheme, Theme } from '@mui/material';\nimport createTheme from './createSkipperTheme';\nimport memoizeLast from '@sift/resift/memoizeLast';\nimport createReadablePalette from 'styles/skipper/createReadablePalette';\n\nimport { ThemeProvider } from '@mui/material/styles';\n\ninterface Props {\n children: React.ReactNode;\n color: string;\n}\n\nfunction ReadableThemedComponent({ children, color }: Props) {\n const theme = useTheme();\n\n const _getOrCalculateFixedTheme = memoizeLast((theme: Theme) => {\n return createTheme({ ...theme, palette: createReadablePalette(theme.palette, color) });\n });\n\n const fixedTheme = _getOrCalculateFixedTheme(theme);\n\n return {children};\n}\n\nexport default ReadableThemedComponent;\n","import { createContextFetch } from 'resift';\nimport orgChartSettingsFetch from 'pages/OrgChart/fetches/orgChartSettingsFetch';\n\nconst {\n useContextFetch: useOrgChartSettings,\n ContextFetchProvider: OrgChartSettingsProvider,\n // @ts-ignore\n} = createContextFetch(orgChartSettingsFetch);\n\nexport { OrgChartSettingsProvider };\nexport default useOrgChartSettings;\n","import { defineFetch } from 'resift';\nimport _get from 'lodash/get';\n\nexport default defineFetch({\n displayName: 'assistants fetch',\n share: {\n namespace: 'person',\n merge: (previous, next) => ({\n ...previous,\n _entityRelationships: {\n ..._get(previous, ['_entityRelationships']),\n ...next,\n },\n }),\n },\n make: (personId: string) => ({\n request: (assistantIds: string[]) => ({ http }: any) =>\n http({\n method: 'POST',\n route: `/people/${personId}/assistants`,\n data: assistantIds.map(id => ({ person: id })),\n }),\n }),\n});\n","import { defineFetch } from 'resift';\nimport _get from 'lodash/get';\n\nexport default defineFetch({\n displayName: 'assistant of fetch',\n share: {\n namespace: 'person',\n merge: (previous, next) => ({\n ...previous,\n _entityRelationships: {\n ..._get(previous, ['_entityRelationships']),\n ...next,\n },\n }),\n },\n make: (personId: string) => ({\n request: (assistantIds: string[]) => ({ http }) =>\n http({\n method: 'POST',\n route: `/people/${personId}/assistantOf`,\n data: assistantIds.map(id => ({ person: id })),\n }),\n }),\n});\n","import moment from 'moment';\nimport pluralize from 'pluralize';\nimport memoizeLast from '@sift/resift/memoizeLast';\n\nfunction getAnniversaryDateContent(anniversaryDate: string) {\n if (!anniversaryDate) {\n return null;\n }\n\n const parsedDate = moment(anniversaryDate, 'MM-DD-YYYY');\n const now = moment();\n\n const years = now.diff(parsedDate, 'years');\n const monthsTotal = now.diff(parsedDate, 'months');\n\n const months = monthsTotal - years * 12;\n\n const yearsPart = years > 0 ? `${years} ${pluralize('years', years)}` : '';\n const _monthsPart = `${months} ${pluralize('months', months)}`;\n\n const monthsPart = (() => {\n if (months > 0) {\n return _monthsPart;\n }\n\n if (yearsPart) {\n return '';\n }\n\n return 'Less than one month';\n })();\n\n const tenure = `${[yearsPart, monthsPart].filter(x => !!x).join(' ')}`;\n const startedDate = parsedDate.format('MMM D, YYYY');\n const tenureWithStartDate = `${tenure}, started on ${startedDate}`;\n\n return {\n tenure,\n tenureWithStartDate,\n };\n}\n\nexport default memoizeLast(getAnniversaryDateContent);\n","import React from 'react';\nimport PropTypes from 'prop-types';\n\nclass SeparatedDetailedCopy extends React.PureComponent {\n static propTypes = {\n person: PropTypes.object.isRequired,\n };\n\n render() {\n const { person } = this.props;\n\n if (person.separationImplicit) {\n return 'This is an approximated date based on removal from Sift rather than official data';\n } else {\n return 'This is an exact separation date provided by official data';\n }\n }\n}\n\nexport default SeparatedDetailedCopy;\n","import React from 'react';\nimport PropTypes from 'prop-types';\n\nimport Button from '@mui/material/Button';\nimport Dialog from '@mui/material/Dialog';\nimport DialogActions from '@mui/material/DialogActions';\nimport DialogContent from '@mui/material/DialogContent';\nimport DialogContentText from '@mui/material/DialogContentText';\nimport DialogTitle from '@mui/material/DialogTitle';\n\nimport SeparatedDetailedCopy from './DetailedCopy';\n\nclass SeparatedDetailDialog extends React.Component {\n static propTypes = {\n person: PropTypes.object.isRequired,\n children: PropTypes.node.isRequired,\n };\n\n state = {\n open: false,\n };\n\n handleClickOpen = () => {\n this.setState({ open: true });\n };\n\n handleClose = () => {\n this.setState({ open: false });\n };\n\n render() {\n const { person, children } = this.props;\n\n return (\n \n
\n );\n}\n\nconst useStyles = makeStyles()(theme => {\n return {\n root: {\n display: 'flex',\n flex: '1 1 auto',\n flexDirection: 'column',\n justifyContent: 'center',\n overflow: 'hidden',\n whiteSpace: 'nowrap',\n textOverflow: 'ellipsis',\n padding: `0 ${theme.spacing(2)}`,\n },\n displayName: {\n ...typography(theme, 'h6'),\n fontWeight: theme.typography.fontWeightBold,\n color: 'inherit',\n lineHeight: 1.35,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n display: 'flex',\n alignItems: 'center',\n },\n altName: {\n ...typography(theme, 'body2'),\n },\n withAltName: {\n marginBottom: theme.spacing(0.4),\n },\n subOverflow: {\n flex: '1 1 auto',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n },\n title: {\n ...typography(theme, 'body2'),\n lineHeight: 1.35,\n color: 'inherit',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n },\n };\n});\n\nItemInfo.propTypes = {\n title: PropTypes.string,\n subtitle: PropTypes.string,\n query: PropTypes.string,\n topRightIcon: PropTypes.node,\n};\n\nexport default ItemInfo;\n","import React from 'react';\nimport PropTypes from 'prop-types';\n\nimport Tooltip from '@mui/material/Tooltip';\nimport ErrorOutline from '@mui/icons-material/ErrorOutline';\nimport SeparatedCopy from './Copy';\n\nimport { makeStyles } from 'tss-react/mui';\n\nfunction SeparatedIcon({ className, person }) {\n const { classes } = useStyles();\n\n return (\n }>\n \n \n );\n}\n\nconst useStyles = makeStyles()(() => {\n return {\n icon: {\n // TODO: use theme\n color: '#ffd436',\n },\n };\n});\n\nSeparatedIcon.propTypes = {\n person: PropTypes.object.isRequired,\n className: PropTypes.string,\n};\n\nexport default SeparatedIcon;\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport { useSelector } from 'react-redux';\nimport _get from 'lodash/get';\nimport { useTheme } from '@mui/material/styles';\nimport getPictureUrl from 'helpers/getPictureUrl';\n\n// Components\nimport MuiAvatar from '@mui/material/Avatar';\n\nimport { makeStyles } from 'tss-react/mui';\nimport classnames from 'classnames';\n\n/**\n * A Persons active avatar. In some configurations an official photo\n * and a custom photo are available to people. This component will\n * be connected to the store to make sure the proper setting is respected\n * and the correct photo is shown.\n */\nfunction Avatar({ size, person, showBorder, className, ...other }) {\n const { classes } = useStyles();\n const { root, border, background, ...avatarClasses } = classes;\n const theme = useTheme();\n const preferOfficialPhotos = useSelector(state =>\n _get(state, 'settings.directory.preferOfficialPhotos', false),\n );\n const officialPictureUrl = person.officialPictureUrl;\n const _pictureUrl = getPictureUrl(\n person.pictureUrl,\n officialPictureUrl,\n preferOfficialPhotos,\n theme,\n );\n\n const name =\n person.firstName && person.lastName\n ? `${person.firstName[0]} ${person.lastName[0]}`\n : person.displayName[0];\n\n return (\n
\n \n
\n );\n}\n\nAvatar.propTypes = {\n /**\n * Person to display photo for.\n */\n person: PropTypes.object.isRequired,\n /**\n * Photo size. We prefere a size prop over size styling to\n * have control over the size of the image that is requested\n * from the API.\n */\n size: PropTypes.number.isRequired,\n /**\n * In most cases we display a white border around the avatar. But\n * in some cases the border will not show up so we allow it to be\n * disabled. Defaults to `true`.\n */\n showBorder: PropTypes.bool,\n /**\n * Defaults to `false` .\n */\n className: PropTypes.string,\n};\n\nconst useStyles = makeStyles()(theme => {\n return {\n root: {\n position: 'relative',\n },\n background: {\n backgroundColor: theme.palette.primary.main,\n },\n border: {\n border: `2px solid ${theme.palette.common.white}`,\n },\n };\n});\n\nexport default Avatar;\n","import React from 'react';\nimport PropTypes from 'prop-types';\n\n// UI\nimport { Link } from 'react-router-dom';\n\nimport { makeStyles } from 'tss-react/mui';\nimport classnames from 'classnames';\n\n/**\n * Need to link to a Person/User/Profile thing? Use m3!\n *\n * This component is wrapping `react-router-dom`s `Link` component. Checkout\n * its API for additional props that are available.\n */\nfunction PersonLink(props) {\n let {\n to,\n person,\n classes: classesFromProps,\n className,\n children,\n staticContext,\n ...otherProps\n } = props;\n const { classes } = useStyles(undefined, { props: { classes: classesFromProps } });\n\n if (!to) {\n to = `/profile/${person.id}`;\n }\n\n return (\n \n {children}\n \n );\n}\n\nconst useStyles = makeStyles()(theme => {\n return {\n root: {\n color: theme.palette.textColor,\n cursor: 'pointer',\n textDecoration: 'none !important',\n '&:hover': {\n color: theme.palette.accent1Color,\n },\n },\n };\n});\n\nPersonLink.propTypes = {\n person: PropTypes.object.isRequired,\n classes: PropTypes.object,\n to: PropTypes.string,\n children: PropTypes.node,\n className: PropTypes.string,\n // from react-router\n staticContext: PropTypes.any,\n};\n\nexport default PersonLink;\n","// React\nimport React from 'react';\nimport _get from 'lodash/get';\nimport { useHistory } from 'react-router';\nimport typography from 'helpers/typography';\n\n// UI\nimport Item from './Item';\nimport PersonItemInfo from './PersonItemInfo';\nimport ResultItemPropTypes from './ResultItemPropTypes';\nimport SeparatedIcon from 'components/People/Separated/Icon';\nimport Org from 'components/Icons/Org';\nimport IconButton from '@mui/material/IconButton';\nimport Tooltip from '@mui/material/Tooltip';\n\nimport Avatar from 'components/People/Avatar';\nimport PersonLink from 'components/Link/PersonLink';\n\nimport { makeStyles } from 'tss-react/mui';\n\nfunction PersonItem(props) {\n const { value: person, fieldKey, query, ...otherProps } = props;\n const { classes } = useStyles();\n const history = useHistory();\n const { id, displayName, title, separationDate, _alternateFieldValues } = person;\n const altDisplayName = _get(_alternateFieldValues, 'displayName');\n\n const routeToOrgChart = e => {\n e.preventDefault();\n history.push({\n pathname: `/orgchart/${id}`,\n });\n };\n\n const matched = _get(person, '_matchedOn', [])\n .map(match => match.field)\n .join(', ');\n\n return (\n \n (\n
\n \n \n \n \n \n
\n )}\n >\n \n\n \n ) : null\n }\n query={query}\n />\n \n \n );\n}\n\nconst useStyles = makeStyles()(theme => {\n return {\n root: {\n display: 'flex',\n overflow: 'hidden',\n },\n linkRoot: {\n width: '100%',\n },\n border: {\n border: `1px solid ${theme.palette.common.white}`,\n },\n text: {\n ...typography(theme, 'body1'),\n fontWeight: theme.typography.fontWeightBold,\n },\n rightIcon: {\n fill: theme.palette.primary.contrastText,\n },\n };\n});\n\nPersonItem.propTypes = ResultItemPropTypes;\n\nexport default PersonItem;\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport { useSnackbar } from 'notistack';\nimport CopyToClipboard from 'react-copy-to-clipboard';\nimport readableColor from 'styles/skipper/readableColor';\n\nimport { makeStyles } from 'tss-react/mui';\nimport classNames from 'classnames';\n\nimport IconButton from '@mui/material/IconButton';\nimport CopyIcon from '@mui/icons-material/FileCopyOutlined';\n\n/**\n * Want to link to an attributes action? Use this link!\n *\n * This component is wrapping `react-router-dom`s `Link` component. Checkout\n * its API for additional props that are available.\n */\nfunction FieldLink(props) {\n let {\n value,\n fieldType,\n classes: classesFromProps,\n className,\n children,\n closeSnackbar,\n themed,\n handleClickChildren,\n handleClickCopyIcon,\n ...otherProps\n } = props;\n const { classes } = useStyles(undefined, { props: { classes: classesFromProps } });\n const { enqueueSnackbar } = useSnackbar();\n\n const handleCopy = () => {\n if (handleClickCopyIcon) handleClickCopyIcon();\n enqueueSnackbar(`“${value}” copied to clipboard!`, {\n variant: 'success',\n });\n };\n\n let to = null;\n\n switch (fieldType) {\n case 'email':\n to = `mailto:${value}`;\n break;\n case 'phoneNumber':\n if (typeof value !== 'string') {\n to = `tel:`;\n } else {\n to = `tel:${value.replace(/[^\\d]/g, '')}`;\n }\n break;\n case 'phone':\n if (typeof value !== 'string') {\n to = `tel:`;\n } else {\n to = `tel:${value.replace(/[^\\d]/g, '')}`;\n }\n break;\n case 'link':\n otherProps = {\n ...otherProps,\n target: '_blank',\n };\n if (typeof value !== 'string') {\n to = `#`;\n } else {\n if (!value.match(/http/i)) {\n to = `http://${value}`;\n } else {\n to = value;\n }\n }\n break;\n case 'hyperlink':\n otherProps = {\n ...otherProps,\n target: '_blank',\n rel: 'noopener noreferrer',\n };\n if (typeof value !== 'string') {\n to = `#`;\n } else {\n if (!value.match(/http/i)) {\n to = `http://${value}`;\n } else {\n to = value;\n }\n }\n break;\n case 'presence':\n to = `im:sip:${value}`;\n break;\n\n default:\n break;\n }\n\n return (\n