@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.v2.tsx
CHANGED
|
@@ -544,368 +544,17 @@ export const NumberInput = ({ field, value, onChange, form, ...props }: FormInpu
|
|
|
544
544
|
)
|
|
545
545
|
}
|
|
546
546
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
const [payers, setPayers] = useState<{ id: string, name: string, databaseRecord?: DatabaseRecord, type?: string, state?: string }[]>([])
|
|
551
|
-
const [query, setQuery] = useState('')
|
|
552
|
-
|
|
553
|
-
const addressQuestion = useMemo(() => responses?.find(r => {
|
|
554
|
-
if (r.answer.type !== 'Address') return false
|
|
555
|
-
if (r.field.intakeField !== 'Address') return false
|
|
556
|
-
|
|
557
|
-
// make sure state is actually defined (in case of multiple address questions, where 1+ are blank)
|
|
558
|
-
if (!r.answer.value?.state) return false
|
|
559
|
-
|
|
560
|
-
return true
|
|
561
|
-
}), [responses])
|
|
562
|
-
|
|
563
|
-
const state = useMemo(() => (
|
|
564
|
-
(addressQuestion?.answer?.type === 'Address' ? addressQuestion?.answer?.value?.state : undefined) || enduser?.state
|
|
565
|
-
), [enduser?.state, addressQuestion])
|
|
566
|
-
|
|
567
|
-
const loadRef = useRef(false) // so session changes don't cause
|
|
568
|
-
useEffect(() => {
|
|
569
|
-
if (field?.options?.dataSource === CANVAS_TITLE) return // instead, look-up while typing against Canvas Search API
|
|
570
|
-
if (loadRef.current) return
|
|
571
|
-
loadRef.current = true
|
|
572
|
-
|
|
573
|
-
// just load all at once, should be reasonably performant compared to paging
|
|
574
|
-
session.api.form_fields.load_choices_from_database({ fieldId: field.id, limit: 10000 })
|
|
575
|
-
.then(({ choices }) => setPayers(
|
|
576
|
-
choices
|
|
577
|
-
.map(c => ({
|
|
578
|
-
id: c.values.find(v => v.label?.trim()?.toLowerCase() === 'id')?.value?.toString() || '',
|
|
579
|
-
name: c.values.find(v => v.label?.trim()?.toLowerCase() === 'name')?.value?.toString() || '',
|
|
580
|
-
state: c.values.find(v => v.label?.trim()?.toLowerCase() === 'state')?.value?.toString() || '',
|
|
581
|
-
type: c.values.find(v => v.label?.trim()?.toLowerCase() === 'type')?.value?.toString() || '',
|
|
582
|
-
databaseRecord: c,
|
|
583
|
-
}))
|
|
584
|
-
.filter(c => !c.state || !state || (c.state === state))
|
|
585
|
-
))
|
|
586
|
-
.catch(console.error)
|
|
587
|
-
}, [session, state, field?.options?.dataSource])
|
|
588
|
-
|
|
589
|
-
const searchRef = useRef(query)
|
|
590
|
-
useEffect(() => {
|
|
591
|
-
if (field?.options?.dataSource !== CANVAS_TITLE) { return }
|
|
592
|
-
if (!query) return
|
|
593
|
-
if (searchRef.current === query) return
|
|
594
|
-
searchRef.current = query
|
|
595
|
-
|
|
596
|
-
session.api.integrations.proxy_read({
|
|
597
|
-
integration: CANVAS_TITLE,
|
|
598
|
-
query,
|
|
599
|
-
type: 'organizations',
|
|
600
|
-
})
|
|
601
|
-
.then(({ data }) => {
|
|
602
|
-
try {
|
|
603
|
-
setPayers(data.map((d: any) => ({
|
|
604
|
-
id: d.resource.id,
|
|
605
|
-
name: d.resource.name,
|
|
606
|
-
})))
|
|
607
|
-
} catch(err) { console.error }
|
|
608
|
-
})
|
|
609
|
-
.catch(console.error)
|
|
610
|
-
}, [session, field?.options?.dataSource, query])
|
|
611
|
-
|
|
612
|
-
return (
|
|
613
|
-
<Grid container spacing={2} sx={{ mt: '0' }}>
|
|
614
|
-
<Grid item xs={12} sm={6}>
|
|
615
|
-
<Autocomplete freeSolo={!field.options?.requirePredefinedInsurer} options={payers.map(p => p.name)}
|
|
616
|
-
value={value?.payerName || ''}
|
|
617
|
-
onChange={(e, v) => onChange({
|
|
618
|
-
...value,
|
|
619
|
-
payerName: v || '',
|
|
620
|
-
payerId: payers.find(p => p.name === v)?.id || '',
|
|
621
|
-
payerType: payers.find(p => p.name === v)?.type || '',
|
|
622
|
-
}, field.id)}
|
|
623
|
-
onInputChange={
|
|
624
|
-
field.options?.requirePredefinedInsurer
|
|
625
|
-
? (e, v) => { if (v) { setQuery(v) } }
|
|
626
|
-
: (e, v) => {
|
|
627
|
-
if (v) { setQuery(v) }
|
|
628
|
-
|
|
629
|
-
const databaseRecord = payers.find(p => p.name === v)?.databaseRecord
|
|
630
|
-
if (databaseRecord) {
|
|
631
|
-
onDatabaseSelect?.([databaseRecord])
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
onChange({
|
|
635
|
-
...value,
|
|
636
|
-
payerName: v || '',
|
|
637
|
-
payerId: payers.find(p => p.name === v)?.id || '',
|
|
638
|
-
payerType: payers.find(p => p.name === v)?.type || '',
|
|
639
|
-
}, field.id)
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
renderInput={(params) => (
|
|
643
|
-
<TextField {...params} InputProps={{ ...params.InputProps, sx: defaultInputProps.sx }}
|
|
644
|
-
required={!field.isOptional} size="small" label={"Insurer"}
|
|
645
|
-
placeholder={field.options?.dataSource === CANVAS_TITLE ? "Search insurer..." : "Insurer"}
|
|
646
|
-
/>
|
|
647
|
-
)}
|
|
648
|
-
/>
|
|
649
|
-
</Grid>
|
|
650
|
-
<Grid item xs={12} sm={6}>
|
|
651
|
-
<TextField InputProps={defaultInputProps} required={!field.isOptional} fullWidth value={value?.memberId ?? ''}
|
|
652
|
-
onChange={e => onChange({ ...value, memberId: e.target.value }, field.id)}
|
|
653
|
-
label={form_display_text_for_language(form, "Member ID", '')}
|
|
654
|
-
size="small"
|
|
655
|
-
/>
|
|
656
|
-
</Grid>
|
|
657
|
-
|
|
658
|
-
<Grid item xs={12} sm={6}>
|
|
659
|
-
<TextField InputProps={defaultInputProps} required={false} fullWidth value={value?.planName ?? ''}
|
|
660
|
-
onChange={e => onChange({ ...value, planName: e.target.value }, field.id)}
|
|
661
|
-
label={form_display_text_for_language(form, "Plan Name", '')}
|
|
662
|
-
size="small"
|
|
663
|
-
/>
|
|
664
|
-
</Grid>
|
|
665
|
-
|
|
666
|
-
<Grid item xs={12} sm={6}>
|
|
667
|
-
<DateStringInput size="small" label="Plan Start Date"
|
|
668
|
-
field={{
|
|
669
|
-
...field,
|
|
670
|
-
isOptional: true, //field.isOptional || field.options?.billingProvider === 'Candid'
|
|
671
|
-
}}
|
|
672
|
-
value={value?.startDate || ''}
|
|
673
|
-
onChange={startDate =>
|
|
674
|
-
onChange({
|
|
675
|
-
...value,
|
|
676
|
-
startDate,
|
|
677
|
-
}, field.id)
|
|
678
|
-
}
|
|
679
|
-
/>
|
|
680
|
-
</Grid>
|
|
681
|
-
|
|
682
|
-
{field.options?.includeGroupNumber &&
|
|
683
|
-
<Grid item xs={12}>
|
|
684
|
-
<TextField InputProps={defaultInputProps} fullWidth value={value?.groupNumber ?? ''}
|
|
685
|
-
onChange={e => onChange({ ...value, groupNumber: e.target.value }, field.id)}
|
|
686
|
-
label={form_display_text_for_language(form, "Group Number", '')}
|
|
687
|
-
size="small"
|
|
688
|
-
/>
|
|
689
|
-
</Grid>
|
|
690
|
-
}
|
|
547
|
+
// InsuranceInput, BridgeEligibilityInput, and AppointmentBookingInput logic is shared with inputs.tsx to avoid duplication
|
|
548
|
+
import { InsuranceInput as SharedInsuranceInput, BridgeEligibilityInput as SharedBridgeEligibilityInput, AppointmentBookingInput as SharedAppointmentBookingInput } from './inputs'
|
|
691
549
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
(field.options?.billingProvider === CANVAS_TITLE || field.options?.dataSource === CANVAS_TITLE )
|
|
697
|
-
? INSURANCE_RELATIONSHIPS_CANVAS
|
|
698
|
-
: INSURANCE_RELATIONSHIPS
|
|
699
|
-
)
|
|
700
|
-
.sort((x, y) => x.localeCompare(y))
|
|
701
|
-
}
|
|
702
|
-
value={value?.relationship || 'Self'}
|
|
703
|
-
onChange={relationship =>
|
|
704
|
-
onChange({ ...value, relationship: relationship as InsuranceRelationship || 'Self' }, field.id)
|
|
705
|
-
}
|
|
706
|
-
/>
|
|
707
|
-
</Grid>
|
|
708
|
-
|
|
709
|
-
{(value?.relationship || 'Self') !== 'Self' &&
|
|
710
|
-
<>
|
|
711
|
-
<Grid item xs={12}>
|
|
712
|
-
<Typography sx={{ fontWeight: 'bold' }}>Policy Owner Details</Typography>
|
|
713
|
-
</Grid>
|
|
714
|
-
|
|
715
|
-
<Grid item xs={6}>
|
|
716
|
-
<TextField label="First Name" size="small" InputProps={defaultInputProps} fullWidth
|
|
717
|
-
value={value?.relationshipDetails?.fname || ''}
|
|
718
|
-
required={!field.isOptional}
|
|
719
|
-
onChange={e =>
|
|
720
|
-
onChange({
|
|
721
|
-
...value,
|
|
722
|
-
relationshipDetails: { ...value?.relationshipDetails, fname: e.target.value }
|
|
723
|
-
}, field.id)
|
|
724
|
-
}
|
|
725
|
-
/>
|
|
726
|
-
</Grid>
|
|
727
|
-
<Grid item xs={6}>
|
|
728
|
-
<TextField label="Last Name" size="small" InputProps={defaultInputProps} fullWidth
|
|
729
|
-
value={value?.relationshipDetails?.lname || ''}
|
|
730
|
-
required={!field.isOptional}
|
|
731
|
-
onChange={e =>
|
|
732
|
-
onChange({
|
|
733
|
-
...value,
|
|
734
|
-
relationshipDetails: { ...value?.relationshipDetails, lname: e.target.value }
|
|
735
|
-
}, field.id)
|
|
736
|
-
}
|
|
737
|
-
/>
|
|
738
|
-
</Grid>
|
|
739
|
-
<Grid item xs={6}>
|
|
740
|
-
<StringSelector options={TELLESCOPE_GENDERS} size="small" label="Gender"
|
|
741
|
-
value={value?.relationshipDetails?.gender || ''}
|
|
742
|
-
required={!field.isOptional}
|
|
743
|
-
onChange={v =>
|
|
744
|
-
onChange({
|
|
745
|
-
...value,
|
|
746
|
-
relationshipDetails: { ...value?.relationshipDetails, gender: v as TellescopeGender }
|
|
747
|
-
}, field.id)
|
|
748
|
-
}
|
|
749
|
-
/>
|
|
750
|
-
</Grid>
|
|
751
|
-
<Grid item xs={6}>
|
|
752
|
-
<DateStringInput size="small" label="Date of Birth"
|
|
753
|
-
field={{
|
|
754
|
-
...field,
|
|
755
|
-
isOptional: field.isOptional || field.options?.billingProvider === 'Candid'
|
|
756
|
-
}}
|
|
757
|
-
value={value?.relationshipDetails?.dateOfBirth || ''}
|
|
758
|
-
onChange={dateOfBirth =>
|
|
759
|
-
onChange({
|
|
760
|
-
...value,
|
|
761
|
-
relationshipDetails: { ...value?.relationshipDetails, dateOfBirth }
|
|
762
|
-
}, field.id)
|
|
763
|
-
}
|
|
764
|
-
/>
|
|
765
|
-
</Grid>
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
{/* <Grid item xs={6}>
|
|
769
|
-
<TextField label="Email" type="email" size="small" InputProps={defaultInputProps} fullWidth
|
|
770
|
-
value={value?.relationshipDetails?.email || ''}
|
|
771
|
-
onChange={e =>
|
|
772
|
-
onChange({
|
|
773
|
-
...value,
|
|
774
|
-
relationshipDetails: { ...value?.relationshipDetails, email: e.target.value }
|
|
775
|
-
}, field.id)
|
|
776
|
-
}
|
|
777
|
-
/>
|
|
778
|
-
</Grid>
|
|
779
|
-
<Grid item xs={6}>
|
|
780
|
-
<TextField label="Cell Phone" size="small" InputProps={defaultInputProps} fullWidth
|
|
781
|
-
value={value?.relationshipDetails?.phone || ''}
|
|
782
|
-
onChange={e =>
|
|
783
|
-
onChange({
|
|
784
|
-
...value,
|
|
785
|
-
relationshipDetails: { ...value?.relationshipDetails, phone: e.target.value }
|
|
786
|
-
}, field.id)
|
|
787
|
-
}
|
|
788
|
-
/>
|
|
789
|
-
</Grid>
|
|
790
|
-
|
|
791
|
-
<Grid item xs={12}>
|
|
792
|
-
<AddressInput field={field} value={{
|
|
793
|
-
addressLineOne: value?.relationshipDetails?.address?.lineOne || '',
|
|
794
|
-
addressLineTwo: value?.relationshipDetails?.address?.lineTwo || '',
|
|
795
|
-
city: value?.relationshipDetails?.address?.city || '',
|
|
796
|
-
state: value?.relationshipDetails?.address?.state || '',
|
|
797
|
-
zipCode: value?.relationshipDetails?.address?.zipCode || '',
|
|
798
|
-
}}
|
|
799
|
-
onChange={v => {
|
|
800
|
-
const { addressLineOne='', addressLineTwo='', ...address } = v || {}
|
|
801
|
-
onChange({
|
|
802
|
-
...value,
|
|
803
|
-
relationshipDetails: {
|
|
804
|
-
...value?.relationshipDetails,
|
|
805
|
-
address: {
|
|
806
|
-
lineOne: addressLineOne,
|
|
807
|
-
lineTwo: addressLineTwo,
|
|
808
|
-
...address,
|
|
809
|
-
},
|
|
810
|
-
}
|
|
811
|
-
}, field.id)
|
|
812
|
-
}}
|
|
813
|
-
/>
|
|
814
|
-
</Grid> */}
|
|
815
|
-
|
|
816
|
-
{/* <Grid item xs={6}>
|
|
817
|
-
<TextField label="Address"
|
|
818
|
-
value={value?.relationshipDetails?.address?.lineOne || ''}
|
|
819
|
-
onChange={e =>
|
|
820
|
-
onChange({
|
|
821
|
-
...value,
|
|
822
|
-
relationshipDetails: {
|
|
823
|
-
...value?.relationshipDetails,
|
|
824
|
-
address: {
|
|
825
|
-
...value?.relationshipDetails?.address,
|
|
826
|
-
lineOne: e.target.value
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
}, field.id)
|
|
830
|
-
}
|
|
831
|
-
/>
|
|
832
|
-
</Grid>
|
|
833
|
-
<Grid item xs={6}>
|
|
834
|
-
<TextField label="Line Two"
|
|
835
|
-
value={value?.relationshipDetails?.address?.lineTwo || ''}
|
|
836
|
-
onChange={e =>
|
|
837
|
-
onChange({
|
|
838
|
-
...value,
|
|
839
|
-
relationshipDetails: {
|
|
840
|
-
...value?.relationshipDetails,
|
|
841
|
-
address: {
|
|
842
|
-
...value?.relationshipDetails?.address,
|
|
843
|
-
lineTwo: e.target.value
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
}, field.id)
|
|
847
|
-
}
|
|
848
|
-
/>
|
|
849
|
-
</Grid>
|
|
850
|
-
<Grid item xs={6}>
|
|
851
|
-
<TextField label="City"
|
|
852
|
-
value={value?.relationshipDetails?.address?.city || ''}
|
|
853
|
-
onChange={e =>
|
|
854
|
-
onChange({
|
|
855
|
-
...value,
|
|
856
|
-
relationshipDetails: {
|
|
857
|
-
...value?.relationshipDetails,
|
|
858
|
-
address: {
|
|
859
|
-
...value?.relationshipDetails?.address,
|
|
860
|
-
city: e.target.value
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
}, field.id)
|
|
864
|
-
}
|
|
865
|
-
/>
|
|
866
|
-
</Grid>
|
|
867
|
-
<Grid item xs={2}>
|
|
868
|
-
<TextField label="State"
|
|
869
|
-
value={value?.relationshipDetails?.address?.state || ''}
|
|
870
|
-
onChange={e =>
|
|
871
|
-
onChange({
|
|
872
|
-
...value,
|
|
873
|
-
relationshipDetails: {
|
|
874
|
-
...value?.relationshipDetails,
|
|
875
|
-
address: {
|
|
876
|
-
...value?.relationshipDetails?.address,
|
|
877
|
-
state: e.target.value
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
}, field.id)
|
|
881
|
-
}
|
|
882
|
-
/>
|
|
883
|
-
</Grid>
|
|
550
|
+
// Wrap the shared InsuranceInput component with v2-specific props
|
|
551
|
+
export const InsuranceInput = (props: FormInputProps<'Insurance'>) => {
|
|
552
|
+
return <SharedInsuranceInput {...props} inputProps={defaultInputProps} />
|
|
553
|
+
}
|
|
884
554
|
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
sx={{ width: 100 }}
|
|
889
|
-
disablePortal
|
|
890
|
-
onChange={(e, v) => v &&
|
|
891
|
-
onChange({
|
|
892
|
-
...value as any,
|
|
893
|
-
state: v ?? '',
|
|
894
|
-
},
|
|
895
|
-
field.id
|
|
896
|
-
)}
|
|
897
|
-
renderInput={(params) => (
|
|
898
|
-
<TextField {...params} InputProps={{ ...params.InputProps, sx: defaultInputProps.sx }}
|
|
899
|
-
size={'small'} label={"State"} required={!field.isOptional}
|
|
900
|
-
/>
|
|
901
|
-
)}
|
|
902
|
-
{...props}
|
|
903
|
-
/>
|
|
904
|
-
</Grid> */}
|
|
905
|
-
</>
|
|
906
|
-
}
|
|
907
|
-
</Grid>
|
|
908
|
-
)
|
|
555
|
+
// Wrap the shared BridgeEligibilityInput component with v2-specific props
|
|
556
|
+
export const BridgeEligibilityInput = (props: FormInputProps<'Bridge Eligibility'>) => {
|
|
557
|
+
return <SharedBridgeEligibilityInput {...props} inputProps={defaultInputProps} />
|
|
909
558
|
}
|
|
910
559
|
|
|
911
560
|
|
|
@@ -1543,7 +1192,7 @@ export const MultipleChoiceInput = ({ field, form, value: _value, onChange }: Fo
|
|
|
1543
1192
|
display: 'flex',
|
|
1544
1193
|
alignItems: 'center',
|
|
1545
1194
|
width: '100%',
|
|
1546
|
-
border: isSelected ? '
|
|
1195
|
+
border: isSelected ? '3px solid' : '1px solid',
|
|
1547
1196
|
borderColor: 'primary.main',
|
|
1548
1197
|
borderRadius: 1,
|
|
1549
1198
|
padding: '16px 16px',
|
|
@@ -2667,239 +2316,9 @@ export const RelatedContactsInput = ({ field, value: _value, onChange, error: pa
|
|
|
2667
2316
|
)
|
|
2668
2317
|
}
|
|
2669
2318
|
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
const [loaded, setLoaded] = useState<Awaited<ReturnType<typeof session['api']['form_fields']['booking_info']>>>()
|
|
2674
|
-
const [error, setError] = useState('')
|
|
2675
|
-
const [acknowledgedWarning, setAcknowledgedWarning] = useState(false)
|
|
2676
|
-
const [height, setHeight] = useState(450)
|
|
2677
|
-
const [confirming, setConfirming] = useState(false)
|
|
2678
|
-
|
|
2679
|
-
const bookingPageId = field?.options?.bookingPageId
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
const downloadICS = useCallback(async (event : Pick<CalendarEvent, 'id'>) => {
|
|
2683
|
-
try {
|
|
2684
|
-
downloadFile(
|
|
2685
|
-
await session.api.calendar_events.download_ics_file({ calendarEventId: event.id, excludeAttendee: true }) as any,
|
|
2686
|
-
{ name: "event.ics", dataIsURL: true, type: 'text/calendar'}
|
|
2687
|
-
)
|
|
2688
|
-
} catch(err) {
|
|
2689
|
-
console.error(err)
|
|
2690
|
-
}
|
|
2691
|
-
}, [session])
|
|
2692
|
-
|
|
2693
|
-
const addressQuestion = useMemo(() => responses?.find(r => {
|
|
2694
|
-
if (r.answer.type !== 'Address') return false
|
|
2695
|
-
if (r.field.intakeField !== 'Address') return false
|
|
2696
|
-
|
|
2697
|
-
// make sure state is actually defined (in case of multiple address questions, where 1+ are blank)
|
|
2698
|
-
if (!r.answer.value?.state) return false
|
|
2699
|
-
|
|
2700
|
-
return true
|
|
2701
|
-
}), [responses])
|
|
2702
|
-
const state = useMemo(() => (
|
|
2703
|
-
addressQuestion?.answer?.type === 'Address' ? addressQuestion?.answer?.value?.state : undefined
|
|
2704
|
-
), [addressQuestion])
|
|
2705
|
-
|
|
2706
|
-
const loadBookingInfo = useCallback(() => {
|
|
2707
|
-
if (!bookingPageId) return
|
|
2708
|
-
|
|
2709
|
-
setError('')
|
|
2710
|
-
session.api.form_fields.booking_info({
|
|
2711
|
-
enduserId,
|
|
2712
|
-
bookingPageId,
|
|
2713
|
-
enduserFields: { state }
|
|
2714
|
-
})
|
|
2715
|
-
.then(setLoaded)
|
|
2716
|
-
.catch(e => setError(e?.message || e?.toString() || 'Error loading appointment details'))
|
|
2717
|
-
}, [enduserId, bookingPageId, session, state])
|
|
2718
|
-
|
|
2719
|
-
const fetchRef = useRef(false)
|
|
2720
|
-
useEffect(() => {
|
|
2721
|
-
if (value) return
|
|
2722
|
-
if (!bookingPageId) return
|
|
2723
|
-
if (fetchRef.current) return
|
|
2724
|
-
fetchRef.current = true
|
|
2725
|
-
|
|
2726
|
-
loadBookingInfo()
|
|
2727
|
-
}, [bookingPageId, loadBookingInfo, value])
|
|
2728
|
-
|
|
2729
|
-
useEffect(() => {
|
|
2730
|
-
const handleMessage = (m: MessageEvent) => {
|
|
2731
|
-
// entropy to separate from other booking pages rendered on the same screen
|
|
2732
|
-
if (
|
|
2733
|
-
m?.data?.type === 'Booking Success'
|
|
2734
|
-
&& typeof m?.data?.bookedEventId === 'string'
|
|
2735
|
-
&& (!m?.data?.entropy || m?.data?.entropy === loaded?.entropy)
|
|
2736
|
-
) {
|
|
2737
|
-
onChange(m.data.bookedEventId, field.id)
|
|
2738
|
-
emit_gtm_event({ event: 'form_progress', fieldId: field.id, formId: field.formId, title: field.title, status: "Appointment Booked" })
|
|
2739
|
-
}
|
|
2740
|
-
if (m?.data?.type === 'CalendarPicker') {
|
|
2741
|
-
setHeight(750)
|
|
2742
|
-
}
|
|
2743
|
-
if (m?.data?.type === 'UsersPicker') {
|
|
2744
|
-
setHeight(450)
|
|
2745
|
-
}
|
|
2746
|
-
if (m?.data?.type === 'Confirmation') {
|
|
2747
|
-
setConfirming(true)
|
|
2748
|
-
}
|
|
2749
|
-
if (m?.data?.type === 'Join Link' && m?.data?.link) {
|
|
2750
|
-
update_local_storage('tellescope_last_booking_page_join_link', m.data.link)
|
|
2751
|
-
}
|
|
2752
|
-
else {
|
|
2753
|
-
setConfirming(false)
|
|
2754
|
-
}
|
|
2755
|
-
}
|
|
2756
|
-
|
|
2757
|
-
window.addEventListener('message', handleMessage)
|
|
2758
|
-
return () => { window.removeEventListener('message', handleMessage) }
|
|
2759
|
-
}, [field?.id, field?.formId, field?.title, onChange, acknowledgedWarning, value, loaded?.entropy])
|
|
2760
|
-
|
|
2761
|
-
if (value) {
|
|
2762
|
-
return (
|
|
2763
|
-
<Grid container direction="column" spacing={1}>
|
|
2764
|
-
<Grid item>
|
|
2765
|
-
<Grid container alignItems="center" wrap="nowrap">
|
|
2766
|
-
<CheckCircleOutline color="success" />
|
|
2767
|
-
|
|
2768
|
-
<Typography sx={{ ml: 1, fontSize: 20 }}>
|
|
2769
|
-
Your appointment has been booked
|
|
2770
|
-
</Typography>
|
|
2771
|
-
</Grid>
|
|
2772
|
-
</Grid>
|
|
2773
|
-
|
|
2774
|
-
<Grid item sx={{ maxWidth: 250 }}>
|
|
2775
|
-
<LoadingButton variant="contained" style={{ maxWidth: 250 }}
|
|
2776
|
-
submitText="Add to Calendar" submittingText="Downloading..."
|
|
2777
|
-
onClick={() => downloadICS({ id: value })}
|
|
2778
|
-
/>
|
|
2779
|
-
</Grid>
|
|
2780
|
-
</Grid>
|
|
2781
|
-
)
|
|
2782
|
-
}
|
|
2783
|
-
if (!bookingPageId) {
|
|
2784
|
-
return <Typography>No booking page specified</Typography>
|
|
2785
|
-
}
|
|
2786
|
-
if (error) {
|
|
2787
|
-
return (
|
|
2788
|
-
<Grid container direction="column" spacing={1}>
|
|
2789
|
-
<Grid item>
|
|
2790
|
-
<Typography color="error">Error: {error}</Typography>
|
|
2791
|
-
</Grid>
|
|
2792
|
-
|
|
2793
|
-
<Grid item>
|
|
2794
|
-
<LoadingButton disabled={!bookingPageId} style={{ maxWidth: 300 }}
|
|
2795
|
-
variant="contained" onClick={loadBookingInfo}
|
|
2796
|
-
submitText="Try Again" submittingText="Loading..."
|
|
2797
|
-
/>
|
|
2798
|
-
</Grid>
|
|
2799
|
-
</Grid>
|
|
2800
|
-
)
|
|
2801
|
-
}
|
|
2802
|
-
if (!loaded?.bookingURL) {
|
|
2803
|
-
return <LinearProgress />
|
|
2804
|
-
}
|
|
2805
|
-
|
|
2806
|
-
let bookingURL = loaded.bookingURL
|
|
2807
|
-
if (field.options?.userTags?.length) {
|
|
2808
|
-
bookingURL += `&userTags=${
|
|
2809
|
-
field.options.userTags
|
|
2810
|
-
.flatMap(t => {
|
|
2811
|
-
// set dynamic tags if found
|
|
2812
|
-
if (t === '{{logic}}') {
|
|
2813
|
-
return new URL(window.location.href).searchParams.get('logic') || '{{logic}}'
|
|
2814
|
-
}
|
|
2815
|
-
if (t.startsWith("{{field.") && t.endsWith(".value}}")) {
|
|
2816
|
-
const fieldId = t.replace('{{field.', '').replace(".value}}", '')
|
|
2817
|
-
|
|
2818
|
-
const answer = responses?.find(r => r.fieldId === fieldId)?.answer
|
|
2819
|
-
if (!answer?.value) return t
|
|
2820
|
-
|
|
2821
|
-
if (answer.type === 'Insurance') {
|
|
2822
|
-
return answer.value.payerName || ''
|
|
2823
|
-
}
|
|
2824
|
-
if (Array.isArray(answer.value) && typeof answer.value?.[0] === 'string') {
|
|
2825
|
-
return answer.value as string[]
|
|
2826
|
-
}
|
|
2827
|
-
return form_response_value_to_string(answer.value)
|
|
2828
|
-
}
|
|
2829
|
-
return t
|
|
2830
|
-
})
|
|
2831
|
-
.join(',')
|
|
2832
|
-
}`
|
|
2833
|
-
}
|
|
2834
|
-
if (field.options?.userFilterTags?.length) {
|
|
2835
|
-
bookingURL += `&userFilterTags=${
|
|
2836
|
-
field.options.userFilterTags
|
|
2837
|
-
.flatMap(t => {
|
|
2838
|
-
// set dynamic tags if found
|
|
2839
|
-
if (t === '{{logic}}') {
|
|
2840
|
-
return new URL(window.location.href).searchParams.get('logic') || '{{logic}}'
|
|
2841
|
-
}
|
|
2842
|
-
if (t.startsWith("{{field.") && t.endsWith(".value}}")) {
|
|
2843
|
-
const fieldId = t.replace('{{field.', '').replace(".value}}", '')
|
|
2844
|
-
|
|
2845
|
-
const answer = responses?.find(r => r.fieldId === fieldId)?.answer
|
|
2846
|
-
if (!answer?.value) return t
|
|
2847
|
-
|
|
2848
|
-
if (answer.type === 'Insurance') {
|
|
2849
|
-
return answer.value.payerName || ''
|
|
2850
|
-
}
|
|
2851
|
-
if (Array.isArray(answer.value) && typeof answer.value?.[0] === 'string') {
|
|
2852
|
-
return answer.value as string[]
|
|
2853
|
-
}
|
|
2854
|
-
return form_response_value_to_string(answer.value)
|
|
2855
|
-
}
|
|
2856
|
-
return t
|
|
2857
|
-
})
|
|
2858
|
-
.join(',')
|
|
2859
|
-
}`
|
|
2860
|
-
}
|
|
2861
|
-
// need to use form?.id for internally-submitted forms because formResponseId isn't generated until initial submission or saved draft
|
|
2862
|
-
if (field.options?.holdAppointmentMinutes && (formResponseId || field?.id)) {
|
|
2863
|
-
bookingURL += `&formResponseId=${formResponseId || field?.id}`
|
|
2864
|
-
bookingURL += `&holdAppointmentMinutes=${field.options.holdAppointmentMinutes}`
|
|
2865
|
-
}
|
|
2866
|
-
|
|
2867
|
-
return (
|
|
2868
|
-
<Grid container direction="column" spacing={1} sx={{ mt: 1 }}>
|
|
2869
|
-
{/* When skipping user selection, include a back button at the top for clearer navigation on mobile */}
|
|
2870
|
-
{!!field.options?.userFilterTags?.length && !field.options.userTags?.length && !isPreviousDisabled?.() && !confirming &&
|
|
2871
|
-
<Grid item alignSelf="flex-start" >
|
|
2872
|
-
<Button variant="outlined" onClick={goToPreviousField} sx={{ height: 25, p: 0.5, px: 1 }}>
|
|
2873
|
-
Back
|
|
2874
|
-
</Button>
|
|
2875
|
-
</Grid>
|
|
2876
|
-
}
|
|
2877
|
-
|
|
2878
|
-
{loaded.warningMessage &&
|
|
2879
|
-
<Grid item>
|
|
2880
|
-
<Typography color="error" sx={{ fontSize: 20, fontWeight: 'bold' }}>
|
|
2881
|
-
{loaded.warningMessage}
|
|
2882
|
-
</Typography>
|
|
2883
|
-
</Grid>
|
|
2884
|
-
}
|
|
2885
|
-
|
|
2886
|
-
<Grid item>
|
|
2887
|
-
{(!loaded.warningMessage || acknowledgedWarning)
|
|
2888
|
-
? (
|
|
2889
|
-
<iframe title="Appointment Booking Embed"
|
|
2890
|
-
src={bookingURL}
|
|
2891
|
-
style={{ border: 'none', width: '100%', height }}
|
|
2892
|
-
/>
|
|
2893
|
-
)
|
|
2894
|
-
: (
|
|
2895
|
-
<Button variant="outlined" onClick={() => setAcknowledgedWarning(true)}>
|
|
2896
|
-
Show Booking Page Preview
|
|
2897
|
-
</Button>
|
|
2898
|
-
)
|
|
2899
|
-
}
|
|
2900
|
-
</Grid>
|
|
2901
|
-
</Grid>
|
|
2902
|
-
)
|
|
2319
|
+
// AppointmentBookingInput logic is shared with inputs.tsx to avoid duplication
|
|
2320
|
+
export const AppointmentBookingInput = (props: FormInputProps<'Appointment Booking'>) => {
|
|
2321
|
+
return <SharedAppointmentBookingInput {...props} />
|
|
2903
2322
|
}
|
|
2904
2323
|
|
|
2905
2324
|
export const HeightInput = ({ field, value={} as any, onChange, ...props }: FormInputProps<'Height'>) => (
|
package/src/Forms/types.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { AnswerForType, FormResponseValue } from "@tellescope/types-models";
|
|
|
2
2
|
import { DatabaseRecord, Enduser, Form, FormField } from "@tellescope/types-client";
|
|
3
3
|
import { FileBlob, TreeNode } from "@tellescope/types-utilities";
|
|
4
4
|
import { JSXElementConstructor } from "react";
|
|
5
|
+
import { SxProps } from "@mui/material";
|
|
5
6
|
import { Response } from "./hooks";
|
|
6
7
|
|
|
7
8
|
export type FormFieldNode = TreeNode<FormField>
|
|
@@ -23,8 +24,8 @@ export interface FormInputProps<K extends keyof AnswerForType> {
|
|
|
23
24
|
enduserId?: string,
|
|
24
25
|
enduser?: Partial<Enduser>
|
|
25
26
|
error?: boolean,
|
|
26
|
-
goToPreviousField?: () => void,
|
|
27
|
-
goToNextField?: (response?: FormResponseValue['answer']) => void,
|
|
27
|
+
goToPreviousField?: () => void,
|
|
28
|
+
goToNextField?: (response?: FormResponseValue['answer']) => void,
|
|
28
29
|
isPreviousDisabled?: () => boolean,
|
|
29
30
|
formResponseId?: string,
|
|
30
31
|
rootResponseId?: string,
|
|
@@ -37,6 +38,7 @@ export interface FormInputProps<K extends keyof AnswerForType> {
|
|
|
37
38
|
uploadingFiles?: { fieldId: string }[]
|
|
38
39
|
setUploadingFiles?: React.Dispatch<React.SetStateAction<{ fieldId: string }[]>>,
|
|
39
40
|
groupFields?: FormField[],
|
|
41
|
+
inputProps?: { sx: SxProps },
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
export type FormInputs = {
|