@tellescope/react-components 1.173.0 → 1.175.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.
Files changed (47) hide show
  1. package/lib/cjs/Forms/inputs.d.ts.map +1 -1
  2. package/lib/cjs/Forms/inputs.js +3 -1
  3. package/lib/cjs/Forms/inputs.js.map +1 -1
  4. package/lib/cjs/inputs_shared.d.ts +4 -1
  5. package/lib/cjs/inputs_shared.d.ts.map +1 -1
  6. package/lib/cjs/inputs_shared.js +7 -1
  7. package/lib/cjs/inputs_shared.js.map +1 -1
  8. package/lib/cjs/state.d.ts +49 -0
  9. package/lib/cjs/state.d.ts.map +1 -1
  10. package/lib/cjs/state.js +74 -17
  11. package/lib/cjs/state.js.map +1 -1
  12. package/lib/cjs/table.d.ts +2 -1
  13. package/lib/cjs/table.d.ts.map +1 -1
  14. package/lib/cjs/table.js +25 -17
  15. package/lib/cjs/table.js.map +1 -1
  16. package/lib/esm/CMS/components.d.ts +0 -1
  17. package/lib/esm/CMS/components.d.ts.map +1 -1
  18. package/lib/esm/Forms/forms.d.ts +3 -3
  19. package/lib/esm/Forms/inputs.d.ts +1 -1
  20. package/lib/esm/Forms/inputs.d.ts.map +1 -1
  21. package/lib/esm/Forms/inputs.js +3 -1
  22. package/lib/esm/Forms/inputs.js.map +1 -1
  23. package/lib/esm/Forms/inputs.native.d.ts +0 -1
  24. package/lib/esm/Forms/inputs.native.d.ts.map +1 -1
  25. package/lib/esm/controls.d.ts +2 -2
  26. package/lib/esm/inputs.d.ts +1 -1
  27. package/lib/esm/inputs_shared.d.ts +4 -1
  28. package/lib/esm/inputs_shared.d.ts.map +1 -1
  29. package/lib/esm/inputs_shared.js +6 -1
  30. package/lib/esm/inputs_shared.js.map +1 -1
  31. package/lib/esm/layout.d.ts +1 -1
  32. package/lib/esm/state.d.ts +322 -273
  33. package/lib/esm/state.d.ts.map +1 -1
  34. package/lib/esm/state.js +70 -14
  35. package/lib/esm/state.js.map +1 -1
  36. package/lib/esm/table.d.ts +2 -1
  37. package/lib/esm/table.d.ts.map +1 -1
  38. package/lib/esm/table.js +25 -17
  39. package/lib/esm/table.js.map +1 -1
  40. package/lib/esm/theme.native.d.ts +0 -1
  41. package/lib/esm/theme.native.d.ts.map +1 -1
  42. package/lib/tsconfig.tsbuildinfo +1 -1
  43. package/package.json +9 -9
  44. package/src/Forms/inputs.tsx +5 -1
  45. package/src/inputs_shared.tsx +13 -2
  46. package/src/state.tsx +79 -14
  47. package/src/table.tsx +21 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tellescope/react-components",
3
- "version": "1.173.0",
3
+ "version": "1.175.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.175.0",
51
+ "@tellescope/sdk": "^1.175.0",
52
+ "@tellescope/types-client": "^1.175.0",
53
+ "@tellescope/types-models": "^1.175.0",
54
+ "@tellescope/types-utilities": "^1.175.0",
55
+ "@tellescope/utilities": "^1.175.0",
56
+ "@tellescope/validation": "^1.175.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": "d4b035d1d3a40b4177169dd41b911eec7383c949",
88
88
  "publishConfig": {
89
89
  "access": "public"
90
90
  }
@@ -1755,7 +1755,11 @@ export const DropdownInput = ({ field, value, onChange }: FormInputProps<'Dropdo
1755
1755
  ? onChange(e.target.value ? [e.target.value] : [], field.id)
1756
1756
  : undefined
1757
1757
  )}
