@tellescope/react-components 1.153.0 → 1.154.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/lib/cjs/Forms/forms.d.ts.map +1 -1
- package/lib/cjs/Forms/forms.js +53 -44
- package/lib/cjs/Forms/forms.js.map +1 -1
- package/lib/cjs/Forms/hooks.d.ts +1 -1
- package/lib/cjs/Forms/hooks.d.ts.map +1 -1
- package/lib/cjs/Forms/hooks.js.map +1 -1
- package/lib/cjs/Forms/inputs.d.ts +1 -0
- package/lib/cjs/Forms/inputs.d.ts.map +1 -1
- package/lib/cjs/Forms/inputs.js +140 -31
- package/lib/cjs/Forms/inputs.js.map +1 -1
- package/lib/cjs/Forms/types.d.ts +1 -1
- package/lib/cjs/Forms/types.d.ts.map +1 -1
- package/lib/cjs/inputs_shared.d.ts +7 -1
- package/lib/cjs/inputs_shared.d.ts.map +1 -1
- package/lib/cjs/inputs_shared.js +13 -1
- package/lib/cjs/inputs_shared.js.map +1 -1
- package/lib/cjs/state.d.ts +72 -0
- package/lib/cjs/state.d.ts.map +1 -1
- package/lib/cjs/state.js +35 -3
- package/lib/cjs/state.js.map +1 -1
- package/lib/esm/Forms/forms.d.ts +3 -3
- package/lib/esm/Forms/forms.d.ts.map +1 -1
- package/lib/esm/Forms/forms.js +55 -46
- package/lib/esm/Forms/forms.js.map +1 -1
- package/lib/esm/Forms/hooks.d.ts +2 -1
- package/lib/esm/Forms/hooks.d.ts.map +1 -1
- package/lib/esm/Forms/hooks.js.map +1 -1
- package/lib/esm/Forms/inputs.d.ts +2 -1
- package/lib/esm/Forms/inputs.d.ts.map +1 -1
- package/lib/esm/Forms/inputs.js +138 -30
- package/lib/esm/Forms/inputs.js.map +1 -1
- package/lib/esm/Forms/inputs.native.d.ts +0 -1
- package/lib/esm/Forms/inputs.native.d.ts.map +1 -1
- package/lib/esm/Forms/types.d.ts +1 -1
- package/lib/esm/Forms/types.d.ts.map +1 -1
- package/lib/esm/controls.d.ts +2 -2
- package/lib/esm/inputs.d.ts +1 -1
- package/lib/esm/inputs_shared.d.ts +7 -1
- package/lib/esm/inputs_shared.d.ts.map +1 -1
- package/lib/esm/inputs_shared.js +11 -1
- package/lib/esm/inputs_shared.js.map +1 -1
- package/lib/esm/state.d.ts +324 -252
- package/lib/esm/state.d.ts.map +1 -1
- package/lib/esm/state.js +30 -0
- package/lib/esm/state.js.map +1 -1
- package/lib/esm/theme.native.d.ts +0 -1
- package/lib/esm/theme.native.d.ts.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/src/Forms/forms.tsx +14 -3
- package/src/Forms/hooks.tsx +1 -1
- package/src/Forms/inputs.tsx +161 -27
- package/src/Forms/types.ts +1 -1
- package/src/inputs_shared.tsx +24 -2
- package/src/state.tsx +43 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tellescope/react-components",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.154.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.
|
|
51
|
-
"@tellescope/sdk": "^1.
|
|
52
|
-
"@tellescope/types-client": "^1.
|
|
53
|
-
"@tellescope/types-models": "^1.
|
|
54
|
-
"@tellescope/types-utilities": "^1.
|
|
55
|
-
"@tellescope/utilities": "^1.
|
|
56
|
-
"@tellescope/validation": "^1.
|
|
50
|
+
"@tellescope/constants": "^1.154.0",
|
|
51
|
+
"@tellescope/sdk": "^1.154.0",
|
|
52
|
+
"@tellescope/types-client": "^1.154.0",
|
|
53
|
+
"@tellescope/types-models": "^1.154.0",
|
|
54
|
+
"@tellescope/types-utilities": "^1.154.0",
|
|
55
|
+
"@tellescope/utilities": "^1.154.0",
|
|
56
|
+
"@tellescope/validation": "^1.154.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",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
|
81
81
|
"react-native": "^0.65.0 || ^0.66.0 || ^0.67.0 || ^0.68.0 || ^0.71.0"
|
|
82
82
|
},
|
|
83
|
-
"gitHead": "
|
|
83
|
+
"gitHead": "d96f27c5787398546a644b88fe6ad75208836bad",
|
|
84
84
|
"publishConfig": {
|
|
85
85
|
"access": "public"
|
|
86
86
|
}
|
package/src/Forms/forms.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useMemo, useState } from "react"
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
|
2
2
|
import { Button, CircularProgress, Flex, LoadingButton, Modal, Paper, Styled, Typography, form_display_text_for_language, useFileUpload, useFormResponses, useSession } from "../index"
|
|
3
3
|
import { useListForFormFields, useOrganizationTheme, useTellescopeForm, WithOrganizationTheme, Response, FileResponse } from "./hooks"
|
|
4
4
|
import { ChangeHandler, FormInputs } from "./types"
|
|
5
|
-
import { AddressInput, AllergiesInput, AppointmentBookingInput, DatabaseSelectInput, DateInput, DateStringInput, DropdownInput, EmailInput, EmotiiInput, FileInput, FilesInput, HeightInput, HiddenValueInput, InsuranceInput, LanguageSelect, MedicationsInput, MultipleChoiceInput, NumberInput, PhoneInput, Progress, RankingInput, RatingInput, RedirectInput, RelatedContactsInput, SignatureInput, StringInput, StringLongInput, StripeInput, TableInput, TimeInput, defaultButtonStyles } from "./inputs"
|
|
5
|
+
import { AddressInput, AllergiesInput, AppointmentBookingInput, ConditionsInput, DatabaseSelectInput, DateInput, DateStringInput, DropdownInput, EmailInput, EmotiiInput, FileInput, FilesInput, HeightInput, HiddenValueInput, InsuranceInput, LanguageSelect, MedicationsInput, MultipleChoiceInput, NumberInput, PhoneInput, Progress, RankingInput, RatingInput, RedirectInput, RelatedContactsInput, SignatureInput, StringInput, StringLongInput, StripeInput, TableInput, TimeInput, defaultButtonStyles } from "./inputs"
|
|
6
6
|
import { PRIMARY_HEX } from "@tellescope/constants"
|
|
7
7
|
import { FormResponse, FormField, Form, Enduser } from "@tellescope/types-client"
|
|
8
8
|
import { FormResponseAnswerFileValue, OrganizationTheme } from "@tellescope/types-models"
|
|
@@ -169,6 +169,7 @@ export const QuestionForField = ({
|
|
|
169
169
|
const HiddenValue = customInputs?.['Hidden Value'] ?? HiddenValueInput
|
|
170
170
|
const Emotii = customInputs?.['Emotii'] ?? EmotiiInput
|
|
171
171
|
const Allergies = customInputs?.['Allergies'] ?? AllergiesInput
|
|
172
|
+
const Conditions = customInputs?.['Conditions'] ?? ConditionsInput
|
|
172
173
|
|
|
173
174
|
const validationMessage = validateField(field)
|
|
174
175
|
|
|
@@ -246,6 +247,9 @@ export const QuestionForField = ({
|
|
|
246
247
|
: field.type === 'Allergies' ? (
|
|
247
248
|
<Allergies enduser={enduser} enduserId={enduserId} field={field} disabled={value.disabled} value={value.answer.value as any} onChange={onFieldChange as ChangeHandler<any>} form={form} />
|
|
248
249
|
)
|
|
250
|
+
: field.type === 'Conditions' ? (
|
|
251
|
+
<Conditions enduser={enduser} enduserId={enduserId} field={field} disabled={value.disabled} value={value.answer.value as any} onChange={onFieldChange as ChangeHandler<any>} form={form} />
|
|
252
|
+
)
|
|
249
253
|
: field.type === 'Height' ? (
|
|
250
254
|
<Height field={field} disabled={value.disabled} value={value.answer.value as any} onChange={onFieldChange as ChangeHandler<any>} form={form} />
|
|
251
255
|
)
|
|
@@ -435,6 +439,7 @@ export const TellescopeSingleQuestionFlow: typeof TellescopeForm = ({
|
|
|
435
439
|
}, [])
|
|
436
440
|
|
|
437
441
|
const [uploading, setUploading] = useState(false)
|
|
442
|
+
const [autosubmitting, setAutoSubmitting] = useState(false)
|
|
438
443
|
|
|
439
444
|
useEffect(() => {
|
|
440
445
|
// ensure redirect question doesn't trip this alert
|
|
@@ -449,6 +454,7 @@ export const TellescopeSingleQuestionFlow: typeof TellescopeForm = ({
|
|
|
449
454
|
return onSuccess?.({} as any)
|
|
450
455
|
}
|
|
451
456
|
|
|
457
|
+
|
|
452
458
|
await submit({
|
|
453
459
|
onSuccess,
|
|
454
460
|
...options,
|
|
@@ -461,13 +467,18 @@ export const TellescopeSingleQuestionFlow: typeof TellescopeForm = ({
|
|
|
461
467
|
})
|
|
462
468
|
}, [isPreview, onSuccess, submit, beforeunloadHandler])
|
|
463
469
|
|
|
470
|
+
const autoSubmitRef = useRef(false)
|
|
464
471
|
useEffect(() => {
|
|
465
472
|
if (!activeField.value.options?.autoSubmit) {
|
|
466
473
|
return
|
|
467
474
|
}
|
|
475
|
+
if (autoSubmitRef.current) return
|
|
468
476
|
|
|
469
477
|
if (responses.find(r => r.fieldId === activeField.value.id && field_can_autosubmit(activeField.value) && r.answer.value)) {
|
|
478
|
+
autoSubmitRef.current = true
|
|
479
|
+
setAutoSubmitting(true)
|
|
470
480
|
handleSubmit()
|
|
481
|
+
.finally(() => setAutoSubmitting(false))
|
|
471
482
|
}
|
|
472
483
|
}, [handleSubmit, responses, activeField])
|
|
473
484
|
|
|
@@ -551,7 +562,7 @@ export const TellescopeSingleQuestionFlow: typeof TellescopeForm = ({
|
|
|
551
562
|
setUploading(!!selectedFiles.find(r => !!r.blobs?.length))
|
|
552
563
|
return handleSubmit({ onFileUploadsDone: () => setUploading(false) })
|
|
553
564
|
}}
|
|
554
|
-
disabled={!!validationMessage || currentValue.field?.options?.disableNext === true}
|
|
565
|
+
disabled={!!validationMessage || currentValue.field?.options?.disableNext === true || autosubmitting}
|
|
555
566
|
submitText={form_display_text_for_language(form, "Submit")}
|
|
556
567
|
submittingText={
|
|
557
568
|
submittingStatus === 'uploading-files'
|
package/src/Forms/hooks.tsx
CHANGED
|
@@ -668,7 +668,7 @@ export const useTellescopeForm = ({ form, urlLogicValue, customization, carePlan
|
|
|
668
668
|
selectedFiles.find(f => f.fieldId === activeField.value.id)
|
|
669
669
|
)
|
|
670
670
|
|
|
671
|
-
const handleDatabaseSelect = useCallback((databaseRecords: DatabaseRecord[]) => {
|
|
671
|
+
const handleDatabaseSelect = useCallback((databaseRecords: Pick<DatabaseRecord, "values" | "databaseId">[]) => {
|
|
672
672
|
try {
|
|
673
673
|
// no need to update if there's no prepopulation
|
|
674
674
|
if (!fields?.find(f => f.prepopulateFromDatabase?.databaseId && f.prepopulateFromDatabase?.field)) return
|
package/src/Forms/inputs.tsx
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from "react"
|
|
2
2
|
import axios from "axios"
|
|
3
|
-
import { Autocomplete, Box, Button, Checkbox, Divider, FormControl, FormControlLabel, FormLabel, Grid, InputLabel, MenuItem, Radio, RadioGroup, Select, SxProps, TextField, TextFieldProps, Typography } from "@mui/material"
|
|
3
|
+
import { Autocomplete, Box, Button, Checkbox, Chip, Divider, FormControl, FormControlLabel, FormLabel, Grid, InputLabel, MenuItem, Radio, RadioGroup, Select, SxProps, TextField, TextFieldProps, Typography } from "@mui/material"
|
|
4
4
|
import { FormInputProps } from "./types"
|
|
5
5
|
import { useDropzone } from "react-dropzone"
|
|
6
6
|
import { CANVAS_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, first_letter_capitalized, form_response_value_to_string, getLocalTimezone, getPublicFileURL, mm_dd_yyyy, replace_enduser_template_values, truncate_string, user_display_name } from "@tellescope/utilities"
|
|
8
|
-
import { Enduser, EnduserRelationship, FormResponseValue, InsuranceRelationship, MultipleChoiceOptions, TellescopeGender } from "@tellescope/types-models"
|
|
8
|
+
import { DatabaseSelectResponse, Enduser, EnduserRelationship, FormResponseValue, InsuranceRelationship, MultipleChoiceOptions, TellescopeGender } from "@tellescope/types-models"
|
|
9
9
|
import { VALID_STATES, emailValidator, phoneValidator } from "@tellescope/validation"
|
|
10
10
|
import Slider from '@mui/material/Slider';
|
|
11
11
|
import LinearProgress from '@mui/material/LinearProgress';
|
|
@@ -1447,6 +1447,7 @@ export const StripeInput = ({ field, value, onChange, setCustomerId }: FormInput
|
|
|
1447
1447
|
const [isCheckout, setIsCheckout] = useState(false)
|
|
1448
1448
|
const [stripePromise, setStripePromise] = useState<ReturnType<typeof loadStripe>>()
|
|
1449
1449
|
const [, { findById: findProduct }] = useProducts({ dontFetch: true })
|
|
1450
|
+
const [answertext, setAnswertext] = useState('')
|
|
1450
1451
|
|
|
1451
1452
|
const fetchRef = useRef(false)
|
|
1452
1453
|
useEffect(() => {
|
|
@@ -1457,12 +1458,13 @@ export const StripeInput = ({ field, value, onChange, setCustomerId }: FormInput
|
|
|
1457
1458
|
fetchRef.current = true
|
|
1458
1459
|
|
|
1459
1460
|
session.api.form_responses.stripe_details({ fieldId: field.id })
|
|
1460
|
-
.then(({ clientSecret, publishableKey, stripeAccount, businessName, customerId, isCheckout }) => {
|
|
1461
|
+
.then(({ clientSecret, publishableKey, stripeAccount, businessName, customerId, isCheckout, answerText }) => {
|
|
1461
1462
|
setIsCheckout(!!isCheckout)
|
|
1462
1463
|
setClientSecret(clientSecret)
|
|
1463
1464
|
setStripePromise(loadStripe(publishableKey, { stripeAccount }))
|
|
1464
1465
|
setBusinessName(businessName)
|
|
1465
1466
|
setCustomerId(customerId)
|
|
1467
|
+
setAnswertext(answerText || '')
|
|
1466
1468
|
})
|
|
1467
1469
|
.catch(console.error)
|
|
1468
1470
|
}, [session, value, field.id])
|
|
@@ -1488,7 +1490,7 @@ export const StripeInput = ({ field, value, onChange, setCustomerId }: FormInput
|
|
|
1488
1490
|
<EmbeddedCheckoutProvider stripe={stripePromise}
|
|
1489
1491
|
options={{
|
|
1490
1492
|
clientSecret,
|
|
1491
|
-
onComplete: () => onChange('Completed checkout', field.id),
|
|
1493
|
+
onComplete: () => onChange(answertext || 'Completed checkout', field.id),
|
|
1492
1494
|
}}
|
|
1493
1495
|
>
|
|
1494
1496
|
<EmbeddedCheckout />
|
|
@@ -1498,7 +1500,7 @@ export const StripeInput = ({ field, value, onChange, setCustomerId }: FormInput
|
|
|
1498
1500
|
<Elements stripe={stripePromise} options={{
|
|
1499
1501
|
clientSecret,
|
|
1500
1502
|
}}>
|
|
1501
|
-
<StripeForm businessName={businessName} onSuccess={() => onChange('Saved card details', field.id)}
|
|
1503
|
+
<StripeForm businessName={businessName} onSuccess={() => onChange(answertext || 'Saved card details', field.id)}
|
|
1502
1504
|
cost={cost}
|
|
1503
1505
|
field={field}
|
|
1504
1506
|
/>
|
|
@@ -1661,7 +1663,7 @@ const choicesForDatabase: {
|
|
|
1661
1663
|
} = {}
|
|
1662
1664
|
|
|
1663
1665
|
const LOAD_CHOICES_LIMIT = 500
|
|
1664
|
-
const useDatabaseChoices = ({ databaseId='', field } : { databaseId?: string, field: FormField }) => {
|
|
1666
|
+
const useDatabaseChoices = ({ databaseId='', field, otherAnswers } : { databaseId?: string, field: FormField, otherAnswers?: DatabaseSelectResponse[] }) => {
|
|
1665
1667
|
const session = useResolvedSession()
|
|
1666
1668
|
const [renderCount, setRenderCount] = useState(0)
|
|
1667
1669
|
|
|
@@ -1697,13 +1699,20 @@ const useDatabaseChoices = ({ databaseId='', field } : { databaseId?: string, fi
|
|
|
1697
1699
|
|
|
1698
1700
|
return {
|
|
1699
1701
|
doneLoading: choicesForDatabase[databaseId]?.done ?? false,
|
|
1700
|
-
choices:
|
|
1702
|
+
choices: [
|
|
1703
|
+
...choicesForDatabase[databaseId]?.records ?? [],
|
|
1704
|
+
...(otherAnswers || []).map(v => ({
|
|
1705
|
+
id: v.text,
|
|
1706
|
+
databaseId,
|
|
1707
|
+
values: [{ label: field.options?.databaseLabel || '', type: 'Text', value: v.text }],
|
|
1708
|
+
}) as Pick<DatabaseRecord, 'id' | 'values' | 'databaseId'>)
|
|
1709
|
+
],
|
|
1701
1710
|
renderCount,
|
|
1702
1711
|
}
|
|
1703
1712
|
}
|
|
1704
1713
|
|
|
1705
1714
|
|
|
1706
|
-
const label_for_database_record = (field: FormField, record?: DatabaseRecord) => {
|
|
1715
|
+
const label_for_database_record = (field: FormField, record?: Pick<DatabaseRecord, 'values'>) => {
|
|
1707
1716
|
if (!record) return ''
|
|
1708
1717
|
|
|
1709
1718
|
const addedLabels = (
|
|
@@ -1722,14 +1731,34 @@ const label_for_database_record = (field: FormField, record?: DatabaseRecord) =>
|
|
|
1722
1731
|
)
|
|
1723
1732
|
}
|
|
1724
1733
|
|
|
1734
|
+
const get_other_answers = (_value?: DatabaseSelectResponse[], typing?: string) => {
|
|
1735
|
+
try {
|
|
1736
|
+
const existing = (
|
|
1737
|
+
(_value || [])
|
|
1738
|
+
.filter(v => typeof v === 'string' || v.recordId === v.text)
|
|
1739
|
+
.map(v => typeof v === 'string' ? { databaseId: '', recordId: v, text: v } : v)
|
|
1740
|
+
)
|
|
1741
|
+
if (typing) {
|
|
1742
|
+
existing.push({ text: typing, databaseId: '', recordId: typing })
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
return existing
|
|
1746
|
+
} catch(err) { console.error(err) }
|
|
1747
|
+
|
|
1748
|
+
return []
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1725
1751
|
export const DatabaseSelectInput = ({ field, value: _value, onChange, onDatabaseSelect, responses }: FormInputProps<'Database Select'> & {
|
|
1726
1752
|
responses: FormResponseValue[],
|
|
1727
1753
|
}) => {
|
|
1754
|
+
const [typing, setTyping] = useState('')
|
|
1728
1755
|
const { choices, doneLoading } = useDatabaseChoices({
|
|
1729
1756
|
databaseId: field.options?.databaseId,
|
|
1730
1757
|
field,
|
|
1758
|
+
otherAnswers: get_other_answers(_value, field?.options?.other ? typing : undefined),
|
|
1731
1759
|
})
|
|
1732
1760
|
|
|
1761
|
+
console.log(choices, _value)
|
|
1733
1762
|
const value = React.useMemo(() => {
|
|
1734
1763
|
try {
|
|
1735
1764
|
// if the value is a string (some single answer that was save), make sure we coerce to array
|
|
@@ -1797,7 +1826,7 @@ export const DatabaseSelectInput = ({ field, value: _value, onChange, onDatabase
|
|
|
1797
1826
|
}, [choices, filterResponse, field.options?.databaseFilter, value])
|
|
1798
1827
|
|
|
1799
1828
|
const filteredChoices = useMemo(() => {
|
|
1800
|
-
const filtered
|
|
1829
|
+
const filtered = []
|
|
1801
1830
|
|
|
1802
1831
|
const uniques = new Set<string>([])
|
|
1803
1832
|
for (const c of filteredChoicesWithPotentialDuplicates) {
|
|
@@ -1814,6 +1843,7 @@ export const DatabaseSelectInput = ({ field, value: _value, onChange, onDatabase
|
|
|
1814
1843
|
if (!doneLoading) return <LinearProgress />
|
|
1815
1844
|
return (
|
|
1816
1845
|
<Autocomplete id={field.id} freeSolo={false}
|
|
1846
|
+
componentsProps={{ popper: { sx: { wordBreak: "break-word" } } } }
|
|
1817
1847
|
options={filteredChoices} multiple={true}
|
|
1818
1848
|
getOptionLabel={o => (
|
|
1819
1849
|
Array.isArray(o) // edge case
|
|
@@ -1842,7 +1872,19 @@ export const DatabaseSelectInput = ({ field, value: _value, onChange, onDatabase
|
|
|
1842
1872
|
field.id,
|
|
1843
1873
|
)
|
|
1844
1874
|
}}
|
|
1875
|
+
inputValue={typing}
|
|
1876
|
+
onInputChange={(e, v) => e && setTyping(v)}
|
|
1845
1877
|
renderInput={params => <TextField {...params} InputProps={{ ...params.InputProps, sx: defaultInputProps.sx }} />}
|
|
1878
|
+
// use custom Chip to ensure very long entries break properly (whitespace: normal)
|
|
1879
|
+
renderTags={(value, getTagProps) =>
|
|
1880
|
+
value.map((value, index) => (
|
|
1881
|
+
<Chip
|
|
1882
|
+
label={<Typography style={{whiteSpace: 'normal'}}>{Array.isArray(value) ? '' : label_for_database_record(field, value)}</Typography>}
|
|
1883
|
+
{...getTagProps({ index })}
|
|
1884
|
+
sx={{height:"100%", py: 0.5 }}
|
|
1885
|
+
/>
|
|
1886
|
+
))
|
|
1887
|
+
}
|
|
1846
1888
|
/>
|
|
1847
1889
|
)
|
|
1848
1890
|
}
|
|
@@ -2832,49 +2874,141 @@ export const AllergiesInput = ({ goToNextField, goToPreviousField, field, value,
|
|
|
2832
2874
|
const [query, setQuery] = useState('')
|
|
2833
2875
|
const [results, setResults] = useState<{ code: string, display: string }[]>([])
|
|
2834
2876
|
|
|
2877
|
+
// if two allergy questions shown in a row, reset state
|
|
2878
|
+
useEffect(() => {
|
|
2879
|
+
setQuery('')
|
|
2880
|
+
setResults([])
|
|
2881
|
+
}, [field.id])
|
|
2882
|
+
|
|
2835
2883
|
const fetchRef = useRef(query)
|
|
2836
2884
|
useEffect(() => {
|
|
2837
2885
|
if (fetchRef.current === query) return
|
|
2838
2886
|
fetchRef.current = query
|
|
2839
2887
|
|
|
2888
|
+
if (!query) return
|
|
2889
|
+
|
|
2840
2890
|
const t = setTimeout(() => {
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2891
|
+
if (field.options?.dataSource === CANVAS_TITLE) {
|
|
2892
|
+
session.api.integrations
|
|
2893
|
+
.proxy_read({
|
|
2894
|
+
integration: CANVAS_TITLE,
|
|
2895
|
+
type: 'allergies',
|
|
2896
|
+
query,
|
|
2897
|
+
})
|
|
2898
|
+
.then((r : { data: AllergyResult }) => {
|
|
2899
|
+
const deduped: typeof results = []
|
|
2900
|
+
const totalResults = (
|
|
2901
|
+
(r.data.entry || [])
|
|
2902
|
+
.flatMap(v => v?.resource?.code?.coding || [])
|
|
2903
|
+
.filter(v => v.system.includes('fdbhealth'))
|
|
2904
|
+
.map(v => ({ code: v.code, display: v.display, system: v.system }))
|
|
2905
|
+
)
|
|
2906
|
+
for (const v of totalResults) {
|
|
2907
|
+
if (deduped.find(d => d.display === v.display)) { continue }
|
|
2908
|
+
|
|
2909
|
+
deduped.push(v)
|
|
2910
|
+
}
|
|
2911
|
+
setResults(deduped)
|
|
2912
|
+
})
|
|
2913
|
+
} else {
|
|
2914
|
+
session.api.allergy_codes.getSome({ search: { query }})
|
|
2915
|
+
.then(results => {
|
|
2916
|
+
const deduped: typeof results = []
|
|
2917
|
+
for (const v of results) {
|
|
2918
|
+
if (deduped.find(d => d.display === v.display)) { continue }
|
|
2919
|
+
|
|
2920
|
+
deduped.push(v)
|
|
2921
|
+
}
|
|
2922
|
+
setResults(deduped)
|
|
2923
|
+
})
|
|
2924
|
+
}
|
|
2925
|
+
|
|
2926
|
+
|
|
2927
|
+
}, 200)
|
|
2928
|
+
|
|
2929
|
+
return () => { clearTimeout(t) }
|
|
2930
|
+
}, [session, query, field?.options?.dataSource])
|
|
2931
|
+
|
|
2932
|
+
return (
|
|
2933
|
+
<Autocomplete multiple value={value || []} options={results} style={{ marginTop: 5 }}
|
|
2934
|
+
noOptionsText={query.length ? 'No results found' : 'Type to start search'}
|
|
2935
|
+
onChange={(e, v) => {
|
|
2936
|
+
if (!v) { return }
|
|
2937
|
+
onChange(v, field.id)
|
|
2938
|
+
setResults([])
|
|
2939
|
+
}}
|
|
2940
|
+
getOptionLabel={v => first_letter_capitalized(v.display)} filterOptions={o => o}
|
|
2941
|
+
inputValue={query} onInputChange={(e, v) => e && setQuery(v) }
|
|
2942
|
+
renderInput={(params) => (
|
|
2943
|
+
<TextField {...params} InputProps={{ ...params.InputProps, sx: defaultInputProps.sx }}
|
|
2944
|
+
required={!field.isOptional} size="small" label="" placeholder="Search allergies..."
|
|
2945
|
+
/>
|
|
2946
|
+
)}
|
|
2947
|
+
renderTags={(value, getTagProps) =>
|
|
2948
|
+
value.map((value, index) => (
|
|
2949
|
+
<Chip
|
|
2950
|
+
label={<Typography style={{whiteSpace: 'normal'}}>{value.display}</Typography>}
|
|
2951
|
+
{...getTagProps({ index })}
|
|
2952
|
+
sx={{height:"100%", py: 0.5 }}
|
|
2953
|
+
/>
|
|
2954
|
+
))
|
|
2955
|
+
}
|
|
2956
|
+
/>
|
|
2957
|
+
)
|
|
2958
|
+
}
|
|
2959
|
+
|
|
2960
|
+
export const ConditionsInput = ({ goToNextField, goToPreviousField, field, value, onChange, form, formResponseId, ...props }: FormInputProps<'Conditions'>) => {
|
|
2961
|
+
const session = useResolvedSession()
|
|
2962
|
+
const [query, setQuery] = useState('')
|
|
2963
|
+
const [results, setResults] = useState<{ code: string, display: string }[]>([])
|
|
2964
|
+
|
|
2965
|
+
const fetchRef = useRef(query)
|
|
2966
|
+
useEffect(() => {
|
|
2967
|
+
if (fetchRef.current === query) return
|
|
2968
|
+
fetchRef.current = query
|
|
2969
|
+
|
|
2970
|
+
if (!query) return
|
|
2971
|
+
|
|
2972
|
+
const t = setTimeout(() => {
|
|
2973
|
+
session.api.diagnosis_codes.getSome({ search: { query } })
|
|
2974
|
+
.then(codes => {
|
|
2848
2975
|
const deduped: typeof results = []
|
|
2849
|
-
const
|
|
2850
|
-
(r.data.entry || [])
|
|
2851
|
-
.flatMap(v => v?.resource?.code?.coding || [])
|
|
2852
|
-
.filter(v => v.system.includes('fdbhealth'))
|
|
2853
|
-
.map(v => ({ code: v.code, display: v.display, system: v.system }))
|
|
2854
|
-
)
|
|
2855
|
-
for (const v of totalResults) {
|
|
2976
|
+
for (const v of codes) {
|
|
2856
2977
|
if (deduped.find(d => d.display === v.display)) { continue }
|
|
2857
2978
|
|
|
2858
2979
|
deduped.push(v)
|
|
2859
2980
|
}
|
|
2860
2981
|
setResults(deduped)
|
|
2861
2982
|
})
|
|
2862
|
-
},
|
|
2983
|
+
}, 200)
|
|
2863
2984
|
|
|
2864
2985
|
return () => { clearTimeout(t) }
|
|
2865
2986
|
}, [session, query])
|
|
2866
2987
|
|
|
2867
2988
|
return (
|
|
2868
|
-
<Autocomplete multiple value={value || []} options={
|
|
2989
|
+
<Autocomplete multiple value={value || []} options={results} style={{ marginTop: 5 }}
|
|
2869
2990
|
noOptionsText={query.length ? 'No results found' : 'Type to start search'}
|
|
2870
|
-
onChange={(e, v) =>
|
|
2991
|
+
onChange={(e, v) => {
|
|
2992
|
+
if (!v) { return }
|
|
2993
|
+
onChange(v, field.id)
|
|
2994
|
+
setResults([])
|
|
2995
|
+
}}
|
|
2871
2996
|
getOptionLabel={v => first_letter_capitalized(v.display)} filterOptions={o => o}
|
|
2872
2997
|
inputValue={query} onInputChange={(e, v) => e && setQuery(v) }
|
|
2873
2998
|
renderInput={(params) => (
|
|
2874
2999
|
<TextField {...params} InputProps={{ ...params.InputProps, sx: defaultInputProps.sx }}
|
|
2875
|
-
required={!field.isOptional} size="small" label="" placeholder="Search
|
|
3000
|
+
required={!field.isOptional} size="small" label="" placeholder="Search conditions..."
|
|
2876
3001
|
/>
|
|
2877
3002
|
)}
|
|
3003
|
+
renderTags={(value, getTagProps) =>
|
|
3004
|
+
value.map((value, index) => (
|
|
3005
|
+
<Chip
|
|
3006
|
+
label={<Typography style={{whiteSpace: 'normal'}}>{value.display}</Typography>}
|
|
3007
|
+
{...getTagProps({ index })}
|
|
3008
|
+
sx={{height:"100%", py: 0.5 }}
|
|
3009
|
+
/>
|
|
3010
|
+
))
|
|
3011
|
+
}
|
|
2878
3012
|
/>
|
|
2879
3013
|
)
|
|
2880
3014
|
}
|
package/src/Forms/types.ts
CHANGED
|
@@ -17,7 +17,7 @@ export interface FormInputProps<K extends keyof AnswerForType> {
|
|
|
17
17
|
size?: 'small',
|
|
18
18
|
label?: string,
|
|
19
19
|
fullWidth?: boolean,
|
|
20
|
-
onDatabaseSelect?: (r: DatabaseRecord[]) => void,
|
|
20
|
+
onDatabaseSelect?: (r: Pick<DatabaseRecord, "values" | "databaseId">[]) => void,
|
|
21
21
|
responses?: Response[]
|
|
22
22
|
title?: string,
|
|
23
23
|
enduserId?: string,
|
package/src/inputs_shared.tsx
CHANGED
|
@@ -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 { AppointmentBookingPage, AppointmentLocation, AutomationTrigger, CalendarEventTemplate, CallHoldQueue, ChatRoom, Database, DatabaseRecord, 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, useAppointmentBookingPages, useAppointmentLocations, useAutomationTriggers, useCalendarEventTemplates, useCallHoldQueues, useChatRooms, useDatabaseRecords, useDatabases, 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 { 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, 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 "."
|
|
10
10
|
import { SxProps } from "@mui/material"
|
|
11
11
|
import { AccessPermissions } from "@tellescope/types-models"
|
|
12
12
|
|
|
@@ -618,6 +618,28 @@ export const SuggestedContactSearch = (props: Omit<GenericSearchProps<SuggestedC
|
|
|
618
618
|
)
|
|
619
619
|
}
|
|
620
620
|
|
|
621
|
+
export const AllergyCodeSearch = (props: Omit<GenericSearchProps<AllergyCode>, 'filterKey'> & { filterKey?: string }) => {
|
|
622
|
+
const session = useSession()
|
|
623
|
+
const [, { addLocalElements }] = useAllergyCodes({ dontFetch: true })
|
|
624
|
+
return (
|
|
625
|
+
<ModelSearchInput filterKey="allergy-code" {...props}
|
|
626
|
+
searchAPI={session.api.allergy_codes.getSome}
|
|
627
|
+
onLoad={addLocalElements}
|
|
628
|
+
/>
|
|
629
|
+
)
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
export const DiagnosisCodeSearch = (props: Omit<GenericSearchProps<DiagnosisCode>, 'filterKey'> & { filterKey?: string }) => {
|
|
633
|
+
const session = useSession()
|
|
634
|
+
const [, { addLocalElements }] = useDiagnosisCodes({ dontFetch: true })
|
|
635
|
+
return (
|
|
636
|
+
<ModelSearchInput filterKey="diagnoses-code" {...props}
|
|
637
|
+
searchAPI={session.api.diagnosis_codes.getSome}
|
|
638
|
+
onLoad={addLocalElements}
|
|
639
|
+
/>
|
|
640
|
+
)
|
|
641
|
+
}
|
|
642
|
+
|
|
621
643
|
export const CallHoldQueueSearch = (props: Omit<GenericSearchProps<CallHoldQueue>, 'filterKey'> & { filterKey?: string }) => {
|
|
622
644
|
const session = useSession()
|
|
623
645
|
const [, { addLocalElements }] = useCallHoldQueues({ dontFetch: true })
|
package/src/state.tsx
CHANGED
|
@@ -93,6 +93,8 @@ import {
|
|
|
93
93
|
FaxLog,
|
|
94
94
|
CallHoldQueue,
|
|
95
95
|
SuggestedContact,
|
|
96
|
+
DiagnosisCode,
|
|
97
|
+
AllergyCode,
|
|
96
98
|
} from "@tellescope/types-client"
|
|
97
99
|
|
|
98
100
|
import {
|
|
@@ -107,7 +109,7 @@ import {
|
|
|
107
109
|
} from '@tellescope/sdk'
|
|
108
110
|
import { value_is_loaded } from './loading'
|
|
109
111
|
import { matches_organization, object_is_empty, objects_equivalent } from '@tellescope/utilities'
|
|
110
|
-
import { ModelName, ReadFilter, SortBy } from '@tellescope/types-models'
|
|
112
|
+
import { Diagnosis, ModelName, ReadFilter, SortBy } from '@tellescope/types-models'
|
|
111
113
|
|
|
112
114
|
const RESET_CACHE_TYPE = "cache/reset" as const
|
|
113
115
|
export const resetStateAction = createAction(RESET_CACHE_TYPE)
|
|
@@ -353,6 +355,8 @@ const portalBrandingsSlice = createSliceForList<PortalBranding, 'portal_branding
|
|
|
353
355
|
const messageTemplateSnippetsSlice = createSliceForList<MessageTemplateSnippet, 'message_template_snippets'>('message_template_snippets')
|
|
354
356
|
const faxLogsSlice = createSliceForList<FaxLog, 'fax_logs'>('fax_logs')
|
|
355
357
|
const suggestedContactsSlice = createSliceForList<SuggestedContact, 'suggested_contacts'>('suggested_contacts')
|
|
358
|
+
const diagnosisCodesSlice = createSliceForList<DiagnosisCode, 'diagnosis_codes'>('diagnosis_codes')
|
|
359
|
+
const allergyCodesSlice = createSliceForList<AllergyCode, 'allergy_codes'>('allergy_codes')
|
|
356
360
|
|
|
357
361
|
const roleBasedAccessPermissionsSlice = createSliceForList<RoleBasedAccessPermission, 'role_based_access_permissions'>('role_based_access_permissions')
|
|
358
362
|
|
|
@@ -440,6 +444,8 @@ export const sharedConfig = {
|
|
|
440
444
|
fax_logs: faxLogsSlice.reducer,
|
|
441
445
|
call_hold_queues: callHoldQueuesSlice.reducer,
|
|
442
446
|
suggested_contacts: suggestedContactsSlice.reducer,
|
|
447
|
+
diagnosis_codes: diagnosisCodesSlice.reducer,
|
|
448
|
+
allergy_codes: allergyCodesSlice.reducer,
|
|
443
449
|
},
|
|
444
450
|
}
|
|
445
451
|
|
|
@@ -1256,6 +1262,42 @@ export const usePortalBrandings = (options={} as HookOptions<PortalBranding>) =>
|
|
|
1256
1262
|
},
|
|
1257
1263
|
)
|
|
1258
1264
|
}
|
|
1265
|
+
export const useAllergyCodes = (options={} as HookOptions<AllergyCode>) => {
|
|
1266
|
+
const session = useResolvedSession()
|
|
1267
|
+
|
|
1268
|
+
return useListStateHook('allergy_codes', useTypedSelector(s => s.allergy_codes), session, allergyCodesSlice,
|
|
1269
|
+
{
|
|
1270
|
+
loadQuery: session.api.allergy_codes.getSome,
|
|
1271
|
+
findOne: session.api.allergy_codes.getOne,
|
|
1272
|
+
findByIds: session.api.allergy_codes.getByIds,
|
|
1273
|
+
addOne: session.api.allergy_codes.createOne,
|
|
1274
|
+
addSome: session.api.allergy_codes.createSome,
|
|
1275
|
+
deleteOne: session.api.allergy_codes.deleteOne,
|
|
1276
|
+
updateOne: session.api.allergy_codes.updateOne,
|
|
1277
|
+
},
|
|
1278
|
+
{
|
|
1279
|
+
...options,
|
|
1280
|
+
},
|
|
1281
|
+
)
|
|
1282
|
+
}
|
|
1283
|
+
export const useDiagnosisCodes = (options={} as HookOptions<DiagnosisCode>) => {
|
|
1284
|
+
const session = useResolvedSession()
|
|
1285
|
+
|
|
1286
|
+
return useListStateHook('diagnosis_codes', useTypedSelector(s => s.diagnosis_codes), session, diagnosisCodesSlice,
|
|
1287
|
+
{
|
|
1288
|
+
loadQuery: session.api.diagnosis_codes.getSome,
|
|
1289
|
+
findOne: session.api.diagnosis_codes.getOne,
|
|
1290
|
+
findByIds: session.api.diagnosis_codes.getByIds,
|
|
1291
|
+
addOne: session.api.diagnosis_codes.createOne,
|
|
1292
|
+
addSome: session.api.diagnosis_codes.createSome,
|
|
1293
|
+
deleteOne: session.api.diagnosis_codes.deleteOne,
|
|
1294
|
+
updateOne: session.api.diagnosis_codes.updateOne,
|
|
1295
|
+
},
|
|
1296
|
+
{
|
|
1297
|
+
...options,
|
|
1298
|
+
},
|
|
1299
|
+
)
|
|
1300
|
+
}
|
|
1259
1301
|
export const useEnduserProblems = (options={} as HookOptions<EnduserProblem>) => {
|
|
1260
1302
|
const session = useResolvedSession()
|
|
1261
1303
|
|