{"version":3,"file":"index.2696e915.js","sources":["../../src/Services/AuthService.ts","../../src/Contexts/AuthContext.ts","../../src/Providers/AuthProvider.tsx","../../src/Hooks/useAuth.ts","../../src/Assets/logo.png","../../src/Views/Loading.tsx","../../src/Hooks/useApi.ts","../../src/Views/Navbar.tsx","../../src/Views/Sidebar.tsx","../../src/Views/Layout.tsx","../../src/Contexts/UsersContext.ts","../../src/Providers/UsersProvider.tsx","../../src/Contexts/OrganizationContext.ts","../../src/Providers/OrganizationProvider.tsx","../../src/Routes/PrivateRoute.tsx","../../src/Views/Shared/Pager.tsx","../../src/Views/Shared/LetterFilter.tsx","../../src/Views/Shared/TextControl.tsx","../../src/Views/Shared/TabHeader.tsx","../../src/Views/Shared/Tab.tsx","../../src/Views/Shared/TabContent.tsx","../../src/Views/Contact/ContactTable.tsx","../../src/Views/Shared/Notes.tsx","../../src/Views/Shared/SelectControl.tsx","../../src/Assets/StateCodes.ts","../../src/Hooks/useOrganization.ts","../../src/Views/Shared/TextAreaControl.tsx","../../src/Views/Shared/CheckboxControl.tsx","../../src/Views/Shared/CustomFieldsEditor.tsx","../../src/types.ts","../../src/Views/Shared/AddressEditor.tsx","../../src/Views/Ticket/TicketTable.tsx","../../src/Views/Shared/FileTable.tsx","../../src/Views/Company/EditCompany.tsx","../../src/Views/Shared/TextDisplay.tsx","../../src/Views/Shared/Display.tsx","../../src/Views/Shared/SelectDisplay.tsx","../../src/Views/Shared/TextAreaDisplay.tsx","../../src/Views/Shared/CheckboxDisplay.tsx","../../src/Views/Shared/CustomFieldsDisplay.tsx","../../src/Views/Shared/AddressDisplay.tsx","../../src/Views/Shared/Timeline.tsx","../../src/Views/Shared/TranscriptTable.tsx","../../src/Assets/TicketStatus.ts","../../src/Hooks/useQuery.ts","../../src/Views/Ticket/EditTicket.tsx","../../src/Views/Contact/EditContact.tsx","../../src/Views/Shared/DisplayLink.tsx","../../src/Views/Shared/Card.tsx","../../src/Views/Dashboard/RecentlyUpdatedCompanies.tsx","../../src/Views/Dashboard/RecentlyUpdatedContacts.tsx","../../src/Views/Dashboard/RecentlyUpdatedTickets.tsx","../../src/Routes/Routes.tsx","../../src/Views/Auth/LoginErrorCallback.tsx","../../src/Views/Auth/LoginCallback.tsx","../../src/Views/Auth/Logout.tsx","../../src/Views/Auth/LogoutCallback.tsx","../../src/Views/Company/ViewCompany.tsx","../../src/Views/Company/Companies.tsx","../../src/Views/Contact/ViewContact.tsx","../../src/Views/Contact/Contacts.tsx","../../src/Views/Ticket/ViewTicket.tsx","../../src/Views/Ticket/Tickets.tsx","../../src/Views/Dashboard/index.tsx","../../src/App.tsx","../../src/main.tsx"],"sourcesContent":["interface Token {\r\n token: string;\r\n expires: Date;\r\n}\r\n\r\nclass AuthService {\r\n constructor() {\r\n this.loadTokenFromStorage();\r\n this.loadLegacyTokenFromStorage();\r\n this.loadProfileFromStorage();\r\n }\r\n\r\n token: Token = {\r\n token: \"\",\r\n expires: new Date(Date.now() - 1000),\r\n };\r\n\r\n legacyToken: Token = {\r\n token: \"\",\r\n expires: new Date(Date.now() - 1000),\r\n };\r\n\r\n profile = {\r\n userName: \"\",\r\n picture: \"\",\r\n firstName: \"\",\r\n lastName: \"\",\r\n };\r\n\r\n loadProfileFromStorage() {\r\n const profileJson = sessionStorage.getItem(\"profile\");\r\n\r\n if (profileJson) {\r\n this.profile = JSON.parse(profileJson);\r\n }\r\n }\r\n\r\n loadTokenFromStorage() {\r\n const item = sessionStorage.getItem(\"token\");\r\n\r\n if (!item) {\r\n return;\r\n }\r\n\r\n const data = JSON.parse(item);\r\n\r\n this.token = {\r\n token: data.token,\r\n expires: new Date(data.expires),\r\n };\r\n }\r\n\r\n loadLegacyTokenFromStorage() {\r\n const item = sessionStorage.getItem(\"legacyToken\");\r\n\r\n if (!item) {\r\n return;\r\n }\r\n\r\n const data = JSON.parse(item);\r\n\r\n this.legacyToken = {\r\n token: data.token,\r\n expires: new Date(data.expires),\r\n };\r\n }\r\n\r\n isAuthenticated() {\r\n if (!this.token) {\r\n return false;\r\n }\r\n\r\n return this.token.expires.getTime() > Date.now() && this.token.token !== \"\";\r\n }\r\n\r\n loginRedirect() {\r\n sessionStorage.setItem(\"redirectUri\", window.location.pathname);\r\n const redirectUri = `${window.location.origin}/login/callback`;\r\n const qs = `redirectUri=${encodeURIComponent(redirectUri)}`;\r\n const url = `${import.meta.env.VITE_AUTH_URL}/identity/authenticate?${qs}`;\r\n window.location.replace(url);\r\n }\r\n\r\n logout() {\r\n const redirectUri = `${window.location.origin}/logout/callback`;\r\n const qs = `redirectUri=${encodeURIComponent(redirectUri)}`;\r\n const url = `${import.meta.env.VITE_AUTH_URL}/account/logout?${qs}`;\r\n window.location.replace(url);\r\n }\r\n\r\n logoutCallback() {\r\n sessionStorage.removeItem(\"token\");\r\n sessionStorage.removeItem(\"legacyToken\");\r\n window.location.replace(\"/\");\r\n }\r\n\r\n retryLogin() {\r\n const url = sessionStorage.getItem(\"redirectUri\") || \"/\";\r\n window.location.replace(url);\r\n }\r\n\r\n async refresh(temporaryToken: string) {\r\n await this.fetchAccessToken(temporaryToken);\r\n console.log(\"auth token refreshed\");\r\n }\r\n\r\n async fetchAccessToken(temporaryToken: string) {\r\n const response = await fetch(`${import.meta.env.VITE_API_URL}/token`, {\r\n method: \"post\",\r\n body: JSON.stringify({ temporaryToken }),\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n },\r\n });\r\n\r\n if (!response.ok) {\r\n console.error(\"unable to acquire access token\");\r\n }\r\n\r\n const data = await response.json();\r\n\r\n this.token = {\r\n token: data.token.token,\r\n expires: new Date(data.token.expires),\r\n };\r\n\r\n this.legacyToken = {\r\n token: data.legacyToken.token,\r\n expires: new Date(data.legacyToken.expires),\r\n };\r\n\r\n this.profile = data.profile;\r\n\r\n sessionStorage.setItem(\"token\", JSON.stringify(this.token));\r\n sessionStorage.setItem(\"legacyToken\", JSON.stringify(this.legacyToken));\r\n sessionStorage.setItem(\"profile\", JSON.stringify(data.profile));\r\n }\r\n\r\n async handleLoginCallback() {\r\n const params = new URLSearchParams(window.location.search);\r\n const temporaryToken = params.get(\"token\")!;\r\n try {\r\n await this.fetchAccessToken(temporaryToken);\r\n window.location.replace(sessionStorage.getItem(\"redirectUri\")!);\r\n } catch (err) {\r\n window.location.replace(\"/login/callback/error\");\r\n }\r\n }\r\n}\r\n\r\nconst authService = new AuthService();\r\n\r\nfunction silentRenew() {\r\n let frameRemoved = false;\r\n\r\n console.log(\"silent renew\");\r\n const frame = document.createElement(\"iframe\");\r\n const qs = `originUri=${encodeURIComponent(window.location.origin)}`;\r\n frame.src = `${import.meta.env.VITE_AUTH_URL}/identity/silentrenew?${qs}`;\r\n frame.style.width = \"0\";\r\n frame.style.height = \"0\";\r\n frame.style.border = \"0\";\r\n frame.style.border = \"none\";\r\n frame.style.position = \"absolute\";\r\n\r\n const destroy = () => {\r\n document.body.removeChild(frame);\r\n window.removeEventListener(\"message\", handleMessage);\r\n frameRemoved = true;\r\n };\r\n\r\n const handleMessage = (message: any) => {\r\n try {\r\n if (message.origin === import.meta.env.VITE_AUTH_URL) {\r\n const data = JSON.parse(message.data);\r\n if (data.type === \"silentrenew\") {\r\n if (data.isAuthenticated) {\r\n authService.refresh(data.token);\r\n } else {\r\n authService.logout();\r\n }\r\n\r\n destroy();\r\n }\r\n }\r\n } catch (err) {\r\n console.error(err);\r\n destroy();\r\n }\r\n };\r\n\r\n window.addEventListener(\"message\", handleMessage);\r\n document.body.appendChild(frame);\r\n\r\n setTimeout(() => {\r\n if (!frameRemoved) {\r\n console.log(\"silent renew timeout\");\r\n destroy();\r\n }\r\n }, 30000);\r\n}\r\n\r\nlet checkAccessTokenTimeout: any;\r\n\r\n/**\r\n * Check the access token to see if it needs to be refreshed.\r\n * When the token is close to expiration, a call will be made to renew it.\r\n */\r\n\r\nfunction checkAccessToken() {\r\n console.log(\"checking token expiration\");\r\n const expires = authService.token.expires;\r\n const delta = expires.getTime() - Date.now() - 1000 * 60 * 10;\r\n console.log(`token expires in ${expires.getTime() - Date.now()}`);\r\n console.log(`token expiry delta ${delta}`);\r\n\r\n if (delta < 0) {\r\n console.log(\"token expires soon, renewing\");\r\n silentRenew();\r\n }\r\n\r\n clearTimeout(checkAccessTokenTimeout);\r\n checkAccessTokenTimeout = setTimeout(() => checkAccessToken(), 30000);\r\n}\r\n\r\n\r\nwindow.addEventListener(\"focus\", () => {\r\n checkAccessToken();\r\n});\r\n\r\ncheckAccessTokenTimeout = setTimeout(() => checkAccessToken(), 30000);\r\n\r\nexport default authService;\r\n","import React from \"react\";\r\nimport AuthService from \"../Services/AuthService\";\r\nexport const AuthContext = React.createContext(AuthService);","import React from \"react\";\r\nimport { AuthContext } from \"../Contexts/AuthContext\";\r\nimport AuthService from \"../Services/AuthService\";\r\n\r\nexport function AuthProvider(props: { children: React.ReactNode }) {\r\n return (\r\n \r\n {props.children}\r\n \r\n );\r\n}","import React from \"react\";\r\nimport { AuthContext } from \"../Contexts/AuthContext\";\r\n\r\nexport default function useAuth() {\r\n return React.useContext(AuthContext);\r\n}","export default \"__VITE_ASSET__27d8e524__\"","import React from 'react';\r\nimport logoUrl from \"../Assets/logo.png\"\r\n\r\n\r\nexport default function Loading() {\r\n return (\r\n
\r\n \"loading\"\r\n
\r\n );\r\n}","import React from \"react\";\r\nimport useAuth from \"./useAuth\";\r\nimport $ from 'jquery';\r\n\r\nclass Api {\r\n accessToken: string;\r\n\r\n constructor(accessToken: string) {\r\n this.accessToken = accessToken;\r\n }\r\n\r\n get(endpoint: string, data?:any) {\r\n return fetch(`${import.meta.env.VITE_API_URL}/${endpoint}${data ? \"?\" + $.param(data) : ''}`, {\r\n method: \"get\",\r\n headers: {\r\n Authorization: `Bearer ${this.accessToken}`,\r\n },\r\n });\r\n }\r\n\r\n post(endpoint: string, data?: any) {\r\n return fetch(`${import.meta.env.VITE_API_URL}/${endpoint}`, {\r\n method: \"post\",\r\n body: data ? JSON.stringify(data) : undefined,\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n Authorization: `Bearer ${this.accessToken}`,\r\n },\r\n });\r\n }\r\n\r\n uploadFile(endpoint: string, data: FormData) {\r\n return fetch(`${import.meta.env.VITE_API_URL}/${endpoint}`, {\r\n method: \"post\",\r\n body: data,\r\n headers: {\r\n Authorization: `Bearer ${this.accessToken}`,\r\n },\r\n });\r\n }\r\n\r\n put(endpoint: string, data?: any) {\r\n return fetch(`${import.meta.env.VITE_API_URL}/${endpoint}`, {\r\n method: \"put\",\r\n body: data ? JSON.stringify(data) : undefined,\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n Authorization: `Bearer ${this.accessToken}`,\r\n },\r\n });\r\n }\r\n\r\n delete(endpoint: string, data?: any) {\r\n return fetch(`${import.meta.env.VITE_API_URL}/${endpoint}`, {\r\n method: \"delete\",\r\n body: data ? JSON.stringify(data) : undefined,\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n Authorization: `Bearer ${this.accessToken}`,\r\n },\r\n });\r\n }\r\n}\r\n\r\nexport default function useApi() {\r\n const auth = useAuth();\r\n return React.useMemo(() => new Api(auth.token.token), [auth.token.token]);\r\n}","import React from 'react';\r\nimport { Link } from 'react-router-dom';\r\nimport { SyncIcon, ThreeBarsIcon } from '@primer/octicons-react'\r\nimport _ from 'underscore';\r\nimport useApi from '../Hooks/useApi';\r\nimport { SearchResult } from '../types';\r\nimport logoUrl from \"../Assets/logo.png\";\r\n\r\n\r\n\r\nexport default function Navbar() {\r\n const [searchResults, setSearchResults] = React.useState>([]);\r\n const [searching, setSearching] = React.useState(false);\r\n const [showingSearchResults, setShowingSearchResults] = React.useState(false);\r\n const [term, setTerm] = React.useState('');\r\n\r\n const api = useApi();\r\n const search = _.debounce(async (term: string) => {\r\n setSearching(true);\r\n const response = await api.get('api/record/search', { term });\r\n const results = await response.json();\r\n setSearchResults(results);\r\n setShowingSearchResults(true);\r\n setSearching(false);\r\n }, 1000);\r\n\r\n React.useEffect(() => {\r\n document.body.addEventListener('click', closeSearchResults);\r\n\r\n return function cleanup() {\r\n window.removeEventListener('click', closeSearchResults );\r\n } \r\n },[]);\r\n\r\n const closeSearchResults = () => {\r\n setShowingSearchResults(false);\r\n setSearchResults([]);\r\n setTerm('');\r\n }\r\n\r\n const debouncedSearch = React.useCallback(e => {\r\n const term = e.currentTarget.value;\r\n setTerm(term);\r\n if (!term) {\r\n return;\r\n }\r\n search(term);\r\n }, [search]);\r\n\r\n const apiMap = {\r\n \"Contact\": \"contacts\",\r\n \"Ticket\": \"tickets\",\r\n \"Company\": \"companies\"\r\n }\r\n\r\n const renderSearchResults = React.useCallback(() => {\r\n if (!showingSearchResults) {\r\n return null;\r\n }\r\n\r\n if (!searchResults.length) {\r\n return (\r\n \r\n )\r\n }\r\n\r\n return (\r\n \r\n )\r\n }, [searchResults, showingSearchResults, apiMap]);\r\n\r\n return (\r\n \r\n );\r\n}","import React from 'react';\r\nimport { Link } from 'react-router-dom';\r\nimport { OrganizationIcon, HomeFillIcon, PeopleIcon, ChecklistIcon, SignOutIcon, GearIcon } from '@primer/octicons-react'\r\nimport authService from '../Services/AuthService';\r\n\r\n\r\nexport default function Navbar() {\r\n \r\n const [path, setPath] = React.useState(window.location.pathname);\r\n\r\n var onClickLink = React.useCallback((e) => {\r\n setPath(e.currentTarget.pathname);\r\n },[setPath]);\r\n\r\n var getClassName = React.useCallback((link:string) => {\r\n var className = \"nav-link\";\r\n if(link === path){\r\n className += \" active\";\r\n }\r\n\r\n return className;\r\n }, [path]);\r\n\r\n\r\n return (\r\n \r\n );\r\n} ","import React from 'react';\r\nimport Navbar from './Navbar';\r\nimport Sidebar from './Sidebar';\r\ninterface Props {\r\n children: React.ReactNode;\r\n }\r\n\r\nexport default function Layout(props:Props){\r\n return(\r\n
\r\n
\r\n
\r\n \r\n \r\n
\r\n {props.children}\r\n
\r\n
\r\n
\r\n
\r\n )\r\n}","import React from \"react\";\r\nimport { User } from \"../types\";\r\n\r\nexport const UsersContext = React.createContext>([])","import React from \"react\";\r\nimport { UsersContext } from \"../Contexts/UsersContext\";\r\nimport useApi from \"../Hooks/useApi\";\r\nimport { User } from \"../types\";\r\nimport Loading from \"../Views/Loading\";\r\n\r\nexport function UsersProvider(props: { children: React.ReactNode }) {\r\n const api = useApi();\r\n const [users, setUsers] = React.useState>([]);\r\n const [loading, setLoading] = React.useState(true);\r\n\r\n const load = React.useCallback(async() =>{\r\n const response = await api.get('api/user');\r\n setUsers(await response.json());\r\n setLoading(false);\r\n },[api]);\r\n\r\n React.useEffect(() => {\r\n load();\r\n },[load])\r\n\r\n if(loading){\r\n return \r\n }\r\n\r\n\r\n return (\r\n \r\n {props.children}\r\n \r\n );\r\n}","import React from \"react\";\r\nimport { Organization } from \"../types\";\r\n\r\nexport const OrganizationContext = React.createContext({\r\n velaroSiteId: undefined,\r\n expirationDate: new Date(),\r\n name:\"\",\r\n velaroUrl:\"\",\r\n customFieldDefinition: {\r\n address: [],\r\n ticket: [],\r\n contact: [],\r\n company:[]\r\n }\r\n });","import React from \"react\";\r\nimport { OrganizationContext } from \"../Contexts/OrganizationContext\";\r\nimport useApi from \"../Hooks/useApi\";\r\nimport { Organization } from \"../types\";\r\nimport Loading from \"../Views/Loading\";\r\n\r\nexport function OrganizationProvider(props: { children: React.ReactNode }) {\r\n const api = useApi();\r\n const [organization, setOrganization] = React.useState({\r\n customFieldDefinition: {\r\n address: [],\r\n company: [],\r\n contact: [],\r\n ticket: []\r\n },\r\n expirationDate: new Date(),\r\n name: \"\",\r\n velaroUrl: \"\",\r\n velaroSiteId: 0\r\n });\r\n const [loading, setLoading] = React.useState(true);\r\n\r\n const load = React.useCallback(async () => {\r\n const response = await api.get('api/organization');\r\n setOrganization(await response.json());\r\n setLoading(false);\r\n }, [api]);\r\n\r\n React.useEffect(() => {\r\n load();\r\n }, [load])\r\n\r\n if (loading) {\r\n return \r\n }\r\n\r\n\r\n return (\r\n \r\n {props.children}\r\n \r\n );\r\n}","import React from \"react\";\r\nimport { Route } from \"react-router-dom\";\r\nimport useAuth from \"../Hooks/useAuth\";\r\nimport Loading from \"../Views/Loading\";\r\nimport Layout from \"../Views/Layout\";\r\nimport { UsersProvider } from \"../Providers/UsersProvider\";\r\nimport { OrganizationProvider } from \"../Providers/OrganizationProvider\";\r\n\r\nexport function PrivateRoute(props: any) {\r\n const { component, ...rest } = props;\r\n const auth = useAuth();\r\n\r\n const renderFn = (Component: any) => (props: any) => {\r\n if (!!Component && auth.isAuthenticated()) {\r\n return (\r\n \r\n \r\n \r\n \r\n \r\n );\r\n } else {\r\n auth.loginRedirect();\r\n }\r\n };\r\n\r\n return ;\r\n}","import React from 'react';\r\n\r\ninterface Props {\r\n page: number;\r\n setPage(page: number): void;\r\n totalRecordCount: number;\r\n pageSize: number;\r\n}\r\n\r\nexport default function Pager(props: Props) {\r\n const numberOfPages = Math.ceil(props.totalRecordCount / props.pageSize);\r\n\r\n var renderPages = React.useCallback(() => {\r\n let startPage = props.page - 2;\r\n let endPage = props.page + 2;\r\n if (startPage < 0) {\r\n startPage = 0;\r\n }\r\n\r\n if (startPage > 2 && endPage > props.page + 2) {\r\n endPage = props.page + 2;\r\n }\r\n\r\n if (startPage <= 2 && endPage > 4) {\r\n endPage = 4;\r\n startPage = 0;\r\n }\r\n\r\n if (endPage > 4) {\r\n startPage = endPage - 4;\r\n }\r\n\r\n const maxPage = props.totalRecordCount / props.pageSize;\r\n if (endPage > maxPage) {\r\n endPage = maxPage;\r\n }\r\n\r\n let pages = [];\r\n for (var i = startPage; i <= endPage; i++) {\r\n pages.push(i);\r\n }\r\n\r\n return pages.map((p) => {\r\n let className = \"page-item\";\r\n if (p === props.page) {\r\n className += \" active\";\r\n }\r\n return
  • \r\n })\r\n\r\n\r\n }, [props])\r\n\r\n if (numberOfPages < 1) {\r\n return null;\r\n }\r\n\r\n return (\r\n \r\n )\r\n}","import React from 'react';\r\n\r\ninterface Props {\r\n filter: string;\r\n setFilter(filter:string):void;\r\n}\r\n\r\nexport default function LetterFilter(props:Props){\r\n const alphabet = ['All', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']\r\n \r\n function onClick(e:any, filter:string) {\r\n e.preventDefault();\r\n if(filter === 'All'){\r\n filter = '';\r\n }\r\n props.setFilter(filter);\r\n }\r\n\r\n return
    \r\n {alphabet.map(letter => {\r\n let className = letter === props.filter ? 'active': '';\r\n if(letter === 'All' && !props.filter){\r\n className = 'active';\r\n }\r\n return onClick(e,letter)}>{letter}\r\n })}\r\n
    \r\n}","import React from 'react';\r\n\r\ninterface Props {\r\n setValue(input:string): void;\r\n label: string;\r\n value: string;\r\n}\r\n\r\nexport default function TextControl(props: Props){\r\n return (\r\n
    \r\n \r\n props.setValue(e.currentTarget.value)} />\r\n
    \r\n )\r\n}","import React from 'react';\r\n\r\ninterface Props {\r\n \r\n}\r\n\r\nexport default function TabHeader(props: React.PropsWithChildren) {\r\n return (\r\n
    \r\n
    \r\n
      \r\n {props.children}\r\n
    \r\n
    \r\n
    \r\n )\r\n}","import React from 'react';\r\n\r\ninterface Props {\r\n showing: string;\r\n label:string;\r\n setShowing:any;\r\n}\r\n\r\nexport default function Tab(props: Props) {\r\n return (\r\n
  • \r\n {\r\n e.preventDefault();\r\n props.setShowing(props.label);\r\n }}>{props.label}\r\n
  • \r\n )\r\n}","import React from 'react';\r\n\r\ninterface Props {\r\n \r\n}\r\n\r\nexport default function TabContent(props: React.PropsWithChildren) {\r\n return (\r\n
    \r\n
    \r\n {props.children}\r\n
    \r\n
    \r\n )\r\n}","import React from 'react';\r\nimport Loading from '../Loading';\r\nimport useApi from '../../Hooks/useApi';\r\nimport { Contact, PagedContacts } from '../../types';\r\nimport { Link } from 'react-router-dom';\r\nimport moment from 'moment';\r\nimport Pager from '../Shared/Pager';\r\nimport LetterFilter from '../Shared/LetterFilter';\r\n\r\ninterface Props {\r\n companyId?: string;\r\n}\r\n\r\nexport default function ContactTable(props: Props) {\r\n const [loading, setLoading] = React.useState(true);\r\n const [contacts, setContacts] = React.useState>([]);\r\n const [page, setPage] = React.useState(0);\r\n const [totatRecordCount, setTotalRecordCount] = React.useState(0);\r\n const [filter, setFilter] = React.useState(\"\");\r\n const api = useApi();\r\n\r\n const loadContacts = React.useCallback(async () => {\r\n setLoading(true);\r\n const response = await api.get('api/contacts/page', { page, filter, companyId: props.companyId });\r\n const paged = (await response.json()) as PagedContacts;\r\n setContacts(paged.contacts);\r\n setTotalRecordCount(paged.totalRecordCount);\r\n setLoading(false);\r\n }, [page, filter, api, props.companyId]);\r\n\r\n React.useEffect(() => {\r\n loadContacts();\r\n }, [loadContacts]);\r\n\r\n\r\n const renderLoading = React.useCallback(() => {\r\n if (!loading) {\r\n return null;\r\n }\r\n return \r\n \r\n \r\n \r\n \r\n }, [loading]);\r\n\r\n const renderContact = React.useCallback((contact: Contact) => {\r\n const href = `/contacts/${contact.id}`;\r\n return (\r\n \r\n {contact.name}\r\n {moment.utc(contact.createdOn).local().calendar()}\r\n {moment.utc(contact.lastUpdated).local().calendar()}\r\n {contact.phoneNumber}\r\n {contact.emailAddress}\r\n \r\n )\r\n }, []);\r\n\r\n const renderEmpty = React.useCallback(() => {\r\n if (contacts.length || loading) {\r\n return null;\r\n }\r\n\r\n return (\r\n \r\n \r\n
    No results
    \r\n \r\n \r\n )\r\n }, [contacts, loading]);\r\n\r\n const updateFilter = React.useCallback((value: string) => {\r\n setPage(0);\r\n setFilter(value);\r\n }, [])\r\n\r\n\r\n return (\r\n <>\r\n {props.companyId &&\r\n
    \r\n
    \r\n
    \r\n Add Contact\r\n
    \r\n
    \r\n
    \r\n
    }\r\n
    \r\n
    \r\n \r\n
    \r\n
    \r\n
    \r\n \r\n
    \r\n
    \r\n
    \r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {renderLoading()}\r\n {contacts.map(renderContact)}\r\n {renderEmpty()}\r\n \r\n
    NameCreated OnLast UpdatedPhone NumberEmail Address
    \r\n \r\n );\r\n}","import React from 'react';\r\nimport { Note } from '../../types';\r\nimport useApi from '../../Hooks/useApi';\r\nimport moment from 'moment';\r\nimport Loading from '../Loading';\r\n\r\ninterface Props {\r\n companyId?: string;\r\n contactId?: string;\r\n ticketId?: string;\r\n}\r\n\r\nexport default React.memo((props: Props) => {\r\n const api = useApi();\r\n const [loading, setLoading] = React.useState(true);\r\n const [reachedEnd, setReachedEnd] = React.useState(false);\r\n const [notes, setNotes] = React.useState>([]);\r\n const [content, setContent] = React.useState(\"\");\r\n\r\n \r\n const getNotes = React.useCallback(async():Promise => {\r\n let route = 'company';\r\n let params = {\r\n id: props.companyId || props.contactId || props.ticketId,\r\n skip: notes.length\r\n }\r\n\r\n if(props.contactId){\r\n route = 'contact'\r\n }\r\n \r\n if(props.ticketId){\r\n route = 'ticket'\r\n }\r\n\r\n return await api.get(`api/notes/${route}`, params);\r\n },[api, notes.length, props.companyId, props.contactId, props.ticketId]);\r\n\r\n const loadNotes = React.useCallback(async() => {\r\n setLoading(true);\r\n const response = await getNotes();\r\n var noteResponse = (await response.json()) as Array;\r\n setReachedEnd(noteResponse.length === 0);\r\n setNotes(n => [...n, ...noteResponse]);\r\n setLoading(false);\r\n }, [getNotes]);\r\n\r\n var handleScroll = React.useCallback((e:any) => {\r\n if(!reachedEnd && e.currentTarget.scrollTop - e.currentTarget.offsetHeight === -e.currentTarget.scrollHeight){\r\n loadNotes();\r\n }\r\n }, [reachedEnd, loadNotes]);\r\n\r\n\r\n async function addNote() {\r\n if(!content.trim()){\r\n return;\r\n }\r\n\r\n const response = await api.post('api/notes/postnote',{\r\n content,\r\n companyId: props.companyId,\r\n contactId: props.contactId,\r\n ticketId: props.ticketId\r\n });\r\n const note = await response.json();\r\n setNotes([note, ...notes]);\r\n setContent('');\r\n }\r\n\r\n const renderNote = React.useCallback((note: Note) => {\r\n return (\r\n
    \r\n
    \r\n
    {note.content}
    \r\n
    \r\n {moment.utc(note.createdOn).local().calendar()}\r\n
    \r\n )\r\n }, [])\r\n\r\n function onKeyPress(e:any){\r\n if(e.key === \"Enter\" && !e.shiftKey){\r\n addNote();\r\n }\r\n }\r\n\r\n\r\n const renderLoading = React.useCallback(() => {\r\n if(!loading){\r\n return null;\r\n }\r\n return \r\n }, [loading]);\r\n\r\n const renderEmpty = React.useCallback(() => {\r\n if(loading || notes.length){\r\n return null;\r\n }\r\n return (\r\n
    \r\n
    No notes exist
    \r\n
    \r\n )\r\n }, [loading, notes]);\r\n\r\n React.useEffect(() => {\r\n loadNotes();\r\n }, [loadNotes]);\r\n\r\n\r\n return (\r\n <>\r\n
    \r\n {notes.map(renderNote)}\r\n {renderLoading()}\r\n {renderEmpty()}\r\n
    \r\n\r\n \r\n \r\n \r\n )\r\n})","import React from 'react';\r\nimport { SelectOption } from '../../types';\r\n\r\ninterface Props {\r\n setValue(input:any): void;\r\n label: string;\r\n value: string | number;\r\n options: Array\r\n allowEmpty?:boolean;\r\n}\r\n\r\nexport default function SelectControl(props: Props){\r\n function renderOption(selectOption: SelectOption){\r\n return \r\n }\r\n\r\n return (\r\n
    \r\n \r\n \r\n
    \r\n )\r\n}","export default [\r\n {\r\n label: \"Alabama\",\r\n value: \"AL\"\r\n },\r\n {\r\n label: \"Alaska\",\r\n value: \"AK\"\r\n },\r\n {\r\n label: \"American Samoa\",\r\n value: \"AS\"\r\n },\r\n {\r\n label: \"Arizona\",\r\n value: \"AZ\"\r\n },\r\n {\r\n label: \"Arkansas\",\r\n value: \"AR\"\r\n },\r\n {\r\n label: \"California\",\r\n value: \"CA\"\r\n },\r\n {\r\n label: \"Colorado\",\r\n value: \"CO\"\r\n },\r\n {\r\n label: \"Connecticut\",\r\n value: \"CT\"\r\n },\r\n {\r\n label: \"Delaware\",\r\n value: \"DE\"\r\n },\r\n {\r\n label: \"District Of Columbia\",\r\n value: \"DC\"\r\n },\r\n {\r\n label: \"Federated States Of Micronesia\",\r\n value: \"FM\"\r\n },\r\n {\r\n label: \"Florida\",\r\n value: \"FL\"\r\n },\r\n {\r\n label: \"Georgia\",\r\n value: \"GA\"\r\n },\r\n {\r\n label: \"Guam\",\r\n value: \"GU\"\r\n },\r\n {\r\n label: \"Hawaii\",\r\n value: \"HI\"\r\n },\r\n {\r\n label: \"Idaho\",\r\n value: \"ID\"\r\n },\r\n {\r\n label: \"Illinois\",\r\n value: \"IL\"\r\n },\r\n {\r\n label: \"Indiana\",\r\n value: \"IN\"\r\n },\r\n {\r\n label: \"Iowa\",\r\n value: \"IA\"\r\n },\r\n {\r\n label: \"Kansas\",\r\n value: \"KS\"\r\n },\r\n {\r\n label: \"Kentucky\",\r\n value: \"KY\"\r\n },\r\n {\r\n label: \"Louisiana\",\r\n value: \"LA\"\r\n },\r\n {\r\n label: \"Maine\",\r\n value: \"ME\"\r\n },\r\n {\r\n label: \"Marshall Islands\",\r\n value: \"MH\"\r\n },\r\n {\r\n label: \"Maryland\",\r\n value: \"MD\"\r\n },\r\n {\r\n label: \"Massachusetts\",\r\n value: \"MA\"\r\n },\r\n {\r\n label: \"Michigan\",\r\n value: \"MI\"\r\n },\r\n {\r\n label: \"Minnesota\",\r\n value: \"MN\"\r\n },\r\n {\r\n label: \"Mississippi\",\r\n value: \"MS\"\r\n },\r\n {\r\n label: \"Missouri\",\r\n value: \"MO\"\r\n },\r\n {\r\n label: \"Montana\",\r\n value: \"MT\"\r\n },\r\n {\r\n label: \"Nebraska\",\r\n value: \"NE\"\r\n },\r\n {\r\n label: \"Nevada\",\r\n value: \"NV\"\r\n },\r\n {\r\n label: \"New Hampshire\",\r\n value: \"NH\"\r\n },\r\n {\r\n label: \"New Jersey\",\r\n value: \"NJ\"\r\n },\r\n {\r\n label: \"New Mexico\",\r\n value: \"NM\"\r\n },\r\n {\r\n label: \"New York\",\r\n value: \"NY\"\r\n },\r\n {\r\n label: \"North Carolina\",\r\n value: \"NC\"\r\n },\r\n {\r\n label: \"North Dakota\",\r\n value: \"ND\"\r\n },\r\n {\r\n label: \"Northern Mariana Islands\",\r\n value: \"MP\"\r\n },\r\n {\r\n label: \"Ohio\",\r\n value: \"OH\"\r\n },\r\n {\r\n label: \"Oklahoma\",\r\n value: \"OK\"\r\n },\r\n {\r\n label: \"Oregon\",\r\n value: \"OR\"\r\n },\r\n {\r\n label: \"Palau\",\r\n value: \"PW\"\r\n },\r\n {\r\n label: \"Pennsylvania\",\r\n value: \"PA\"\r\n },\r\n {\r\n label: \"Puerto Rico\",\r\n value: \"PR\"\r\n },\r\n {\r\n label: \"Rhode Island\",\r\n value: \"RI\"\r\n },\r\n {\r\n label: \"South Carolina\",\r\n value: \"SC\"\r\n },\r\n {\r\n label: \"South Dakota\",\r\n value: \"SD\"\r\n },\r\n {\r\n label: \"Tennessee\",\r\n value: \"TN\"\r\n },\r\n {\r\n label: \"Texas\",\r\n value: \"TX\"\r\n },\r\n {\r\n label: \"Utah\",\r\n value: \"UT\"\r\n },\r\n {\r\n label: \"Vermont\",\r\n value: \"VT\"\r\n },\r\n {\r\n label: \"Virgin Islands\",\r\n value: \"VI\"\r\n },\r\n {\r\n label: \"Virginia\",\r\n value: \"VA\"\r\n },\r\n {\r\n label: \"Washington\",\r\n value: \"WA\"\r\n },\r\n {\r\n label: \"West Virginia\",\r\n value: \"WV\"\r\n },\r\n {\r\n label: \"Wisconsin\",\r\n value: \"WI\"\r\n },\r\n {\r\n label: \"Wyoming\",\r\n value: \"WY\"\r\n }\r\n ]","import React from \"react\";\r\nimport { OrganizationContext } from \"../Contexts/OrganizationContext\";\r\n\r\nexport default function useOrganization() {\r\n return React.useContext(OrganizationContext);\r\n}","import React from 'react';\r\n\r\ninterface Props {\r\n setValue(input:string): void;\r\n label: string;\r\n value: string;\r\n}\r\n\r\nexport default function TextAreaControl(props: Props){\r\n return (\r\n
    \r\n \r\n