@tellescope/react-components 1.173.0 → 1.174.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tellescope/react-components",
3
- "version": "1.173.0",
3
+ "version": "1.174.0",
4
4
  "description": "",
5
5
  "main": "./lib/cjs/index.js",
6
6
  "module": "./lib/esm/index.js",
@@ -47,13 +47,13 @@
47
47
  "@reduxjs/toolkit": "^1.6.2",
48
48
  "@stripe/react-stripe-js": "^2.9.0",
49
49
  "@stripe/stripe-js": "^1.52.1",
50
- "@tellescope/constants": "^1.173.0",
51
- "@tellescope/sdk": "^1.173.0",
52
- "@tellescope/types-client": "^1.173.0",
53
- "@tellescope/types-models": "^1.173.0",
54
- "@tellescope/types-utilities": "^1.173.0",
55
- "@tellescope/utilities": "^1.173.0",
56
- "@tellescope/validation": "^1.173.0",
50
+ "@tellescope/constants": "^1.174.0",
51
+ "@tellescope/sdk": "^1.174.0",
52
+ "@tellescope/types-client": "^1.174.0",
53
+ "@tellescope/types-models": "^1.174.0",
54
+ "@tellescope/types-utilities": "^1.174.0",
55
+ "@tellescope/utilities": "^1.174.0",
56
+ "@tellescope/validation": "^1.174.0",
57
57
  "@typescript-eslint/eslint-plugin": "^4.33.0",
58
58
  "@typescript-eslint/parser": "^4.33.0",
59
59
  "css-to-react-native": "^3.0.0",
@@ -84,7 +84,7 @@
84
84
  "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0",
85
85
  "react-native": "^0.65.0 || ^0.66.0 || ^0.67.0 || ^0.68.0 || ^0.71.0"
86
86
  },
87
- "gitHead": "0239f40d9a62d16c2d94830a5f65db54aee48e1f",
87
+ "gitHead": "b18cb31c80f3294f01abeb2bcb2d73c19441f738",
88
88
  "publishConfig": {
89
89
  "access": "public"
90
90
  }
@@ -5,8 +5,8 @@ import { LoadFunction, LoadFunctionArguments } from "@tellescope/sdk"
5
5
  import { ALL_ACCESS, UNSEARCHABLE_FIELDS } from "@tellescope/constants"
6
6
  import { SearchAPIProps, useSearchAPI } from "./hooks"
7
7
  import { TextFieldProps } from "./mui"
8
- import { AgentRecord, AllergyCode, AppointmentBookingPage, AppointmentLocation, AutomationTrigger, CalendarEventTemplate, CallHoldQueue, ChatRoom, Database, DatabaseRecord, DiagnosisCode, Enduser, EnduserOrder, FaxLog, File, Form, FormGroup, Forum, Journey, ManagedContentRecord, MessageTemplateSnippet, Organization, PrescriptionRoute, SuggestedContact, Template, Ticket, TicketQueue, User, UserNotification } from "@tellescope/types-client"
9
- import { Button, Checkbox, Flex, HoverPaper, LoadingButton, LoadingData, LoadingLinear, ScrollingList, SearchTextInput, Typography, useAgentRecords, useAllergyCodes, useAppointmentBookingPages, useAppointmentLocations, useAutomationTriggers, useCalendarEventTemplates, useCallHoldQueues, useChatRooms, useDatabaseRecords, useDatabases, useDiagnosisCodes, useEnduserOrders, useEndusers, useFaxLogs, useFiles, useFormGroups, useForms, useForums, useJourneys, useManagedContentRecords, useMessageTemplateSnippets, useNotifications, useOrganization, useOrganizations, usePrescriptionRoutes, useResolvedSession, useSession, useSuggestedContacts, useTemplates, useTicketQueues, useTickets, useUsers, value_is_loaded } from "."
8
+ import { AgentRecord, AllergyCode, AppointmentBookingPage, AppointmentLocation, AutomationTrigger, CalendarEventTemplate, CallHoldQueue, ChatRoom, Database, DatabaseRecord, DiagnosisCode, Enduser, EnduserOrder, FaxLog, File, Form, FormGroup, Forum, Journey, ManagedContentRecord, MessageTemplateSnippet, Organization, PrescriptionRoute, SuggestedContact, Template, Ticket, TicketQueue, User, UserNotification, Waitlist } from "@tellescope/types-client"
9
+ import { Button, Checkbox, Flex, HoverPaper, LoadingButton, LoadingData, LoadingLinear, ScrollingList, SearchTextInput, Typography, useAgentRecords, useAllergyCodes, useAppointmentBookingPages, useAppointmentLocations, useAutomationTriggers, useCalendarEventTemplates, useCallHoldQueues, useChatRooms, useDatabaseRecords, useDatabases, useDiagnosisCodes, useEnduserOrders, useEndusers, useFaxLogs, useFiles, useFormGroups, useForms, useForums, useJourneys, useManagedContentRecords, useMessageTemplateSnippets, useNotifications, useOrganization, useOrganizations, usePrescriptionRoutes, useResolvedSession, useSession, useSuggestedContacts, useTemplates, useTicketQueues, useTickets, useUsers, useWaitlists, value_is_loaded } from "."
10
10
  import { SxProps } from "@mui/material"