1758
- placeholder={field.placeholder + ((!field.title && !field.isOptional) ? '*' : '')}
1758
+ placeholder={
1759
+ field.placeholder
1760
+ ? field.placeholder + ((!field.title && !field.isOptional) ? '*' : '')
1761
+ : undefined
1762
+ }
1759
1763
  label={
1760
1764
  (!field.options?.radio && field.options?.other)
1761
1765
  ? "Press enter to save a custom value"
@@ -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
  }
package/src/table.tsx CHANGED
@@ -38,7 +38,7 @@ import { PRIMARY_HEX } from "@tellescope/constants"
38
38
 
39
39
  import CloudDownloadIcon from '@mui/icons-material/CloudDownload';
40
40
  import { Autocomplete } from "@mui/material"
41
- import { SortingField } from "@tellescope/types-models"
41
+ import { ListQueryQualifier, SortingField } from "@tellescope/types-models"
42
42
  // import DragHandleIcon from '@mui/icons-material/DragHandle';
43
43
 
44
44
  const LIGHT_GRAY = "#fafafa"
@@ -101,6 +101,7 @@ const checkboxStyle: React.CSSProperties = {
101
101
  type LocalFilter = {
102
102
  query: string,
103
103
  values?: string[],
104
+ valuesQualifier?: ListQueryQualifier,
104
105
  }
105
106
 
106
107
  // export type SortType = 'infer'
@@ -183,6 +184,19 @@ export const TableHeader = <T extends Item>({
183
184
  <Flex flex={1} justifyContent="center">
184
185
  {fields[openFilter]?.filterType === 'multi'
185
186
  ? (
187
+ <>
188
+ <Autocomplete disableClearable disablePortal size={'small'} options={['One Of', 'All Of']} sx={{ width: 140, mr: 0.5 }}
189
+ onChange={(e, value) => {
190
+ setLocalFilters(fs => fs.map((f, i) => i === openFilter ? { ...f, valuesQualifier: value === 'All Of' ? 'All Of' : 'One Of' } : f))
191
+ }}
192
+ value={localFilters[openFilter]?.valuesQualifier === 'All Of' ? 'All Of' : 'One Of'}
193
+ renderInput={(params) =>
194
+ <TextField {...params} fullWidth autoFocus label={"Qualifier"} size={'small'}
195
+ value={localFilters[openFilter]?.valuesQualifier === 'All Of' ? 'All Of' : 'One Of'}
196
+ />
197
+ }
198
+ />
199
+
186
200
  <Autocomplete size={'small'}
187
201
  disablePortal multiple
188
202
  options={(filterSuggestions?.[fields[openFilter]?.key?.toString()] || []).sort()}
@@ -192,13 +206,14 @@ export const TableHeader = <T extends Item>({
192
206
  }}
193
207
  value={localFilters[openFilter]?.values ?? []}
194
208
  renderInput={(params) =>
195
- <TextField {...params} autoFocus label={"Filter"} size={'small'}
209
+ <TextField {...params} autoFocus label={"Filter by"} size={'small'}
196
210
  style={{ width: 400 }}
197
211
  value={localFilters[openFilter]?.query ?? ''}
198
212
  // onKeyUp={e => e.which === 13 && onEnterPress?.()}
199
213
  />
200
214
  }
201
215
  />
216
+ </>
202
217
  ) : (
203
218
  <Autocomplete size={'small'}
204
219
  disablePortal
@@ -922,13 +937,16 @@ export const Table = <T extends Item>({
922
937
  const { getFilterValue, filterType } = fields[i]
923
938
  if (!getFilterValue) continue
924
939
 
925
- const { query, values } = localFilters[i] ?? {}
940
+ const { query, values, valuesQualifier } = localFilters[i] ?? {}
926
941
 
927
942
  if (filterType === 'multi') {
928
943
  if (!values?.length) continue
929
944
 
930
945
  const filterValueOrValues = getFilterValue(v)
931
946
  const filterValues = Array.isArray(filterValueOrValues) ? filterValueOrValues : [filterValueOrValues]
947
+ if (valuesQualifier === 'All Of') {
948
+ return values.every(v => filterValues.includes(v))
949
+ }
932
950
  if (!filterValues.find(v => values.includes(v))) {
933
951
  return false
934
952
  }