@tellescope/react-components 1.234.1 → 1.235.1
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/lib/cjs/CMS/ContentViewer.d.ts.map +1 -1
- package/lib/cjs/CMS/ContentViewer.js +26 -22
- package/lib/cjs/CMS/ContentViewer.js.map +1 -1
- package/lib/cjs/Forms/forms.d.ts.map +1 -1
- package/lib/cjs/Forms/forms.js +37 -35
- package/lib/cjs/Forms/forms.js.map +1 -1
- package/lib/cjs/Forms/forms.v2.d.ts.map +1 -1
- package/lib/cjs/Forms/forms.v2.js +37 -35
- package/lib/cjs/Forms/forms.v2.js.map +1 -1
- package/lib/cjs/Forms/inputs.d.ts +17 -2
- package/lib/cjs/Forms/inputs.d.ts.map +1 -1
- package/lib/cjs/Forms/inputs.js +429 -37
- package/lib/cjs/Forms/inputs.js.map +1 -1
- package/lib/cjs/Forms/inputs.v2.d.ts +3 -2
- package/lib/cjs/Forms/inputs.v2.d.ts.map +1 -1
- package/lib/cjs/Forms/inputs.v2.js +21 -294
- package/lib/cjs/Forms/inputs.v2.js.map +1 -1
- package/lib/cjs/Forms/types.d.ts +4 -0
- package/lib/cjs/Forms/types.d.ts.map +1 -1
- package/lib/esm/CMS/ContentViewer.d.ts.map +1 -1
- package/lib/esm/CMS/ContentViewer.js +27 -23
- package/lib/esm/CMS/ContentViewer.js.map +1 -1
- package/lib/esm/Forms/forms.d.ts.map +1 -1
- package/lib/esm/Forms/forms.js +38 -36
- package/lib/esm/Forms/forms.js.map +1 -1
- package/lib/esm/Forms/forms.v2.d.ts.map +1 -1
- package/lib/esm/Forms/forms.v2.js +38 -36
- package/lib/esm/Forms/forms.v2.js.map +1 -1
- package/lib/esm/Forms/inputs.d.ts +17 -2
- package/lib/esm/Forms/inputs.d.ts.map +1 -1
- package/lib/esm/Forms/inputs.js +427 -38
- package/lib/esm/Forms/inputs.js.map +1 -1
- package/lib/esm/Forms/inputs.v2.d.ts +3 -2
- package/lib/esm/Forms/inputs.v2.d.ts.map +1 -1
- package/lib/esm/Forms/inputs.v2.js +16 -290
- package/lib/esm/Forms/inputs.v2.js.map +1 -1
- package/lib/esm/Forms/types.d.ts +4 -0
- package/lib/esm/Forms/types.d.ts.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +44 -44
- package/src/CMS/ContentViewer.tsx +16 -2
- package/src/Forms/forms.tsx +13 -6
- package/src/Forms/forms.v2.tsx +9 -2
- package/src/Forms/inputs.tsx +563 -66
- package/src/Forms/inputs.v2.tsx +13 -594
- package/src/Forms/types.ts +4 -2
package/src/Forms/inputs.tsx
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from "react"
|
|
2
2
|
import axios from "axios"
|
|
3
|
-
import { Autocomplete, Box, Button, Checkbox, Chip, CircularProgress, Collapse, Divider, FormControl, FormControlLabel, FormLabel, Grid, IconButton as MuiIconButton, InputLabel, MenuItem, Radio, RadioGroup, Select, SxProps, TextField, TextFieldProps, Typography } from "@mui/material"
|
|
3
|
+
import { Autocomplete, Box, Button, Checkbox, Chip, CircularProgress, Collapse, Divider, FormControl, FormControlLabel, FormLabel, Grid, IconButton as MuiIconButton, InputLabel, MenuItem, Paper, Radio, RadioGroup, Select, SxProps, TextField, TextFieldProps, Typography } from "@mui/material"
|
|
4
4
|
import { FormInputProps } from "./types"
|
|
5
5
|
import { useDropzone } from "react-dropzone"
|
|
6
|
-
import { CANVAS_TITLE, EMOTII_TITLE, INSURANCE_RELATIONSHIPS, INSURANCE_RELATIONSHIPS_CANVAS, PRIMARY_HEX, RELATIONSHIP_TYPES, TELLESCOPE_GENDERS } from "@tellescope/constants"
|
|
6
|
+
import { CANVAS_TITLE, BRIDGE_TITLE, EMOTII_TITLE, INSURANCE_RELATIONSHIPS, INSURANCE_RELATIONSHIPS_CANVAS, PRIMARY_HEX, RELATIONSHIP_TYPES, TELLESCOPE_GENDERS } from "@tellescope/constants"
|
|
7
7
|
import { MM_DD_YYYY_to_YYYY_MM_DD, capture_is_supported, downloadFile, emit_gtm_event, first_letter_capitalized, form_response_value_to_string, format_stripe_subscription_interval, getLocalTimezone, getPublicFileURL, mm_dd_yyyy, object_is_empty, replace_enduser_template_values, responses_satisfy_conditions, truncate_string, update_local_storage, user_display_name } from "@tellescope/utilities"
|
|
8
8
|
import { Address, DatabaseSelectResponse, Enduser, EnduserRelationship, FormResponseValue, InsuranceRelationship, MedicationResponse, MultipleChoiceOptions, FormFieldOptionDetails, TellescopeGender, TIMEZONES_USA } from "@tellescope/types-models"
|
|
9
9
|
import { VALID_STATES, emailValidator, phoneValidator } from "@tellescope/validation"
|
|
@@ -26,6 +26,16 @@ import { loadStripe } from '@stripe/stripe-js';
|
|
|
26
26
|
import { CheckCircleOutline, Delete, Edit, ExpandMore } from "@mui/icons-material"
|
|
27
27
|
import { WYSIWYG } from "./wysiwyg"
|
|
28
28
|
|
|
29
|
+
// Bridge Eligibility - shared variable for storing most recent eligibility userIds
|
|
30
|
+
const bridgeEligibilityResult = {
|
|
31
|
+
userIds: [] as string[],
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const getBridgeEligibilityUserIds = () => bridgeEligibilityResult.userIds
|
|
35
|
+
export const setBridgeEligibilityUserIds = (userIds: string[]) => {
|
|
36
|
+
bridgeEligibilityResult.userIds = userIds
|
|
37
|
+
}
|
|
38
|
+
|
|
29
39
|
// Debounce hook for search functionality
|
|
30
40
|
const useDebounce = <T,>(value: T, delay: number): T => {
|
|
31
41
|
const [debouncedValue, setDebouncedValue] = useState<T>(value)
|
|
@@ -409,15 +419,19 @@ export const TableInput = ({ field, value=[], onChange, ...props }: FormInputPro
|
|
|
409
419
|
)
|
|
410
420
|
}
|
|
411
421
|
|
|
412
|
-
export const AutoFocusTextField = (props: TextFieldProps) =>
|
|
413
|
-
|
|
414
|
-
|
|
422
|
+
export const AutoFocusTextField = (props: TextFieldProps & { inputProps?: { sx: SxProps } }) => {
|
|
423
|
+
const { inputProps, ...textFieldProps } = props
|
|
424
|
+
return <TextField InputProps={inputProps || defaultInputProps} {...textFieldProps} />
|
|
425
|
+
}
|
|
415
426
|
|
|
416
|
-
const CustomDateStringInput = forwardRef((props: TextFieldProps, ref) =>
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
427
|
+
const CustomDateStringInput = forwardRef((props: TextFieldProps & { inputProps?: { sx: SxProps } }, ref) => {
|
|
428
|
+
const { inputProps, ...textFieldProps } = props
|
|
429
|
+
return (
|
|
430
|
+
<TextField InputProps={inputProps || defaultInputProps}
|
|
431
|
+
fullWidth inputRef={ref} {...textFieldProps}
|
|
432
|
+
/>
|
|
433
|
+
)
|
|
434
|
+
})
|
|
421
435
|
export const DateStringInput = ({ field, value, onChange, ...props }: FormInputProps<'string'>) => {
|
|
422
436
|
const inputRef = useRef(null);
|
|
423
437
|
|
|
@@ -543,7 +557,9 @@ export const NumberInput = ({ field, value, onChange, form, ...props }: FormInpu
|
|
|
543
557
|
)
|
|
544
558
|
}
|
|
545
559
|
|
|
546
|
-
export const InsuranceInput = ({ field, onDatabaseSelect, value, onChange, form, responses, enduser, ...props }: FormInputProps<'Insurance'>
|
|
560
|
+
export const InsuranceInput = ({ field, onDatabaseSelect, value, onChange, form, responses, enduser, inputProps, ...props }: FormInputProps<'Insurance'> & {
|
|
561
|
+
inputProps?: { sx: SxProps },
|
|
562
|
+
}) => {
|
|
547
563
|
const session = useResolvedSession()
|
|
548
564
|
|
|
549
565
|
const [payers, setPayers] = useState<{ id: string, name: string, databaseRecord?: DatabaseRecord, type?: string, state?: string }[]>([])
|
|
@@ -563,9 +579,11 @@ export const InsuranceInput = ({ field, onDatabaseSelect, value, onChange, form,
|
|
|
563
579
|
(addressQuestion?.answer?.type === 'Address' ? addressQuestion?.answer?.value?.state : undefined) || enduser?.state
|
|
564
580
|
), [enduser?.state, addressQuestion])
|
|
565
581
|
|
|
582
|
+
// load from database
|
|
566
583
|
const loadRef = useRef(false) // so session changes don't cause
|
|
567
584
|
useEffect(() => {
|
|
568
585
|
if (field?.options?.dataSource === CANVAS_TITLE) return // instead, look-up while typing against Canvas Search API
|
|
586
|
+
if (field?.options?.dataSource === BRIDGE_TITLE) return // instead, look-up while typing against Bridge Search API
|
|
569
587
|
if (loadRef.current) return
|
|
570
588
|
loadRef.current = true
|
|
571
589
|
|
|
@@ -585,27 +603,35 @@ export const InsuranceInput = ({ field, onDatabaseSelect, value, onChange, form,
|
|
|
585
603
|
.catch(console.error)
|
|
586
604
|
}, [session, state, field?.options?.dataSource])
|
|
587
605
|
|
|
606
|
+
// load from 3rd-party on search only
|
|
588
607
|
const searchRef = useRef(query)
|
|
589
608
|
useEffect(() => {
|
|
590
|
-
if (field?.options?.dataSource !== CANVAS_TITLE) { return }
|
|
609
|
+
if (field?.options?.dataSource !== CANVAS_TITLE && field?.options?.dataSource !== BRIDGE_TITLE) { return }
|
|
591
610
|
if (!query) return
|
|
592
611
|
if (searchRef.current === query) return
|
|
593
612
|
searchRef.current = query
|
|
594
613
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
614
|
+
const integration = field?.options?.dataSource === CANVAS_TITLE ? CANVAS_TITLE : BRIDGE_TITLE
|
|
615
|
+
const type = field?.options?.dataSource === CANVAS_TITLE ? 'organizations' : 'payers'
|
|
616
|
+
|
|
617
|
+
const t = setTimeout(() => (
|
|
618
|
+
session.api.integrations.proxy_read({
|
|
619
|
+
integration,
|
|
620
|
+
query,
|
|
621
|
+
type,
|
|
622
|
+
})
|
|
623
|
+
.then(({ data }) => {
|
|
624
|
+
try {
|
|
625
|
+
setPayers(data.map((d: any) => ({
|
|
626
|
+
id: field?.options?.dataSource === CANVAS_TITLE ? d.resource.id : d.id,
|
|
627
|
+
name: field?.options?.dataSource === CANVAS_TITLE ? d.resource.name : d.name,
|
|
628
|
+
})))
|
|
629
|
+
} catch(err) { console.error }
|
|
630
|
+
})
|
|
631
|
+
.catch(console.error)
|
|
632
|
+
), 300)
|
|
633
|
+
|
|
634
|
+
return () => { clearTimeout(t) }
|
|
609
635
|
}, [session, field?.options?.dataSource, query])
|
|
610
636
|
|
|
611
637
|
return (
|
|
@@ -630,24 +656,25 @@ export const InsuranceInput = ({ field, onDatabaseSelect, value, onChange, form,
|
|
|
630
656
|
onDatabaseSelect?.([databaseRecord])
|
|
631
657
|
}
|
|
632
658
|
|
|
659
|
+
// don't lose existing payerId on back-and-forth navigation
|
|
633
660
|
onChange({
|
|
634
661
|
...value,
|
|
635
662
|
payerName: v || '',
|
|
636
|
-
payerId: payers.find(p => p.name === v)?.id || '',
|
|
663
|
+
payerId: (value?.payerName === v && value?.payerId ? value.payerId : '') || payers.find(p => p.name === v)?.id || '',
|
|
637
664
|
payerType: payers.find(p => p.name === v)?.type || '',
|
|
638
665
|
}, field.id)
|
|
639
666
|
}
|
|
640
667
|
}
|
|
641
668
|
renderInput={(params) => (
|
|
642
|
-
<TextField {...params} InputProps={{ ...params.InputProps, sx: defaultInputProps.sx }}
|
|
669
|
+
<TextField {...params} InputProps={{ ...params.InputProps, sx: (inputProps || defaultInputProps).sx }}
|
|
643
670
|
required={!field.isOptional} size="small" label={"Insurer"}
|
|
644
|
-
placeholder={field.options?.dataSource === CANVAS_TITLE ? "Search insurer..." : "Insurer"}
|
|
671
|
+
placeholder={(field.options?.dataSource === CANVAS_TITLE || field.options?.dataSource === BRIDGE_TITLE) ? "Search insurer..." : "Insurer"}
|
|
645
672
|
/>
|
|
646
673
|
)}
|
|
647
674
|
/>
|
|
648
675
|
</Grid>
|
|
649
676
|
<Grid item xs={12} sm={6}>
|
|
650
|
-
<TextField InputProps={defaultInputProps} required={!field.isOptional} fullWidth value={value?.memberId ?? ''}
|
|
677
|
+
<TextField InputProps={inputProps || defaultInputProps} required={!field.isOptional} fullWidth value={value?.memberId ?? ''}
|
|
651
678
|
onChange={e => onChange({ ...value, memberId: e.target.value }, field.id)}
|
|
652
679
|
label={form_display_text_for_language(form, "Member ID", '')}
|
|
653
680
|
size="small"
|
|
@@ -655,8 +682,8 @@ export const InsuranceInput = ({ field, onDatabaseSelect, value, onChange, form,
|
|
|
655
682
|
</Grid>
|
|
656
683
|
|
|
657
684
|
<Grid item xs={12} sm={6}>
|
|
658
|
-
<TextField InputProps={defaultInputProps} required={false} fullWidth value={value?.planName ?? ''}
|
|
659
|
-
onChange={e => onChange({ ...value, planName: e.target.value }, field.id)}
|
|
685
|
+
<TextField InputProps={inputProps || defaultInputProps} required={false} fullWidth value={value?.planName ?? ''}
|
|
686
|
+
onChange={e => onChange({ ...value, planName: e.target.value }, field.id)}
|
|
660
687
|
label={form_display_text_for_language(form, "Plan Name", '')}
|
|
661
688
|
size="small"
|
|
662
689
|
/>
|
|
@@ -664,14 +691,15 @@ export const InsuranceInput = ({ field, onDatabaseSelect, value, onChange, form,
|
|
|
664
691
|
|
|
665
692
|
<Grid item xs={12} sm={6}>
|
|
666
693
|
<DateStringInput size="small" label="Plan Start Date"
|
|
694
|
+
inputProps={inputProps}
|
|
667
695
|
field={{
|
|
668
696
|
...field,
|
|
669
697
|
isOptional: true, //field.isOptional || field.options?.billingProvider === 'Candid'
|
|
670
|
-
}}
|
|
671
|
-
value={value?.startDate || ''}
|
|
672
|
-
onChange={startDate =>
|
|
673
|
-
onChange({
|
|
674
|
-
...value,
|
|
698
|
+
}}
|
|
699
|
+
value={value?.startDate || ''}
|
|
700
|
+
onChange={startDate =>
|
|
701
|
+
onChange({
|
|
702
|
+
...value,
|
|
675
703
|
startDate,
|
|
676
704
|
}, field.id)
|
|
677
705
|
}
|
|
@@ -680,7 +708,7 @@ export const InsuranceInput = ({ field, onDatabaseSelect, value, onChange, form,
|
|
|
680
708
|
|
|
681
709
|
{field.options?.includeGroupNumber &&
|
|
682
710
|
<Grid item xs={12}>
|
|
683
|
-
<TextField InputProps={defaultInputProps} fullWidth value={value?.groupNumber ?? ''}
|
|
711
|
+
<TextField InputProps={inputProps || defaultInputProps} fullWidth value={value?.groupNumber ?? ''}
|
|
684
712
|
onChange={e => onChange({ ...value, groupNumber: e.target.value }, field.id)}
|
|
685
713
|
label={form_display_text_for_language(form, "Group Number", '')}
|
|
686
714
|
size="small"
|
|
@@ -690,16 +718,17 @@ export const InsuranceInput = ({ field, onDatabaseSelect, value, onChange, form,
|
|
|
690
718
|
|
|
691
719
|
<Grid item xs={12}>
|
|
692
720
|
<StringSelector size="small" label="Relationship to Policy Owner"
|
|
721
|
+
inputProps={inputProps}
|
|
693
722
|
options={
|
|
694
723
|
(
|
|
695
724
|
(field.options?.billingProvider === CANVAS_TITLE || field.options?.dataSource === CANVAS_TITLE )
|
|
696
|
-
? INSURANCE_RELATIONSHIPS_CANVAS
|
|
725
|
+
? INSURANCE_RELATIONSHIPS_CANVAS
|
|
697
726
|
: INSURANCE_RELATIONSHIPS
|
|
698
727
|
)
|
|
699
728
|
.sort((x, y) => x.localeCompare(y))
|
|
700
729
|
}
|
|
701
|
-
value={value?.relationship || 'Self'}
|
|
702
|
-
onChange={relationship =>
|
|
730
|
+
value={value?.relationship || 'Self'}
|
|
731
|
+
onChange={relationship =>
|
|
703
732
|
onChange({ ...value, relationship: relationship as InsuranceRelationship || 'Self' }, field.id)
|
|
704
733
|
}
|
|
705
734
|
/>
|
|
@@ -712,24 +741,24 @@ export const InsuranceInput = ({ field, onDatabaseSelect, value, onChange, form,
|
|
|
712
741
|
</Grid>
|
|
713
742
|
|
|
714
743
|
<Grid item xs={6}>
|
|
715
|
-
<TextField label="First Name" size="small" InputProps={defaultInputProps} fullWidth
|
|
716
|
-
value={value?.relationshipDetails?.fname || ''}
|
|
744
|
+
<TextField label="First Name" size="small" InputProps={inputProps || defaultInputProps} fullWidth
|
|
745
|
+
value={value?.relationshipDetails?.fname || ''}
|
|
717
746
|
required={!field.isOptional}
|
|
718
|
-
onChange={e =>
|
|
719
|
-
onChange({
|
|
720
|
-
...value,
|
|
747
|
+
onChange={e =>
|
|
748
|
+
onChange({
|
|
749
|
+
...value,
|
|
721
750
|
relationshipDetails: { ...value?.relationshipDetails, fname: e.target.value }
|
|
722
751
|
}, field.id)
|
|
723
752
|
}
|
|
724
753
|
/>
|
|
725
754
|
</Grid>
|
|
726
755
|
<Grid item xs={6}>
|
|
727
|
-
<TextField label="Last Name" size="small" InputProps={defaultInputProps} fullWidth
|
|
728
|
-
value={value?.relationshipDetails?.lname || ''}
|
|
756
|
+
<TextField label="Last Name" size="small" InputProps={inputProps || defaultInputProps} fullWidth
|
|
757
|
+
value={value?.relationshipDetails?.lname || ''}
|
|
729
758
|
required={!field.isOptional}
|
|
730
|
-
onChange={e =>
|
|
731
|
-
onChange({
|
|
732
|
-
...value,
|
|
759
|
+
onChange={e =>
|
|
760
|
+
onChange({
|
|
761
|
+
...value,
|
|
733
762
|
relationshipDetails: { ...value?.relationshipDetails, lname: e.target.value }
|
|
734
763
|
}, field.id)
|
|
735
764
|
}
|
|
@@ -737,11 +766,12 @@ export const InsuranceInput = ({ field, onDatabaseSelect, value, onChange, form,
|
|
|
737
766
|
</Grid>
|
|
738
767
|
<Grid item xs={6}>
|
|
739
768
|
<StringSelector options={TELLESCOPE_GENDERS} size="small" label="Gender"
|
|
740
|
-
|
|
769
|
+
inputProps={inputProps}
|
|
770
|
+
value={value?.relationshipDetails?.gender || ''}
|
|
741
771
|
required={!field.isOptional}
|
|
742
|
-
onChange={v =>
|
|
743
|
-
onChange({
|
|
744
|
-
...value,
|
|
772
|
+
onChange={v =>
|
|
773
|
+
onChange({
|
|
774
|
+
...value,
|
|
745
775
|
relationshipDetails: { ...value?.relationshipDetails, gender: v as TellescopeGender }
|
|
746
776
|
}, field.id)
|
|
747
777
|
}
|
|
@@ -749,14 +779,15 @@ export const InsuranceInput = ({ field, onDatabaseSelect, value, onChange, form,
|
|
|
749
779
|
</Grid>
|
|
750
780
|
<Grid item xs={6}>
|
|
751
781
|
<DateStringInput size="small" label="Date of Birth"
|
|
782
|
+
inputProps={inputProps}
|
|
752
783
|
field={{
|
|
753
784
|
...field,
|
|
754
785
|
isOptional: field.isOptional || field.options?.billingProvider === 'Candid'
|
|
755
|
-
}}
|
|
756
|
-
value={value?.relationshipDetails?.dateOfBirth || ''}
|
|
757
|
-
onChange={dateOfBirth =>
|
|
758
|
-
onChange({
|
|
759
|
-
...value,
|
|
786
|
+
}}
|
|
787
|
+
value={value?.relationshipDetails?.dateOfBirth || ''}
|
|
788
|
+
onChange={dateOfBirth =>
|
|
789
|
+
onChange({
|
|
790
|
+
...value,
|
|
760
791
|
relationshipDetails: { ...value?.relationshipDetails, dateOfBirth }
|
|
761
792
|
}, field.id)
|
|
762
793
|
}
|
|
@@ -894,8 +925,8 @@ export const InsuranceInput = ({ field, onDatabaseSelect, value, onChange, form,
|
|
|
894
925
|
field.id
|
|
895
926
|
)}
|
|
896
927
|
renderInput={(params) => (
|
|
897
|
-
<TextField {...params} InputProps={{ ...params.InputProps, sx: defaultInputProps.sx }}
|
|
898
|
-
size={'small'} label={"State"} required={!field.isOptional}
|
|
928
|
+
<TextField {...params} InputProps={{ ...params.InputProps, sx: (inputProps || defaultInputProps).sx }}
|
|
929
|
+
size={'small'} label={"State"} required={!field.isOptional}
|
|
899
930
|
/>
|
|
900
931
|
)}
|
|
901
932
|
{...props}
|
|
@@ -908,7 +939,7 @@ export const InsuranceInput = ({ field, onDatabaseSelect, value, onChange, form,
|
|
|
908
939
|
}
|
|
909
940
|
|
|
910
941
|
|
|
911
|
-
const StringSelector = ({ options, value, onChange, required, getDisplayValue, ...props } : {
|
|
942
|
+
const StringSelector = ({ options, value, onChange, required, getDisplayValue, inputProps, ...props } : {
|
|
912
943
|
options: string[]
|
|
913
944
|
value: string,
|
|
914
945
|
onChange: (v: string) => void,
|
|
@@ -917,11 +948,12 @@ const StringSelector = ({ options, value, onChange, required, getDisplayValue, .
|
|
|
917
948
|
required?: boolean,
|
|
918
949
|
getDisplayValue?: (v: string) => string,
|
|
919
950
|
disabled?: boolean,
|
|
951
|
+
inputProps?: { sx: SxProps },
|
|
920
952
|
}) => (
|
|
921
953
|
<FormControl fullWidth size={props.size} required={required}>
|
|
922
954
|
<InputLabel>{props.label}</InputLabel>
|
|
923
955
|
<Select {...props} value={value} onChange={e => onChange(e.target.value)} fullWidth
|
|
924
|
-
sx={defaultInputProps.sx}
|
|
956
|
+
sx={(inputProps || defaultInputProps).sx}
|
|
925
957
|
>
|
|
926
958
|
{options.map((o, i) => (
|
|
927
959
|
<MenuItem value={o} key={o || i}>{getDisplayValue?.(o) ?? o}</MenuItem>
|
|
@@ -930,9 +962,464 @@ const StringSelector = ({ options, value, onChange, required, getDisplayValue, .
|
|
|
930
962
|
</FormControl>
|
|
931
963
|
)
|
|
932
964
|
|
|
965
|
+
export const BridgeEligibilityInput = ({ field, value, onChange, responses, enduser, inputProps, enduserId, ...props }: FormInputProps<'Bridge Eligibility'> & {
|
|
966
|
+
inputProps?: { sx: SxProps },
|
|
967
|
+
}) => {
|
|
968
|
+
const session = useResolvedSession()
|
|
969
|
+
const [loading, setLoading] = useState(false)
|
|
970
|
+
const [polling, setPolling] = useState(false)
|
|
971
|
+
const [error, setError] = useState<string>()
|
|
972
|
+
|
|
973
|
+
// single-page form must require button-click to check, but 1-page-at-a-time enduser sessions should auto-check
|
|
974
|
+
const isEnduserSession = session.type === 'enduser'
|
|
975
|
+
const eligibilityType = field.options?.bridgeEligibilityType || 'Soft'
|
|
976
|
+
|
|
977
|
+
// Extract payerId from Insurance question response
|
|
978
|
+
const [payerId, memberId, payerName] = useMemo(() => {
|
|
979
|
+
const insuranceResponse = responses?.find(r => r.answer?.type === 'Insurance' && r.answer?.value?.payerId)
|
|
980
|
+
if (insuranceResponse?.answer?.type === 'Insurance') {
|
|
981
|
+
return [
|
|
982
|
+
insuranceResponse.answer.value?.payerId,
|
|
983
|
+
insuranceResponse.answer.value?.memberId,
|
|
984
|
+
insuranceResponse.answer.value?.payerName,
|
|
985
|
+
]
|
|
986
|
+
}
|
|
987
|
+
// existing payer id is automatically resolved on the backend as default
|
|
988
|
+
return []
|
|
989
|
+
}, [responses])
|
|
990
|
+
|
|
991
|
+
// Extract state from Address question or enduser
|
|
992
|
+
const state = useMemo(() => {
|
|
993
|
+
// Find Address field with state value
|
|
994
|
+
const addressResponse = responses?.find(r =>
|
|
995
|
+
r.answer?.type === 'Address' && r.answer?.value?.state
|
|
996
|
+
)
|
|
997
|
+
if (addressResponse?.answer?.type === 'Address') {
|
|
998
|
+
return addressResponse.answer.value?.state
|
|
999
|
+
}
|
|
1000
|
+
// enduser state is automatically resolved on the backend as default
|
|
1001
|
+
}, [responses])
|
|
1002
|
+
|
|
1003
|
+
// Soft eligibility check function - supports multiple service type IDs
|
|
1004
|
+
const checkProviderEligibility = useCallback(async () => {
|
|
1005
|
+
const serviceTypeIds = field.options?.bridgeServiceTypeIds
|
|
1006
|
+
|
|
1007
|
+
if (!serviceTypeIds || serviceTypeIds.length === 0) {
|
|
1008
|
+
setError('Bridge Service Type IDs not configured')
|
|
1009
|
+
return
|
|
1010
|
+
}
|
|
1011
|
+
// payerId and state can be automatically resolved on the backend, if already saved on Enduser, so not required here
|
|
1012
|
+
|
|
1013
|
+
setLoading(true)
|
|
1014
|
+
setError(undefined)
|
|
1015
|
+
|
|
1016
|
+
try {
|
|
1017
|
+
// Fire parallel requests for each service type ID
|
|
1018
|
+
const results = await Promise.all(
|
|
1019
|
+
serviceTypeIds.map(async (serviceTypeId) => {
|
|
1020
|
+
try {
|
|
1021
|
+
const { data } = await session.api.integrations.proxy_read({
|
|
1022
|
+
id: enduserId,
|
|
1023
|
+
integration: BRIDGE_TITLE,
|
|
1024
|
+
type: 'provider-eligibility',
|
|
1025
|
+
query: JSON.stringify({
|
|
1026
|
+
serviceTypeId,
|
|
1027
|
+
payerId,
|
|
1028
|
+
state,
|
|
1029
|
+
}),
|
|
1030
|
+
})
|
|
1031
|
+
return {
|
|
1032
|
+
serviceTypeId,
|
|
1033
|
+
status: data?.status || 'unknown',
|
|
1034
|
+
userIds: data?.userIds || [],
|
|
1035
|
+
}
|
|
1036
|
+
} catch (err: any) {
|
|
1037
|
+
console.error(`Provider eligibility check failed for ${serviceTypeId}:`, err)
|
|
1038
|
+
return {
|
|
1039
|
+
serviceTypeId,
|
|
1040
|
+
status: 'error',
|
|
1041
|
+
userIds: [],
|
|
1042
|
+
error: err?.message,
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
})
|
|
1046
|
+
)
|
|
1047
|
+
|
|
1048
|
+
// Aggregate results - union of userIds across all service types
|
|
1049
|
+
const allUserIds = results.flatMap(r => r.userIds)
|
|
1050
|
+
const uniqueUserIds = Array.from(new Set(allUserIds))
|
|
1051
|
+
|
|
1052
|
+
// Determine aggregated status (ELIGIBLE if any are eligible, otherwise first non-error status)
|
|
1053
|
+
const aggregatedStatus = results.some(r => r.status === 'ELIGIBLE')
|
|
1054
|
+
? 'ELIGIBLE'
|
|
1055
|
+
: results.find(r => r.status !== 'error')?.status || 'unknown'
|
|
1056
|
+
|
|
1057
|
+
// Store aggregated userIds in shared variable for Appointment Booking to use
|
|
1058
|
+
setBridgeEligibilityUserIds(uniqueUserIds)
|
|
1059
|
+
|
|
1060
|
+
// Update the answer with aggregated results
|
|
1061
|
+
onChange({
|
|
1062
|
+
payerId, // Store payerId to detect changes on remount
|
|
1063
|
+
status: aggregatedStatus,
|
|
1064
|
+
userIds: uniqueUserIds,
|
|
1065
|
+
}, field.id)
|
|
1066
|
+
} catch (err: any) {
|
|
1067
|
+
setError(err?.message || 'Failed to check eligibility')
|
|
1068
|
+
console.error('Provider eligibility check failed:', err)
|
|
1069
|
+
} finally {
|
|
1070
|
+
setLoading(false)
|
|
1071
|
+
}
|
|
1072
|
+
}, [session, field, payerId, state, onChange, enduserId])
|
|
1073
|
+
|
|
1074
|
+
// Hard eligibility check function with polling - supports multiple service type IDs
|
|
1075
|
+
const checkServiceEligibility = useCallback(async () => {
|
|
1076
|
+
const serviceTypeIds = field.options?.bridgeServiceTypeIds
|
|
1077
|
+
|
|
1078
|
+
if (!serviceTypeIds || serviceTypeIds.length === 0) {
|
|
1079
|
+
setError('Bridge Service Type IDs not configured')
|
|
1080
|
+
return
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
setLoading(true)
|
|
1084
|
+
setError(undefined)
|
|
1085
|
+
|
|
1086
|
+
try {
|
|
1087
|
+
// Initiate service eligibility checks for all service type IDs in parallel
|
|
1088
|
+
const initiatedChecks = await Promise.all(
|
|
1089
|
+
serviceTypeIds.map(async (serviceTypeId) => {
|
|
1090
|
+
try {
|
|
1091
|
+
const { data } = await session.api.integrations.proxy_read({
|
|
1092
|
+
id: enduserId,
|
|
1093
|
+
integration: BRIDGE_TITLE,
|
|
1094
|
+
type: 'service-eligibility',
|
|
1095
|
+
query: JSON.stringify({
|
|
1096
|
+
serviceTypeId,
|
|
1097
|
+
payerId,
|
|
1098
|
+
memberId,
|
|
1099
|
+
state,
|
|
1100
|
+
}),
|
|
1101
|
+
})
|
|
1102
|
+
|
|
1103
|
+
const serviceEligibilityId = data?.id
|
|
1104
|
+
if (!serviceEligibilityId) {
|
|
1105
|
+
throw new Error('No service eligibility ID returned')
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
return {
|
|
1109
|
+
serviceTypeId,
|
|
1110
|
+
serviceEligibilityId,
|
|
1111
|
+
error: undefined,
|
|
1112
|
+
}
|
|
1113
|
+
} catch (err: any) {
|
|
1114
|
+
console.error(`Service eligibility check initiation failed for ${serviceTypeId}:`, err)
|
|
1115
|
+
return {
|
|
1116
|
+
serviceTypeId,
|
|
1117
|
+
serviceEligibilityId: null,
|
|
1118
|
+
error: err?.message,
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
})
|
|
1122
|
+
)
|
|
1123
|
+
|
|
1124
|
+
setLoading(false)
|
|
1125
|
+
setPolling(true)
|
|
1126
|
+
|
|
1127
|
+
// Poll for results from all checks in parallel
|
|
1128
|
+
const pollForAllResults = async () => {
|
|
1129
|
+
const maxAttempts = 60 // Poll for up to 60 attempts (2 minutes at 2s intervals)
|
|
1130
|
+
|
|
1131
|
+
// Track completion status for each check
|
|
1132
|
+
const checkStatuses = new Map(
|
|
1133
|
+
initiatedChecks.map(check => [
|
|
1134
|
+
check.serviceTypeId,
|
|
1135
|
+
{
|
|
1136
|
+
completed: check.error !== undefined || check.serviceEligibilityId === null,
|
|
1137
|
+
result: check.error ? { status: 'error', userIds: [], error: check.error } : null,
|
|
1138
|
+
serviceEligibilityId: check.serviceEligibilityId,
|
|
1139
|
+
}
|
|
1140
|
+
])
|
|
1141
|
+
)
|
|
1142
|
+
|
|
1143
|
+
let attempts = 0
|
|
1144
|
+
|
|
1145
|
+
const pollAll = async (): Promise<void> => {
|
|
1146
|
+
if (attempts >= maxAttempts) {
|
|
1147
|
+
setError('Eligibility check timed out. Please try again.')
|
|
1148
|
+
setPolling(false)
|
|
1149
|
+
return
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
attempts++
|
|
1153
|
+
|
|
1154
|
+
// Poll all incomplete checks in parallel
|
|
1155
|
+
const pollPromises = initiatedChecks
|
|
1156
|
+
.filter(check => {
|
|
1157
|
+
const status = checkStatuses.get(check.serviceTypeId)
|
|
1158
|
+
return check.serviceEligibilityId && status && !status.completed
|
|
1159
|
+
})
|
|
1160
|
+
.map(async (check) => {
|
|
1161
|
+
try {
|
|
1162
|
+
const { data: pollData } = await session.api.integrations.proxy_read({
|
|
1163
|
+
id: check.serviceEligibilityId!,
|
|
1164
|
+
integration: BRIDGE_TITLE,
|
|
1165
|
+
type: 'service-eligibility-poll',
|
|
1166
|
+
})
|
|
1167
|
+
|
|
1168
|
+
const status = pollData?.status
|
|
1169
|
+
|
|
1170
|
+
// Check if we're in a terminal state
|
|
1171
|
+
if (status && status !== 'PENDING') {
|
|
1172
|
+
const checkStatus = checkStatuses.get(check.serviceTypeId)!
|
|
1173
|
+
checkStatus.completed = true
|
|
1174
|
+
checkStatus.result = {
|
|
1175
|
+
status: status || 'unknown',
|
|
1176
|
+
userIds: pollData?.userIds || [],
|
|
1177
|
+
error: undefined,
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
} catch (err: any) {
|
|
1181
|
+
console.error(`Service eligibility polling failed for ${check.serviceTypeId}:`, err)
|
|
1182
|
+
const checkStatus = checkStatuses.get(check.serviceTypeId)!
|
|
1183
|
+
checkStatus.completed = true
|
|
1184
|
+
checkStatus.result = {
|
|
1185
|
+
status: 'error',
|
|
1186
|
+
userIds: [],
|
|
1187
|
+
error: err?.message,
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
})
|
|
1191
|
+
|
|
1192
|
+
await Promise.all(pollPromises)
|
|
1193
|
+
|
|
1194
|
+
// Check if all checks are completed
|
|
1195
|
+
const allCompleted = Array.from(checkStatuses.values()).every(s => s.completed)
|
|
1196
|
+
|
|
1197
|
+
if (allCompleted) {
|
|
1198
|
+
// Aggregate results - union of userIds across all service types
|
|
1199
|
+
const results = Array.from(checkStatuses.entries()).map(([serviceTypeId, status]) => ({
|
|
1200
|
+
serviceTypeId,
|
|
1201
|
+
status: status.result?.status || 'unknown',
|
|
1202
|
+
userIds: status.result?.userIds || [],
|
|
1203
|
+
}))
|
|
1204
|
+
|
|
1205
|
+
const allUserIds = results.flatMap(r => r.userIds)
|
|
1206
|
+
const uniqueUserIds = Array.from(new Set(allUserIds))
|
|
1207
|
+
|
|
1208
|
+
// Determine aggregated status (ELIGIBLE if any are eligible, otherwise first non-error status)
|
|
1209
|
+
const aggregatedStatus = results.some(r => r.status === 'ELIGIBLE')
|
|
1210
|
+
? 'ELIGIBLE'
|
|
1211
|
+
: results.find(r => r.status !== 'error')?.status || 'unknown'
|
|
1212
|
+
|
|
1213
|
+
// Store aggregated userIds in shared variable for Appointment Booking to use
|
|
1214
|
+
setBridgeEligibilityUserIds(uniqueUserIds)
|
|
1215
|
+
|
|
1216
|
+
// Update the answer with aggregated results
|
|
1217
|
+
onChange({
|
|
1218
|
+
payerId, // Store payerId to detect changes on remount
|
|
1219
|
+
status: aggregatedStatus,
|
|
1220
|
+
userIds: uniqueUserIds,
|
|
1221
|
+
}, field.id)
|
|
1222
|
+
|
|
1223
|
+
setPolling(false)
|
|
1224
|
+
return
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
// Still have pending checks, poll again after delay
|
|
1228
|
+
setTimeout(pollAll, 2000) // Poll every 2 seconds
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
pollAll()
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
pollForAllResults()
|
|
1235
|
+
} catch (err: any) {
|
|
1236
|
+
setError(err?.message || 'Failed to check service eligibility')
|
|
1237
|
+
console.error('Service eligibility check failed:', err)
|
|
1238
|
+
setLoading(false)
|
|
1239
|
+
setPolling(false)
|
|
1240
|
+
}
|
|
1241
|
+
}, [session, field, payerId, memberId, state, onChange, enduserId])
|
|
1242
|
+
|
|
1243
|
+
// Auto-check eligibility for enduser sessions
|
|
1244
|
+
const autoCheckRef = useRef(false)
|
|
1245
|
+
useEffect(() => {
|
|
1246
|
+
if (!isEnduserSession) return
|
|
1247
|
+
|
|
1248
|
+
// If we already have a result and the payer hasn't changed, use the cached result
|
|
1249
|
+
if (value?.status && value?.payerId === payerId) {
|
|
1250
|
+
return
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
if (autoCheckRef.current) return
|
|
1254
|
+
autoCheckRef.current = true
|
|
1255
|
+
|
|
1256
|
+
if (eligibilityType === 'Hard') {
|
|
1257
|
+
checkServiceEligibility()
|
|
1258
|
+
} else {
|
|
1259
|
+
checkProviderEligibility()
|
|
1260
|
+
}
|
|
1261
|
+
}, [isEnduserSession, eligibilityType, checkProviderEligibility, checkServiceEligibility, value, payerId])
|
|
1262
|
+
|
|
1263
|
+
const errorComponent = useMemo(() => (
|
|
1264
|
+
<Grid container spacing={2} direction="column" alignItems="center" style={{ padding: '20px 0' }}>
|
|
1265
|
+
<Grid item>
|
|
1266
|
+
<Paper style={{
|
|
1267
|
+
padding: 16,
|
|
1268
|
+
backgroundColor: '#ffebee',
|
|
1269
|
+
border: '2px solid #f44336'
|
|
1270
|
+
}}>
|
|
1271
|
+
<Grid container spacing={2} direction="column" alignItems="center">
|
|
1272
|
+
<Grid item>
|
|
1273
|
+
<Typography variant="h2" style={{ color: '#f44336' }}>⚠️</Typography>
|
|
1274
|
+
</Grid>
|
|
1275
|
+
<Grid item>
|
|
1276
|
+
<Typography variant="h6" align="center" color="error">
|
|
1277
|
+
Unable to Check Eligibility
|
|
1278
|
+
</Typography>
|
|
1279
|
+
</Grid>
|
|
1280
|
+
<Grid item>
|
|
1281
|
+
<Typography variant="body2" align="center" style={{ color: '#d32f2f' }}>
|
|
1282
|
+
{error}
|
|
1283
|
+
</Typography>
|
|
1284
|
+
</Grid>
|
|
1285
|
+
</Grid>
|
|
1286
|
+
</Paper>
|
|
1287
|
+
</Grid>
|
|
1288
|
+
</Grid>
|
|
1289
|
+
), [error])
|
|
1290
|
+
|
|
1291
|
+
const checkingEligibilityComponent = useMemo(() => (
|
|
1292
|
+
<Grid container spacing={2} direction="column" alignItems="center" style={{ padding: '20px 0' }}>
|
|
1293
|
+
<Grid item>
|
|
1294
|
+
<CircularProgress size={40} />
|
|
1295
|
+
</Grid>
|
|
1296
|
+
<Grid item>
|
|
1297
|
+
<Typography variant="body1">
|
|
1298
|
+
{polling ? 'Verifying eligibility with insurance...' : 'Checking eligibility...'}
|
|
1299
|
+
</Typography>
|
|
1300
|
+
</Grid>
|
|
1301
|
+
<Grid item>
|
|
1302
|
+
<Typography variant="body2" color="textSecondary">
|
|
1303
|
+
{polling ? 'This usually takes 15-30 seconds' : 'This may take a few moments'}
|
|
1304
|
+
</Typography>
|
|
1305
|
+
</Grid>
|
|
1306
|
+
</Grid>
|
|
1307
|
+
), [polling])
|
|
1308
|
+
|
|
1309
|
+
const resultsComponent = useMemo(() => {
|
|
1310
|
+
const isEligible = value?.status === 'ELIGIBLE'
|
|
1311
|
+
return (
|
|
1312
|
+
<Grid container spacing={2} direction="column">
|
|
1313
|
+
<Grid item>
|
|
1314
|
+
<Paper style={{
|
|
1315
|
+
padding: 16,
|
|
1316
|
+
backgroundColor: isEligible ? '#e8f5e9' : '#fff3e0',
|
|
1317
|
+
border: `2px solid ${isEligible ? '#4caf50' : '#ff9800'}`
|
|
1318
|
+
}}>
|
|
1319
|
+
<Grid container spacing={2} direction="column" alignItems="center">
|
|
1320
|
+
<Grid item>
|
|
1321
|
+
{isEligible ? (
|
|
1322
|
+
<CheckCircleOutline style={{ fontSize: 48, color: '#4caf50' }} />
|
|
1323
|
+
) : (
|
|
1324
|
+
<Typography variant="h2" style={{ color: '#ff9800' }}>⚠️</Typography>
|
|
1325
|
+
)}
|
|
1326
|
+
</Grid>
|
|
1327
|
+
<Grid item>
|
|
1328
|
+
<Typography variant="h6" align="center">
|
|
1329
|
+
{isEligible
|
|
1330
|
+
? `${payerName || 'Your insurance provider'} is accepted!`
|
|
1331
|
+
: 'Eligibility Status: ' + first_letter_capitalized((value?.status || 'Unknown').toLowerCase())
|
|
1332
|
+
}
|
|
1333
|
+
</Typography>
|
|
1334
|
+
</Grid>
|
|
1335
|
+
</Grid>
|
|
1336
|
+
</Paper>
|
|
1337
|
+
</Grid>
|
|
1338
|
+
</Grid>
|
|
1339
|
+
)
|
|
1340
|
+
}, [value, payerName])
|
|
1341
|
+
|
|
1342
|
+
// Loading/polling state for enduser sessions
|
|
1343
|
+
if (isEnduserSession) {
|
|
1344
|
+
if (loading || polling) { return checkingEligibilityComponent }
|
|
1345
|
+
if (error) {
|
|
1346
|
+
return errorComponent
|
|
1347
|
+
}
|
|
1348
|
+
if (value?.status) {
|
|
1349
|
+
return resultsComponent
|
|
1350
|
+
}
|
|
1351
|
+
return errorComponent
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
// User/admin interface (non-enduser sessions)
|
|
1355
|
+
return (
|
|
1356
|
+
<Grid container spacing={2} direction="column">
|
|
1357
|
+
<Grid item>
|
|
1358
|
+
<Typography variant="body2" color="textSecondary">
|
|
1359
|
+
Eligibility Type: {eligibilityType}
|
|
1360
|
+
</Typography>
|
|
1361
|
+
<Typography variant="body2" color="textSecondary">
|
|
1362
|
+
Service Type IDs: {field.options?.bridgeServiceTypeIds?.join(', ') || 'Not configured'}
|
|
1363
|
+
</Typography>
|
|
1364
|
+
{state && <Typography variant="body2" color="textSecondary">State: {state}</Typography>}
|
|
1365
|
+
{payerId && <Typography variant="body2" color="textSecondary">Payer ID: {payerId}</Typography>}
|
|
1366
|
+
{memberId && <Typography variant="body2" color="textSecondary">Member ID: {memberId}</Typography>}
|
|
1367
|
+
</Grid>
|
|
1368
|
+
|
|
1369
|
+
{error && (
|
|
1370
|
+
<Grid item>
|
|
1371
|
+
<Typography variant="body2" color="error">{error}</Typography>
|
|
1372
|
+
</Grid>
|
|
1373
|
+
)}
|
|
1374
|
+
|
|
1375
|
+
{polling && (
|
|
1376
|
+
<Grid item>
|
|
1377
|
+
<Typography variant="body2" color="primary">
|
|
1378
|
+
Polling for results... (this may take 15-30 seconds)
|
|
1379
|
+
</Typography>
|
|
1380
|
+
</Grid>
|
|
1381
|
+
)}
|
|
1382
|
+
|
|
1383
|
+
<Grid item container spacing={2}>
|
|
1384
|
+
<Grid item>
|
|
1385
|
+
<LoadingButton
|
|
1386
|
+
variant="outlined"
|
|
1387
|
+
onClick={checkProviderEligibility}
|
|
1388
|
+
submitText="Check Provider Eligibility (Free)"
|
|
1389
|
+
submittingText="Checking..."
|
|
1390
|
+
submitting={loading && !polling}
|
|
1391
|
+
disabled={!field.options?.bridgeServiceTypeIds?.length || loading || polling}
|
|
1392
|
+
/>
|
|
1393
|
+
</Grid>
|
|
1394
|
+
<Grid item>
|
|
1395
|
+
<LoadingButton
|
|
1396
|
+
variant="outlined"
|
|
1397
|
+
onClick={checkServiceEligibility}
|
|
1398
|
+
submitText="Check Service Eligibility (Paid)"
|
|
1399
|
+
submittingText={polling ? "Polling..." : "Initiating..."}
|
|
1400
|
+
submitting={loading || polling}
|
|
1401
|
+
disabled={!field.options?.bridgeServiceTypeIds?.length || loading || polling}
|
|
1402
|
+
/>
|
|
1403
|
+
</Grid>
|
|
1404
|
+
</Grid>
|
|
1405
|
+
|
|
1406
|
+
{value && (
|
|
1407
|
+
<Grid item>
|
|
1408
|
+
<Typography variant="caption" color="textSecondary">
|
|
1409
|
+
Current Answer:
|
|
1410
|
+
</Typography>
|
|
1411
|
+
<pre style={{ fontSize: 11, whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>
|
|
1412
|
+
{JSON.stringify(value, null, 2)}
|
|
1413
|
+
</pre>
|
|
1414
|
+
</Grid>
|
|
1415
|
+
)}
|
|
1416
|
+
</Grid>
|
|
1417
|
+
)
|
|
1418
|
+
}
|
|
1419
|
+
|
|
933
1420
|
const HourSelector = (props : { value: string, onChange: (v: string) => void }) => (
|
|
934
|
-
<StringSelector {...props}
|
|
935
|
-
options={Array(12).fill('').map((_, i) => (i + 1) <= 9 ? `0${i + 1}` : (i + 1).toString())}
|
|
1421
|
+
<StringSelector {...props}
|
|
1422
|
+
options={Array(12).fill('').map((_, i) => (i + 1) <= 9 ? `0${i + 1}` : (i + 1).toString())}
|
|
936
1423
|
/>
|
|
937
1424
|
)
|
|
938
1425
|
const MinuteSelector = (props : { value: string, onChange: (v: string) => void }) => (
|
|
@@ -3685,6 +4172,16 @@ export const AppointmentBookingInput = ({ formResponseId, field, value, onChange
|
|
|
3685
4172
|
.join(',')
|
|
3686
4173
|
}`
|
|
3687
4174
|
}
|
|
4175
|
+
// Filter to Bridge eligibility userIds if option is enabled
|
|
4176
|
+
if (field.options?.useBridgeEligibilityResult) {
|
|
4177
|
+
const bridgeUserIds = getBridgeEligibilityUserIds()
|
|
4178
|
+
|
|
4179
|
+
if (bridgeUserIds.length === 0) {
|
|
4180
|
+
return <Typography>No eligible users found for booking</Typography>
|
|
4181
|
+
}
|
|
4182
|
+
|
|
4183
|
+
bookingURL += `&userIds=${bridgeUserIds.join(',')}`
|
|
4184
|
+
}
|
|
3688
4185
|
// need to use form?.id for internally-submitted forms because formResponseId isn't generated until initial submission or saved draft
|
|
3689
4186
|
if (field.options?.holdAppointmentMinutes && (formResponseId || field?.id)) {
|
|
3690
4187
|
bookingURL += `&formResponseId=${formResponseId || field?.id}`
|