11
11
  import { AccessPermissions } from "@tellescope/types-models"
12
12
 
@@ -618,6 +618,17 @@ export const SuggestedContactSearch = (props: Omit<GenericSearchProps<SuggestedC
618
618
  )
619
619
  }
620
620
 
621
+ export const WaitlistSearch = (props: Omit<GenericSearchProps<Waitlist>, 'filterKey'> & { filterKey?: string }) => {
622
+ const session = useSession()
623
+ const [, { addLocalElements }] = useWaitlists({ dontFetch: true })
624
+ return (
625
+ <ModelSearchInput filterKey="waitlist" {...props}
626
+ searchAPI={session.api.waitlists.getSome}
627
+ onLoad={addLocalElements}
628
+ />
629
+ )
630
+ }
631
+
621
632
  export const AgentRecordSearch = (props: Omit<GenericSearchProps<AgentRecord>, 'filterKey'> & { filterKey?: string }) => {
622
633
  const session = useSession()
623
634
  const [, { addLocalElements }] = useAgentRecords({ dontFetch: true })
package/src/state.tsx CHANGED
@@ -98,6 +98,7 @@ import {
98
98
  IntegrationLog,
99
99
  EnduserEligibilityResult,
100
100
  AgentRecord,
101
+ Waitlist,
101
102
  } from "@tellescope/types-client"
102
103
 
103
104
  import {
@@ -363,6 +364,7 @@ const allergyCodesSlice = createSliceForList<AllergyCode, 'allergy_codes'>('alle
363
364
  const integrationLogsSlice = createSliceForList<IntegrationLog, 'integration_logs'>('integration_logs')
364
365
  const enduserEligibilityResultsSlice = createSliceForList<EnduserEligibilityResult, 'enduser_eligibility_results'>('enduser_eligibility_results')
365
366
  const agentRecordsSlice = createSliceForList<AgentRecord, 'agent_records'>('agent_records')
367
+ const waitlistsSlice = createSliceForList<Waitlist, 'waitlists'>('waitlists')
366
368
 
367
369
  const roleBasedAccessPermissionsSlice = createSliceForList<RoleBasedAccessPermission, 'role_based_access_permissions'>('role_based_access_permissions')
368
370
 
@@ -455,6 +457,7 @@ export const sharedConfig = {
455
457
  suggested_contacts: suggestedContactsSlice.reducer,
456
458
  diagnosis_codes: diagnosisCodesSlice.reducer,
457
459
  allergy_codes: allergyCodesSlice.reducer,
460
+ waitlists: waitlistsSlice.reducer,
458
461
  },
459
462
  }
460
463
 
@@ -528,10 +531,13 @@ export interface LoadMoreFunctions<T> {
528
531
 
529
532
  export const INACTIVE_SYNC_INTERVAL_IN_MS = 30000
530
533
  export const DEFAULT_SYNC_INTERVAL_IN_MS = 15000
534
+ export const MEDIUM_SYNC_INTERAVL = 10000
531
535
  export const FAST_SYNC_INTERVAL = 5000
532
536
 
533
537
  export const lastActiveForSync = { at: new Date(0), hasFocus: true }
534
538
 
539
+ export const lastDataSync = { current : { numResults: 0, at: new Date(0), from: new Date(0), latency: 0, duration: 0 } }
540
+
535
541
  export const useDataSync____internal = () => {
536
542
  const session = useSession()
537
543
  const lastFetch = React.useRef(new Date())
@@ -576,11 +582,31 @@ export const useDataSync____internal = () => {
576
582
  ...Object.values(loadTimings.current).filter(v => v > 999)
577
583
  )
578
584
 
585
+ const handleLoadedData = () => {
586
+ for (const handler of Object.values(handlers.current)) {
587
+ try {
588
+ handler?.()
589
+ } catch(err) { console.error(err) }
590
+ }
591
+ }
592
+
579
593
  if (lastFetch.current.getTime() + pollDurationInMS > Date.now()) {
580
594
  return
581
595
  }
582
596
 
583
- session.sync({ from: lastFetch.current }).then(({ results }) => {
597
+ // ensure we don't miss updates due to latency
598
+ const from = new Date(lastFetch.current.getTime() - 1000) // large leeway could result in same data being fetched twice, but helps ensure nothing is dropped
599
+ lastFetch.current = new Date() // update before syncing, not after it returns
600
+
601
+ session
602
+ .sync({ from })
603
+ .then(({ results }) => {
604
+ lastDataSync.current = {
605
+ numResults: results.length, at: lastFetch.current, from,
606
+ latency: Date.now() - lastFetch.current.getTime(),
607
+ duration: pollDurationInMS,
608
+ }
609
+
584
610
  for (const r of results) {
585
611
  if (r.data === 'deleted') {
586
612
  if (!deleted.current[r.modelName]) {
@@ -599,14 +625,11 @@ export const useDataSync____internal = () => {
599
625
  }
600
626
  }
601
627
  })
602
- .then(() => {
603
- for (const handler of Object.values(handlers.current)) {
604
- handler?.()
605
- }
628
+ .then(handleLoadedData)
629
+ .catch(err => {
630
+ console.error('Sync error', err)
631
+ lastFetch.current = from // don't skip this interval yet
606
632
  })
607
- .catch(console.error)
608
-
609
- lastFetch.current = new Date()
610
633
  }, 1000)
611
634
 
612
635
  return () => { clearInterval(i) }
@@ -631,12 +654,31 @@ export const useDataSync____internal = () => {
631
654
  }, [])
632
655
 
633
656
  const setHandler = useCallback((key: string, handler: undefined | (() => void)) => {
657
+ // call handler when initially set in case results were loaded when there was no handler
658
+ if (!handlers.current[key] && handler) {
659
+ try {
660
+ // console.log("handle on add", key)
661
+ handler?.()
662
+ }
663
+ catch(err) {
664
+ console.error(err)
665
+ }
666
+ }
667
+
668
+ // console.log('setting handler', key)
634
669
  handlers.current[key] = handler
635
670
  }, [])
636
671
 
672
+ const removeHandler = useCallback((key: string, handler: () => void) => {
673
+ if (handlers.current[key] !== handler) return // if a handler was overwritten, don't remove it
674
+
675
+ // console.log('removing handler', key)
676
+ delete handlers.current[key]
677
+ }, [])
678
+
637
679
  return {
638
680
  setLoadTiming,
639
- setHandler,
681
+ setHandler, removeHandler,
640
682
  getLoaded, getDeleted,
641
683
  popLoaded, popDeleted,
642
684
  }
@@ -702,7 +744,7 @@ export const useListStateHook = <T extends { id: string | number }, ADD extends
702
744
  } & HookOptions<T>
703
745
  ): ListStateReturnType<T, ADD> =>
704
746
  {
705
- const { setHandler, popDeleted, popLoaded } = useSyncContext() ?? {}
747
+ const { setHandler, popDeleted, popLoaded, removeHandler } = useSyncContext() ?? {}
706
748
  const { loadQuery, findOne, findByIds, addOne, addSome, updateOne, deleteOne } = apiCalls
707
749
  if (options?.refetchInMS !== undefined && options.refetchInMS < 5000) {
708
750
  throw new Error("refetchInMS must be greater than 5000")
@@ -790,7 +832,7 @@ export const useListStateHook = <T extends { id: string | number }, ADD extends
790
832
  // context not provided
791
833
  if (!setHandler) return
792
834
 
793
- setHandler(modelName, () => {
835
+ const handler = () => {
794
836
  if (state.status !== LoadingStatus.Loaded) return
795
837
 
796
838
  const deleted = popDeleted(modelName)
@@ -802,8 +844,12 @@ export const useListStateHook = <T extends { id: string | number }, ADD extends
802
844
  if (loaded?.length) {
803
845
  addLocalElements(loaded as any, { replaceIfMatch: true })
804
846
  }
805
- })
806
- }, [state.status, setHandler, addLocalElements, removeLocalElements, popDeleted, popLoaded])
847
+ }
848
+
849
+ setHandler(modelName, handler)
850
+
851
+ return () => { removeHandler(modelName, handler) }
852
+ }, [modelName, state.status, setHandler, removeHandler, addLocalElements, removeLocalElements, popDeleted, popLoaded])
807
853
 
808
854
  const findById: ListUpdateMethods<T, ADD>['findById'] = useCallback((id, options) => {
809
855
  if (!id) return undefined
@@ -980,7 +1026,7 @@ export const useListStateHook = <T extends { id: string | number }, ADD extends
980
1026
  const sortBy = loadOptions?.sortBy ?? options?.sortBy
981
1027
 
982
1028
  if (!loadQuery) return
983
- if (options?.dontFetch) return
1029
+ if (options?.dontFetch && !force) return
984
1030
  const fetchKey = (loadFilter || sort || sortBy) ? JSON.stringify({ ...loadFilter, sort, sortBy }) + modelName : modelName
985
1031
 
986
1032
  if (didFetch(fetchKey, force, options?.refetchInMS)) return
@@ -2837,4 +2883,23 @@ export const useCalendarEventsForUser = (options={} as HookOptions<CalendarEvent
2837
2883
  }, [session, loadedRef, fetchEvents, addLocalElements])
2838
2884
 
2839
2885
  return [eventsLoading, { loadEvents, filtered }] as const
2886
+ }
2887
+
2888
+ export const useWaitlists = (options={} as HookOptions<Waitlist>) => {
2889
+ const session = useSession()
2890
+
2891
+ return useListStateHook('waitlists', useTypedSelector(s => s.waitlists), session, waitlistsSlice,
2892
+ {
2893
+ loadQuery: session.api.waitlists.getSome,
2894
+ findOne: session.api.waitlists.getOne,
2895
+ findByIds: session.api.waitlists.getByIds,
2896
+ addOne: session.api.waitlists.createOne,
2897
+ addSome: session.api.waitlists.createSome,
2898
+ deleteOne: session.api.waitlists.deleteOne,
2899
+ updateOne: session.api.waitlists.updateOne,
2900
+ },
2901
+ {
2902
+ ...options,
2903
+ },
2904
+ )
2840
2905
  }