@tellescope/react-components 1.247.0 → 1.249.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/form_responses.d.ts +11 -0
- package/lib/cjs/Forms/form_responses.d.ts.map +1 -1
- package/lib/cjs/Forms/form_responses.js +38 -2
- package/lib/cjs/Forms/form_responses.js.map +1 -1
- package/lib/cjs/Forms/forms.d.ts +4 -3
- package/lib/cjs/Forms/forms.d.ts.map +1 -1
- package/lib/cjs/Forms/forms.js +41 -15
- package/lib/cjs/Forms/forms.js.map +1 -1
- package/lib/cjs/Forms/forms.v2.d.ts +2 -2
- package/lib/cjs/Forms/forms.v2.d.ts.map +1 -1
- package/lib/cjs/Forms/forms.v2.js +8 -8
- package/lib/cjs/Forms/forms.v2.js.map +1 -1
- package/lib/cjs/Forms/hooks.d.ts +2 -0
- package/lib/cjs/Forms/hooks.d.ts.map +1 -1
- package/lib/cjs/Forms/hooks.js +57 -16
- package/lib/cjs/Forms/hooks.js.map +1 -1
- package/lib/cjs/Forms/inputs.d.ts +1 -1
- package/lib/cjs/Forms/inputs.d.ts.map +1 -1
- package/lib/cjs/Forms/inputs.js +6 -3
- package/lib/cjs/Forms/inputs.js.map +1 -1
- package/lib/cjs/Forms/inputs.v2.d.ts +1 -2
- package/lib/cjs/Forms/inputs.v2.d.ts.map +1 -1
- package/lib/cjs/Forms/inputs.v2.js +1 -44
- package/lib/cjs/Forms/inputs.v2.js.map +1 -1
- package/lib/cjs/Forms/types.d.ts +1 -0
- package/lib/cjs/Forms/types.d.ts.map +1 -1
- package/lib/cjs/state.d.ts +7 -1
- package/lib/cjs/state.d.ts.map +1 -1
- package/lib/cjs/state.js +43 -1
- package/lib/cjs/state.js.map +1 -1
- package/lib/esm/CMS/components.d.ts +1 -0
- package/lib/esm/CMS/components.d.ts.map +1 -1
- package/lib/esm/Forms/form_responses.d.ts +11 -0
- package/lib/esm/Forms/form_responses.d.ts.map +1 -1
- package/lib/esm/Forms/form_responses.js +37 -2
- package/lib/esm/Forms/form_responses.js.map +1 -1
- package/lib/esm/Forms/forms.d.ts +6 -5
- package/lib/esm/Forms/forms.d.ts.map +1 -1
- package/lib/esm/Forms/forms.js +41 -15
- package/lib/esm/Forms/forms.js.map +1 -1
- package/lib/esm/Forms/forms.v2.d.ts +4 -4
- package/lib/esm/Forms/forms.v2.d.ts.map +1 -1
- package/lib/esm/Forms/forms.v2.js +8 -8
- package/lib/esm/Forms/forms.v2.js.map +1 -1
- package/lib/esm/Forms/hooks.d.ts +2 -0
- package/lib/esm/Forms/hooks.d.ts.map +1 -1
- package/lib/esm/Forms/hooks.js +58 -17
- package/lib/esm/Forms/hooks.js.map +1 -1
- package/lib/esm/Forms/inputs.d.ts +3 -3
- package/lib/esm/Forms/inputs.d.ts.map +1 -1
- package/lib/esm/Forms/inputs.js +6 -3
- package/lib/esm/Forms/inputs.js.map +1 -1
- package/lib/esm/Forms/inputs.native.d.ts +1 -0
- package/lib/esm/Forms/inputs.native.d.ts.map +1 -1
- package/lib/esm/Forms/inputs.v2.d.ts +1 -2
- package/lib/esm/Forms/inputs.v2.d.ts.map +1 -1
- package/lib/esm/Forms/inputs.v2.js +1 -44
- package/lib/esm/Forms/inputs.v2.js.map +1 -1
- package/lib/esm/Forms/types.d.ts +1 -0
- package/lib/esm/Forms/types.d.ts.map +1 -1
- package/lib/esm/TwilioVideo/hooks.d.ts +1 -1
- package/lib/esm/controls.d.ts +2 -2
- package/lib/esm/inputs.d.ts +1 -1
- package/lib/esm/inputs.native.d.ts +1 -0
- package/lib/esm/inputs.native.d.ts.map +1 -1
- package/lib/esm/state.d.ts +336 -330
- package/lib/esm/state.d.ts.map +1 -1
- package/lib/esm/state.js +42 -1
- package/lib/esm/state.js.map +1 -1
- package/lib/esm/theme.native.d.ts +1 -0
- package/lib/esm/theme.native.d.ts.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/src/Forms/form_responses.tsx +133 -2
- package/src/Forms/forms.tsx +52 -20
- package/src/Forms/forms.v2.tsx +13 -7
- package/src/Forms/hooks.tsx +67 -14
- package/src/Forms/inputs.tsx +6 -3
- package/src/Forms/inputs.v2.tsx +1 -50
- package/src/Forms/types.ts +1 -0
- package/src/state.tsx +37 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tellescope/react-components",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.249.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./lib/cjs/index.js",
|
|
6
6
|
"module": "./lib/esm/index.js",
|
|
@@ -51,13 +51,13 @@
|
|
|
51
51
|
"@reduxjs/toolkit": "1.9.0",
|
|
52
52
|
"@stripe/react-stripe-js": "2.9.0",
|
|
53
53
|
"@stripe/stripe-js": "1.52.1",
|
|
54
|
-
"@tellescope/constants": "1.
|
|
55
|
-
"@tellescope/sdk": "1.
|
|
56
|
-
"@tellescope/types-client": "1.
|
|
57
|
-
"@tellescope/types-models": "1.
|
|
58
|
-
"@tellescope/types-utilities": "1.
|
|
59
|
-
"@tellescope/utilities": "1.
|
|
60
|
-
"@tellescope/validation": "1.
|
|
54
|
+
"@tellescope/constants": "1.249.0",
|
|
55
|
+
"@tellescope/sdk": "1.249.0",
|
|
56
|
+
"@tellescope/types-client": "1.249.0",
|
|
57
|
+
"@tellescope/types-models": "1.249.0",
|
|
58
|
+
"@tellescope/types-utilities": "1.249.0",
|
|
59
|
+
"@tellescope/utilities": "1.249.0",
|
|
60
|
+
"@tellescope/validation": "1.249.0",
|
|
61
61
|
"css-to-react-native": "3.0.0",
|
|
62
62
|
"draft-js": "0.11.7",
|
|
63
63
|
"draftjs-to-html": "0.9.1",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
|
85
85
|
"react-native": "^0.65.0 || ^0.66.0 || ^0.67.0 || ^0.68.0 || ^0.71.0"
|
|
86
86
|
},
|
|
87
|
-
"gitHead": "
|
|
87
|
+
"gitHead": "fb54fd85672184ea09d70e3072e6037eedc84822",
|
|
88
88
|
"publishConfig": {
|
|
89
89
|
"access": "public"
|
|
90
90
|
}
|
|
@@ -2,7 +2,7 @@ import React, { useEffect } from "react"
|
|
|
2
2
|
import { Divider, Grid, Typography } from "@mui/material"
|
|
3
3
|
import { Enduser, FormResponse } from "@tellescope/types-client"
|
|
4
4
|
import { form_response_value_to_string, formatted_date, getOrgnizationLogoURL, remove_script_tags, user_display_name } from "@tellescope/utilities"
|
|
5
|
-
import { DownloadFileIconButton, ImageProps, LabeledIconButton, SecureImage, useEndusers, useOrganization, useResolvedSession, useSession, useUsers, value_is_loaded } from "../index"
|
|
5
|
+
import { DownloadFileIconButton, ImageProps, LabeledIconButton, SecureImage, useEndusers, useEnduserMedications, useEnduserObservations, useOrganization, useResolvedSession, useSession, useUsers, value_is_loaded } from "../index"
|
|
6
6
|
import CloseIcon from '@mui/icons-material/Close';
|
|
7
7
|
import { DatabaseSelectResponse, FormResponseAnswerAddress, FormResponseValueAnswer } from "@tellescope/types-models"
|
|
8
8
|
import { Image } from "../layout"
|
|
@@ -23,6 +23,135 @@ export const AddressDisplay = ({ value } : { value: Required<FormResponseAnswerA
|
|
|
23
23
|
</Grid>
|
|
24
24
|
)
|
|
25
25
|
|
|
26
|
+
type SnapshotRef = { id: string, label: string }
|
|
27
|
+
|
|
28
|
+
export const HistoricalDataSnapshotDisplay = ({ snapshot } : { snapshot: { observations?: SnapshotRef[], medications?: SnapshotRef[], snapshotAt?: string } }) => {
|
|
29
|
+
const { observations: obsRefs = [], medications: medRefs = [], snapshotAt } = snapshot
|
|
30
|
+
const [, { findById: findObservation }] = useEnduserObservations({ dontFetch: true })
|
|
31
|
+
const [, { findById: findMedication }] = useEnduserMedications({ dontFetch: true })
|
|
32
|
+
|
|
33
|
+
const tdStyle = { padding: '6px 8px' } as const
|
|
34
|
+
const deletedStyle = { padding: '6px 8px', color: '#999', fontStyle: 'italic' } as const
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div style={{ marginTop: 10 }}>
|
|
38
|
+
{snapshotAt && (
|
|
39
|
+
<Typography style={{ fontSize: 12, color: '#888', marginBottom: 8 }}>
|
|
40
|
+
Snapshot taken at {formatted_date(new Date(snapshotAt))}
|
|
41
|
+
</Typography>
|
|
42
|
+
)}
|
|
43
|
+
|
|
44
|
+
{obsRefs.length > 0 && (
|
|
45
|
+
<div style={{ marginBottom: 15 }}>
|
|
46
|
+
<Typography style={{ fontWeight: 'bold', marginBottom: 5 }}>Observations</Typography>
|
|
47
|
+
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 14 }}>
|
|
48
|
+
<thead>
|
|
49
|
+
<tr style={{ borderBottom: '2px solid #ccc', textAlign: 'left' }}>
|
|
50
|
+
<th style={tdStyle}>Date</th>
|
|
51
|
+
<th style={tdStyle}>Type</th>
|
|
52
|
+
<th style={tdStyle}>Value</th>
|
|
53
|
+
<th style={tdStyle}>Category</th>
|
|
54
|
+
<th style={tdStyle}>Status</th>
|
|
55
|
+
</tr>
|
|
56
|
+
</thead>
|
|
57
|
+
<tbody>
|
|
58
|
+
{obsRefs.map((ref, i) => {
|
|
59
|
+
const obs = findObservation(ref.id, { batch: true })
|
|
60
|
+
if (obs === undefined) return (
|
|
61
|
+
<tr key={ref.id || i} style={{ borderBottom: '1px solid #eee' }}>
|
|
62
|
+
<td colSpan={5} style={tdStyle}>Loading...</td>
|
|
63
|
+
</tr>
|
|
64
|
+
)
|
|
65
|
+
if (obs === null) return (
|
|
66
|
+
<tr key={ref.id || i} style={{ borderBottom: '1px solid #eee' }}>
|
|
67
|
+
<td colSpan={5} style={deletedStyle}>{ref.label} — Record no longer available</td>
|
|
68
|
+
</tr>
|
|
69
|
+
)
|
|
70
|
+
return (
|
|
71
|
+
<tr key={obs.id || i} style={{ borderBottom: '1px solid #eee' }}>
|
|
72
|
+
<td style={tdStyle}>{obs.timestamp ? formatted_date(new Date(obs.timestamp)) : '-'}</td>
|
|
73
|
+
<td style={tdStyle}>{obs.type || obs.code || '-'}</td>
|
|
74
|
+
<td style={tdStyle}>
|
|
75
|
+
{obs.measurement ? `${obs.measurement.value} ${obs.measurement.unit}` : obs.qualitativeResult || '-'}
|
|
76
|
+
</td>
|
|
77
|
+
<td style={tdStyle}>{obs.category || '-'}</td>
|
|
78
|
+
<td style={tdStyle}>{obs.status || '-'}</td>
|
|
79
|
+
</tr>
|
|
80
|
+
)
|
|
81
|
+
})}
|
|
82
|
+
</tbody>
|
|
83
|
+
</table>
|
|
84
|
+
</div>
|
|
85
|
+
)}
|
|
86
|
+
|
|
87
|
+
{medRefs.length > 0 && (
|
|
88
|
+
<div style={{ marginBottom: 15 }}>
|
|
89
|
+
<Typography style={{ fontWeight: 'bold', marginBottom: 5 }}>Medications</Typography>
|
|
90
|
+
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 14 }}>
|
|
91
|
+
<thead>
|
|
92
|
+
<tr style={{ borderBottom: '2px solid #ccc', textAlign: 'left' }}>
|
|
93
|
+
<th style={tdStyle}>Medication</th>
|
|
94
|
+
<th style={tdStyle}>Dosage</th>
|
|
95
|
+
<th style={tdStyle}>Dispensing</th>
|
|
96
|
+
<th style={tdStyle}>Pharmacy</th>
|
|
97
|
+
<th style={tdStyle}>Prescriber</th>
|
|
98
|
+
<th style={tdStyle}>Date</th>
|
|
99
|
+
</tr>
|
|
100
|
+
</thead>
|
|
101
|
+
<tbody>
|
|
102
|
+
{medRefs.map((ref, i) => {
|
|
103
|
+
const med = findMedication(ref.id, { batch: true })
|
|
104
|
+
if (med === undefined) return (
|
|
105
|
+
<tr key={ref.id || i} style={{ borderBottom: '1px solid #eee' }}>
|
|
106
|
+
<td colSpan={6} style={tdStyle}>Loading...</td>
|
|
107
|
+
</tr>
|
|
108
|
+
)
|
|
109
|
+
if (med === null) return (
|
|
110
|
+
<tr key={ref.id || i} style={{ borderBottom: '1px solid #eee' }}>
|
|
111
|
+
<td colSpan={6} style={deletedStyle}>{ref.label} — Record no longer available</td>
|
|
112
|
+
</tr>
|
|
113
|
+
)
|
|
114
|
+
return (
|
|
115
|
+
<tr key={med.id || i} style={{ borderBottom: '1px solid #eee' }}>
|
|
116
|
+
<td style={tdStyle}>
|
|
117
|
+
{med.title || '-'}
|
|
118
|
+
{med.allergyNote ? <div style={{ color: 'red', fontSize: 12 }}>Allergies: {med.allergyNote}</div> : null}
|
|
119
|
+
{med.directions ? <div style={{ color: '#888', fontSize: 12 }}>Directions: {med.directions}</div> : null}
|
|
120
|
+
</td>
|
|
121
|
+
<td style={tdStyle}>
|
|
122
|
+
{med.dosage
|
|
123
|
+
? med.dosage.description
|
|
124
|
+
? med.dosage.description
|
|
125
|
+
: `${med.dosage.value || ''}${med.dosage.unit ? ` ${med.dosage.unit}` : ''}${med.dosage.quantity ? ` ${med.dosage.quantity} units` : ''}${med.dosage.frequency ? ` ${!isNaN(parseInt(med.dosage.frequency)) ? `${med.dosage.frequency}x ${med.dosage?.frequencyDescriptor ? `Per ${med.dosage.frequencyDescriptor}` : 'daily'}` : med.dosage.frequency}` : ''}`
|
|
126
|
+
: '-'}
|
|
127
|
+
</td>
|
|
128
|
+
<td style={tdStyle}>
|
|
129
|
+
{med.dispensing ? `${med.dispensing.quantity || ''} ${med.dispensing.unit || ''}`.trim() || '-' : '-'}
|
|
130
|
+
</td>
|
|
131
|
+
<td style={tdStyle}>{med.pharmacyName || med.pharmacyId || '-'}</td>
|
|
132
|
+
<td style={tdStyle}>
|
|
133
|
+
{med.prescriberName || '-'}
|
|
134
|
+
{med.source ? <div style={{ fontStyle: 'italic', fontSize: 12 }}>{med.source}</div> : null}
|
|
135
|
+
{med.notes ? <div style={{ fontSize: 12 }}>{med.notes}</div> : null}
|
|
136
|
+
</td>
|
|
137
|
+
<td style={tdStyle}>
|
|
138
|
+
{formatted_date(new Date(med.startedTakingAt || med.prescribedAt || med.createdAt))}
|
|
139
|
+
</td>
|
|
140
|
+
</tr>
|
|
141
|
+
)
|
|
142
|
+
})}
|
|
143
|
+
</tbody>
|
|
144
|
+
</table>
|
|
145
|
+
</div>
|
|
146
|
+
)}
|
|
147
|
+
|
|
148
|
+
{obsRefs.length === 0 && medRefs.length === 0 && (
|
|
149
|
+
<Typography style={{ fontStyle: 'italic', color: '#888' }}>No historical data recorded</Typography>
|
|
150
|
+
)}
|
|
151
|
+
</div>
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
|
|
26
155
|
export const ResponseAnswer = ({ formResponse, fieldId, isHTML, answer: a, printing, onImageClick } : {
|
|
27
156
|
answer: FormResponseValueAnswer,
|
|
28
157
|
formResponse: FormResponse,
|
|
@@ -177,7 +306,9 @@ export const ResponseAnswer = ({ formResponse, fieldId, isHTML, answer: a, print
|
|
|
177
306
|
</Typography>
|
|
178
307
|
) : (
|
|
179
308
|
a.type === 'description'
|
|
180
|
-
?
|
|
309
|
+
? (a.value && typeof a.value === 'string' && a.value.startsWith('{'))
|
|
310
|
+
? (() => { try { return <HistoricalDataSnapshotDisplay snapshot={JSON.parse(a.value)} /> } catch { return <></> } })()
|
|
311
|
+
: <></>
|
|
181
312
|
: <Typography>No value provided</Typography>
|
|
182
313
|
)
|
|
183
314
|
)
|
package/src/Forms/forms.tsx
CHANGED
|
@@ -145,6 +145,7 @@ export const QuestionForField = ({
|
|
|
145
145
|
uploadingFiles, setUploadingFiles, handleFileUpload,
|
|
146
146
|
groupFields,
|
|
147
147
|
AddToDatabase,
|
|
148
|
+
lastNavigationDirectionRef,
|
|
148
149
|
} : {
|
|
149
150
|
spacing?: number,
|
|
150
151
|
form?: Form,
|
|
@@ -163,19 +164,19 @@ export const QuestionForField = ({
|
|
|
163
164
|
setUploadingFiles: React.Dispatch<React.SetStateAction<{ fieldId: string }[]>>,
|
|
164
165
|
groupFields?: FormField[],
|
|
165
166
|
AddToDatabase?: React.JSXElementConstructor<AddToDatabaseProps>,
|
|
166
|
-
} & Pick<TellescopeFormProps, "rootResponseId" | "goToNextField" | "groupId" | "groupInstance" | "submit" | "formResponseId" | 'enduserId' | 'isPreviousDisabled' | 'goToPreviousField' | 'enduser' | 'handleDatabaseSelect' | 'onAddFile' | 'onFieldChange' | 'fields' | 'customInputs' | 'responses' | 'selectedFiles' | 'validateField'>) => {
|
|
167
|
+
} & Pick<TellescopeFormProps, "rootResponseId" | "goToNextField" | "groupId" | "groupInstance" | "submit" | "formResponseId" | 'enduserId' | 'isPreviousDisabled' | 'goToPreviousField' | 'enduser' | 'handleDatabaseSelect' | 'onAddFile' | 'onFieldChange' | 'fields' | 'customInputs' | 'responses' | 'selectedFiles' | 'validateField' | 'lastNavigationDirectionRef'>) => {
|
|
167
168
|
const String = customInputs?.['string'] ?? StringInput
|
|
168
169
|
const StringLong = customInputs?.['stringLong'] ?? StringLongInput
|
|
169
170
|
const Email = customInputs?.['email'] ?? EmailInput
|
|
170
171
|
const Number = customInputs?.['number'] ?? NumberInput
|
|
171
|
-
const Phone = customInputs?.['phone'] ?? PhoneInput
|
|
172
|
-
const ResolvedDateInput = customInputs?.['date'] ?? DateInput
|
|
173
|
-
const Signature = customInputs?.['signature'] ?? SignatureInput
|
|
174
|
-
const MultipleChoice = customInputs?.['multiple_choice'] ?? MultipleChoiceInput
|
|
175
|
-
const Stripe = customInputs?.['Stripe'] ?? StripeInput
|
|
176
|
-
const Chargebee = customInputs?.['Chargebee'] ?? ChargeebeeInput
|
|
177
|
-
const File = customInputs?.['file'] ?? FileInput
|
|
178
|
-
const Files = customInputs?.['files'] ?? FilesInput
|
|
172
|
+
const Phone = customInputs?.['phone'] ?? PhoneInput
|
|
173
|
+
const ResolvedDateInput = customInputs?.['date'] ?? DateInput
|
|
174
|
+
const Signature = customInputs?.['signature'] ?? SignatureInput
|
|
175
|
+
const MultipleChoice = customInputs?.['multiple_choice'] ?? MultipleChoiceInput
|
|
176
|
+
const Stripe = customInputs?.['Stripe'] ?? StripeInput
|
|
177
|
+
const Chargebee = customInputs?.['Chargebee'] ?? ChargeebeeInput
|
|
178
|
+
const File = customInputs?.['file'] ?? FileInput
|
|
179
|
+
const Files = customInputs?.['files'] ?? FilesInput
|
|
179
180
|
const Ranking = customInputs?.['ranking'] ?? RankingInput
|
|
180
181
|
const Rating = customInputs?.['rating'] ?? RatingInput
|
|
181
182
|
const Address = customInputs?.['Address'] ?? AddressInput
|
|
@@ -236,7 +237,7 @@ export const QuestionForField = ({
|
|
|
236
237
|
<div style={{ marginTop: 15 }}></div>
|
|
237
238
|
}
|
|
238
239
|
|
|
239
|
-
<Description field={field} style={{ fontSize: 16 }} enduserId={enduserId} />
|
|
240
|
+
<Description field={field} style={{ fontSize: 16 }} enduserId={enduserId} onFieldChange={onFieldChange} />
|
|
240
241
|
|
|
241
242
|
{feedback.length > 0 &&
|
|
242
243
|
<Flex column style={{ marginBottom: 11, marginTop: 3, }}>
|
|
@@ -293,7 +294,7 @@ export const QuestionForField = ({
|
|
|
293
294
|
<DateStringInput field={field} disabled={value.disabled} value={value.answer.value as string} onChange={onFieldChange as ChangeHandler<'string'>} form={form} />
|
|
294
295
|
)
|
|
295
296
|
: field.type === 'Hidden Value' ? (
|
|
296
|
-
<HiddenValue groupFields={groupFields} isSinglePage={isSinglePage} goToNextField={goToNextField} goToPreviousField={goToPreviousField} field={field} value={value.answer.value as string} onChange={onFieldChange as ChangeHandler<any>} form={form} />
|
|
297
|
+
<HiddenValue groupFields={groupFields} isSinglePage={isSinglePage} goToNextField={goToNextField} goToPreviousField={goToPreviousField} field={field} value={value.answer.value as string} onChange={onFieldChange as ChangeHandler<any>} form={form} lastNavigationDirectionRef={lastNavigationDirectionRef} />
|
|
297
298
|
)
|
|
298
299
|
: field.type === 'Address' ? (
|
|
299
300
|
<Address field={field} disabled={value.disabled} value={value.answer.value as any} onChange={onFieldChange as ChangeHandler<any>} form={form} />
|
|
@@ -320,7 +321,7 @@ export const QuestionForField = ({
|
|
|
320
321
|
<String field={field} disabled={value.disabled} value={value.answer.value as string} onChange={onFieldChange as ChangeHandler<'string'>} form={form} />
|
|
321
322
|
)
|
|
322
323
|
: field.type === 'Appointment Booking' ? (
|
|
323
|
-
<AppointmentBooking formResponseId={formResponseId} enduserId={enduserId} goToPreviousField={goToPreviousField} isPreviousDisabled={isPreviousDisabled} responses={responses} field={field} value={value.answer.value as string} onChange={onFieldChange as ChangeHandler<'Appointment Booking'>} form={form} />
|
|
324
|
+
<AppointmentBooking key={field.id} formResponseId={formResponseId} enduserId={enduserId} goToPreviousField={goToPreviousField} isPreviousDisabled={isPreviousDisabled} responses={responses} field={field} value={value.answer.value as string} onChange={onFieldChange as ChangeHandler<'Appointment Booking'>} form={form} />
|
|
324
325
|
)
|
|
325
326
|
: field.type === 'Stripe' ? (
|
|
326
327
|
<Stripe enduserId={enduserId} field={field} value={value.answer.value as string} onChange={onFieldChange as ChangeHandler<any>} setCustomerId={setCustomerId} form={form} responses={responses} enduser={enduser} />
|
|
@@ -428,13 +429,13 @@ export const QuestionForField = ({
|
|
|
428
429
|
enduser={enduser} goToPreviousField={goToPreviousField} isPreviousDisabled={isPreviousDisabled} goToNextField={goToNextField}
|
|
429
430
|
form={form} formResponseId={formResponseId} rootResponseId={rootResponseId} submit={submit}
|
|
430
431
|
repeats={repeats} onRepeatsChange={onRepeatsChange} setCustomerId={setCustomerId}
|
|
431
|
-
value={value} file={file}
|
|
432
|
+
value={value} file={file}
|
|
432
433
|
onAddFile={onAddFile} onFieldChange={onFieldChange}
|
|
433
434
|
responses={responses} selectedFiles={selectedFiles}
|
|
434
435
|
validateField={validateField} enduserId={enduserId}
|
|
435
436
|
spacing={field.options?.groupPadding}
|
|
436
437
|
logicOptions={logicOptions}
|
|
437
|
-
isInQuestionGroup
|
|
438
|
+
isInQuestionGroup
|
|
438
439
|
groupFields={
|
|
439
440
|
fields.filter(f => field.options?.subFields?.find(s => s.id === f.id))
|
|
440
441
|
}
|
|
@@ -442,6 +443,7 @@ export const QuestionForField = ({
|
|
|
442
443
|
uploadingFiles={uploadingFiles} setUploadingFiles={setUploadingFiles}
|
|
443
444
|
handleFileUpload={handleFileUpload}
|
|
444
445
|
AddToDatabase={AddToDatabase}
|
|
446
|
+
lastNavigationDirectionRef={lastNavigationDirectionRef}
|
|
445
447
|
/>
|
|
446
448
|
</Flex>
|
|
447
449
|
)
|
|
@@ -529,6 +531,7 @@ export const TellescopeSingleQuestionFlow: typeof TellescopeForm = ({
|
|
|
529
531
|
groupInstance,
|
|
530
532
|
logicOptions,
|
|
531
533
|
uploadingFiles, setUploadingFiles, handleFileUpload,
|
|
534
|
+
lastNavigationDirectionRef,
|
|
532
535
|
}) => {
|
|
533
536
|
const beforeunloadHandler = React.useCallback((e: BeforeUnloadEvent) => {
|
|
534
537
|
try {
|
|
@@ -652,6 +655,7 @@ export const TellescopeSingleQuestionFlow: typeof TellescopeForm = ({
|
|
|
652
655
|
logicOptions={logicOptions}
|
|
653
656
|
uploadingFiles={uploadingFiles} setUploadingFiles={setUploadingFiles}
|
|
654
657
|
handleFileUpload={handleFileUpload}
|
|
658
|
+
lastNavigationDirectionRef={lastNavigationDirectionRef}
|
|
655
659
|
/>
|
|
656
660
|
</Flex>
|
|
657
661
|
</Flex>
|
|
@@ -1017,7 +1021,7 @@ export const UpdateResponse = ({
|
|
|
1017
1021
|
)
|
|
1018
1022
|
}
|
|
1019
1023
|
|
|
1020
|
-
const HistoricalDataSection = ({ sources, enduserId } : { sources: HistoricalDataSource[], enduserId: string }) => {
|
|
1024
|
+
const HistoricalDataSection = ({ sources, enduserId, onDataLoaded } : { sources: HistoricalDataSource[], enduserId: string, onDataLoaded?: (json: string) => void }) => {
|
|
1021
1025
|
const session = useSession({ throwIfMissingContext: false })
|
|
1022
1026
|
const [observations, setObservations] = useState<any[]>([])
|
|
1023
1027
|
const [medications, setMedications] = useState<any[]>([])
|
|
@@ -1039,6 +1043,8 @@ const HistoricalDataSection = ({ sources, enduserId } : { sources: HistoricalDat
|
|
|
1039
1043
|
setError('')
|
|
1040
1044
|
try {
|
|
1041
1045
|
const promises: Promise<void>[] = []
|
|
1046
|
+
let loadedObservations: any[] = []
|
|
1047
|
+
let loadedMedications: any[] = []
|
|
1042
1048
|
|
|
1043
1049
|
for (const source of sources) {
|
|
1044
1050
|
if (source.type === 'Observations') {
|
|
@@ -1047,7 +1053,7 @@ const HistoricalDataSection = ({ sources, enduserId } : { sources: HistoricalDat
|
|
|
1047
1053
|
filter: { enduserId, ...source.filter },
|
|
1048
1054
|
limit: source.limit,
|
|
1049
1055
|
})
|
|
1050
|
-
.then((obs: any[]) => setObservations(obs))
|
|
1056
|
+
.then((obs: any[]) => { loadedObservations = obs; setObservations(obs) })
|
|
1051
1057
|
)
|
|
1052
1058
|
} else if (source.type === 'Medications') {
|
|
1053
1059
|
promises.push(
|
|
@@ -1055,12 +1061,35 @@ const HistoricalDataSection = ({ sources, enduserId } : { sources: HistoricalDat
|
|
|
1055
1061
|
filter: { enduserId, status: { _ne: 'draft' }, ...source.filter },
|
|
1056
1062
|
limit: source.limit,
|
|
1057
1063
|
})
|
|
1058
|
-
.then((meds: any[]) => setMedications(meds))
|
|
1064
|
+
.then((meds: any[]) => { loadedMedications = meds; setMedications(meds) })
|
|
1059
1065
|
)
|
|
1060
1066
|
}
|
|
1061
1067
|
}
|
|
1062
1068
|
|
|
1063
1069
|
await Promise.all(promises)
|
|
1070
|
+
|
|
1071
|
+
const obsLabel = (o: any) => {
|
|
1072
|
+
const name = o.type || o.code || ''
|
|
1073
|
+
const val = o.measurement ? `${o.measurement.value} ${o.measurement.unit}` : o.qualitativeResult || ''
|
|
1074
|
+
return `${name}${val ? ' ' + val : ''}`.slice(0, 50)
|
|
1075
|
+
}
|
|
1076
|
+
const medLabel = (m: any) => {
|
|
1077
|
+
const dose = m.dosage?.quantity ? ` ${m.dosage.quantity}${m.dosage.unit ? m.dosage.unit : ''}` : ''
|
|
1078
|
+
return `${m.title || ''}${dose}`.slice(0, 50)
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
const MAX_SNAPSHOT_LENGTH = 24000
|
|
1082
|
+
let obsRefs = loadedObservations.map((o: any) => ({ id: o.id, label: obsLabel(o) }))
|
|
1083
|
+
let medRefs = loadedMedications.map((m: any) => ({ id: m.id, label: medLabel(m) }))
|
|
1084
|
+
|
|
1085
|
+
let json = JSON.stringify({ observations: obsRefs, medications: medRefs, snapshotAt: new Date().toISOString() })
|
|
1086
|
+
while (json.length > MAX_SNAPSHOT_LENGTH && (obsRefs.length + medRefs.length) > 0) {
|
|
1087
|
+
if (obsRefs.length >= medRefs.length) { obsRefs.pop() }
|
|
1088
|
+
else { medRefs.pop() }
|
|
1089
|
+
json = JSON.stringify({ observations: obsRefs, medications: medRefs, snapshotAt: new Date().toISOString() })
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
onDataLoaded?.(json)
|
|
1064
1093
|
} catch (err: any) {
|
|
1065
1094
|
setError(err?.message || 'Failed to load historical data')
|
|
1066
1095
|
} finally {
|
|
@@ -1183,7 +1212,7 @@ const HistoricalDataSection = ({ sources, enduserId } : { sources: HistoricalDat
|
|
|
1183
1212
|
)
|
|
1184
1213
|
}
|
|
1185
1214
|
|
|
1186
|
-
export const Description = ({ field, color="primary", style, enduserId } : { field: FormField, color?: string, enduserId?: string } & Styled) => {
|
|
1215
|
+
export const Description = ({ field, color="primary", style, enduserId, onFieldChange } : { field: FormField, color?: string, enduserId?: string, onFieldChange?: (value: any, fieldId: string) => void } & Styled) => {
|
|
1187
1216
|
const existingContent = (
|
|
1188
1217
|
!field.htmlDescription && field.description ? (
|
|
1189
1218
|
<Typography color={color as any} style={style}>
|
|
@@ -1203,6 +1232,7 @@ export const Description = ({ field, color="primary", style, enduserId } : { fie
|
|
|
1203
1232
|
<HistoricalDataSection
|
|
1204
1233
|
sources={field.options.historicalDataSources}
|
|
1205
1234
|
enduserId={enduserId}
|
|
1235
|
+
onDataLoaded={onFieldChange ? (jsonString) => onFieldChange(jsonString, field.id) : undefined}
|
|
1206
1236
|
/>
|
|
1207
1237
|
) : null}
|
|
1208
1238
|
</>
|
|
@@ -1269,7 +1299,8 @@ export const TellescopeSinglePageForm: React.JSXElementConstructor<TellescopeFor
|
|
|
1269
1299
|
groupInstance,
|
|
1270
1300
|
uploadingFiles, setUploadingFiles, handleFileUpload,
|
|
1271
1301
|
AddToDatabase,
|
|
1272
|
-
|
|
1302
|
+
lastNavigationDirectionRef,
|
|
1303
|
+
...props
|
|
1273
1304
|
}) => {
|
|
1274
1305
|
const list = useListForFormFields(fields, responses, { form: props.form, gender: enduser?.gender })
|
|
1275
1306
|
|
|
@@ -1398,7 +1429,7 @@ export const TellescopeSinglePageForm: React.JSXElementConstructor<TellescopeFor
|
|
|
1398
1429
|
enduserId={props.enduserId} formResponseId={props.formResponseId} rootResponseId={rootResponseId} submit={submit}
|
|
1399
1430
|
enduser={enduser} goToPreviousField={goToPreviousField} isPreviousDisabled={isPreviousDisabled} goToNextField={goToNextField}
|
|
1400
1431
|
repeats={repeats} onRepeatsChange={setRepeats} setCustomerId={setCustomerId}
|
|
1401
|
-
value={value} file={file}
|
|
1432
|
+
value={value} file={file}
|
|
1402
1433
|
customInputs={customInputs}
|
|
1403
1434
|
onAddFile={onAddFile} onFieldChange={onFieldChange}
|
|
1404
1435
|
responses={responses} selectedFiles={selectedFiles}
|
|
@@ -1407,6 +1438,7 @@ export const TellescopeSinglePageForm: React.JSXElementConstructor<TellescopeFor
|
|
|
1407
1438
|
uploadingFiles={uploadingFiles} setUploadingFiles={setUploadingFiles}
|
|
1408
1439
|
handleFileUpload={handleFileUpload}
|
|
1409
1440
|
AddToDatabase={AddToDatabase}
|
|
1441
|
+
lastNavigationDirectionRef={lastNavigationDirectionRef}
|
|
1410
1442
|
/>
|
|
1411
1443
|
</Flex>
|
|
1412
1444
|
</Flex>
|
package/src/Forms/forms.v2.tsx
CHANGED
|
@@ -154,6 +154,7 @@ export const QuestionForField = ({
|
|
|
154
154
|
uploadingFiles, setUploadingFiles, handleFileUpload,
|
|
155
155
|
groupFields,
|
|
156
156
|
AddToDatabase,
|
|
157
|
+
lastNavigationDirectionRef,
|
|
157
158
|
} : {
|
|
158
159
|
spacing?: number,
|
|
159
160
|
form?: Form,
|
|
@@ -172,7 +173,7 @@ export const QuestionForField = ({
|
|
|
172
173
|
setUploadingFiles: React.Dispatch<React.SetStateAction<{ fieldId: string }[]>>,
|
|
173
174
|
groupFields?: FormField[],
|
|
174
175
|
AddToDatabase?: React.JSXElementConstructor<AddToDatabaseProps>,
|
|
175
|
-
} & Pick<TellescopeFormProps, "rootResponseId" | "goToNextField" | "groupId" | "groupInstance" | "submit" | "formResponseId" | 'enduserId' | 'isPreviousDisabled' | 'goToPreviousField' | 'enduser' | 'handleDatabaseSelect' | 'onAddFile' | 'onFieldChange' | 'fields' | 'customInputs' | 'responses' | 'selectedFiles' | 'validateField'>) => {
|
|
176
|
+
} & Pick<TellescopeFormProps, "rootResponseId" | "goToNextField" | "groupId" | "groupInstance" | "submit" | "formResponseId" | 'enduserId' | 'isPreviousDisabled' | 'goToPreviousField' | 'enduser' | 'handleDatabaseSelect' | 'onAddFile' | 'onFieldChange' | 'fields' | 'customInputs' | 'responses' | 'selectedFiles' | 'validateField' | 'lastNavigationDirectionRef'>) => {
|
|
176
177
|
const String = customInputs?.['string'] ?? StringInput
|
|
177
178
|
const StringLong = customInputs?.['stringLong'] ?? StringLongInput
|
|
178
179
|
const Email = customInputs?.['email'] ?? EmailInput
|
|
@@ -303,7 +304,7 @@ export const QuestionForField = ({
|
|
|
303
304
|
<DateStringInput field={field} disabled={value.disabled} value={value.answer.value as string} onChange={onFieldChange as ChangeHandler<'string'>} form={form} />
|
|
304
305
|
)
|
|
305
306
|
: field.type === 'Hidden Value' ? (
|
|
306
|
-
<HiddenValue groupFields={groupFields} isSinglePage={isSinglePage} goToNextField={goToNextField} goToPreviousField={goToPreviousField} field={field} value={value.answer.value as string} onChange={onFieldChange as ChangeHandler<any>} form={form} />
|
|
307
|
+
<HiddenValue groupFields={groupFields} isSinglePage={isSinglePage} goToNextField={goToNextField} goToPreviousField={goToPreviousField} field={field} value={value.answer.value as string} onChange={onFieldChange as ChangeHandler<any>} form={form} lastNavigationDirectionRef={lastNavigationDirectionRef} />
|
|
307
308
|
)
|
|
308
309
|
: field.type === 'Address' ? (
|
|
309
310
|
<Address field={field} disabled={value.disabled} value={value.answer.value as any} onChange={onFieldChange as ChangeHandler<any>} form={form} />
|
|
@@ -330,7 +331,7 @@ export const QuestionForField = ({
|
|
|
330
331
|
<String field={field} disabled={value.disabled} value={value.answer.value as string} onChange={onFieldChange as ChangeHandler<'string'>} form={form} error={!!validationMessage && (!['A response is required', 'A value must be checked', 'A file is required', 'Enter a valid phone number', 'Insurer is required'].includes(validationMessage) || value.touched)} />
|
|
331
332
|
)
|
|
332
333
|
: field.type === 'Appointment Booking' ? (
|
|
333
|
-
<AppointmentBooking formResponseId={formResponseId} enduserId={enduserId} goToPreviousField={goToPreviousField} isPreviousDisabled={isPreviousDisabled} responses={responses} field={field} value={value.answer.value as string} onChange={onFieldChange as ChangeHandler<'Appointment Booking'>} form={form} />
|
|
334
|
+
<AppointmentBooking key={field.id} formResponseId={formResponseId} enduserId={enduserId} goToPreviousField={goToPreviousField} isPreviousDisabled={isPreviousDisabled} responses={responses} field={field} value={value.answer.value as string} onChange={onFieldChange as ChangeHandler<'Appointment Booking'>} form={form} />
|
|
334
335
|
)
|
|
335
336
|
: field.type === 'Stripe' ? (
|
|
336
337
|
<Stripe enduserId={enduserId} field={field} value={value.answer.value as string} onChange={onFieldChange as ChangeHandler<any>} setCustomerId={setCustomerId} form={form} responses={responses} enduser={enduser} />
|
|
@@ -438,13 +439,13 @@ export const QuestionForField = ({
|
|
|
438
439
|
enduser={enduser} goToPreviousField={goToPreviousField} isPreviousDisabled={isPreviousDisabled} goToNextField={goToNextField}
|
|
439
440
|
form={form} formResponseId={formResponseId} rootResponseId={rootResponseId} submit={submit}
|
|
440
441
|
repeats={repeats} onRepeatsChange={onRepeatsChange} setCustomerId={setCustomerId}
|
|
441
|
-
value={value} file={file}
|
|
442
|
+
value={value} file={file}
|
|
442
443
|
onAddFile={onAddFile} onFieldChange={onFieldChange}
|
|
443
444
|
responses={responses} selectedFiles={selectedFiles}
|
|
444
445
|
validateField={validateField} enduserId={enduserId}
|
|
445
446
|
spacing={field.options?.groupPadding}
|
|
446
447
|
logicOptions={logicOptions}
|
|
447
|
-
isInQuestionGroup
|
|
448
|
+
isInQuestionGroup
|
|
448
449
|
groupFields={
|
|
449
450
|
fields.filter(f => field.options?.subFields?.find(s => s.id === f.id))
|
|
450
451
|
}
|
|
@@ -452,6 +453,7 @@ export const QuestionForField = ({
|
|
|
452
453
|
uploadingFiles={uploadingFiles} setUploadingFiles={setUploadingFiles}
|
|
453
454
|
handleFileUpload={handleFileUpload}
|
|
454
455
|
AddToDatabase={AddToDatabase}
|
|
456
|
+
lastNavigationDirectionRef={lastNavigationDirectionRef}
|
|
455
457
|
/>
|
|
456
458
|
</Flex>
|
|
457
459
|
)
|
|
@@ -539,6 +541,7 @@ export const TellescopeSingleQuestionFlowV2: typeof TellescopeFormV2 = ({
|
|
|
539
541
|
groupInstance,
|
|
540
542
|
logicOptions,
|
|
541
543
|
uploadingFiles, setUploadingFiles, handleFileUpload,
|
|
544
|
+
lastNavigationDirectionRef,
|
|
542
545
|
}) => {
|
|
543
546
|
const beforeunloadHandler = React.useCallback((e: BeforeUnloadEvent) => {
|
|
544
547
|
try {
|
|
@@ -689,6 +692,7 @@ export const TellescopeSingleQuestionFlowV2: typeof TellescopeFormV2 = ({
|
|
|
689
692
|
logicOptions={logicOptions}
|
|
690
693
|
uploadingFiles={uploadingFiles} setUploadingFiles={setUploadingFiles}
|
|
691
694
|
handleFileUpload={handleFileUpload}
|
|
695
|
+
lastNavigationDirectionRef={lastNavigationDirectionRef}
|
|
692
696
|
/>
|
|
693
697
|
</Flex>
|
|
694
698
|
</Flex>
|
|
@@ -1137,7 +1141,8 @@ export const TellescopeSinglePageForm: React.JSXElementConstructor<TellescopeFor
|
|
|
1137
1141
|
groupInstance,
|
|
1138
1142
|
uploadingFiles, setUploadingFiles, handleFileUpload,
|
|
1139
1143
|
AddToDatabase,
|
|
1140
|
-
|
|
1144
|
+
lastNavigationDirectionRef,
|
|
1145
|
+
...props
|
|
1141
1146
|
}) => {
|
|
1142
1147
|
const list = useListForFormFields(fields, responses, { form: props.form, gender: enduser?.gender })
|
|
1143
1148
|
|
|
@@ -1266,7 +1271,7 @@ export const TellescopeSinglePageForm: React.JSXElementConstructor<TellescopeFor
|
|
|
1266
1271
|
enduserId={props.enduserId} formResponseId={props.formResponseId} rootResponseId={rootResponseId} submit={submit}
|
|
1267
1272
|
enduser={enduser} goToPreviousField={goToPreviousField} isPreviousDisabled={isPreviousDisabled} goToNextField={goToNextField}
|
|
1268
1273
|
repeats={repeats} onRepeatsChange={setRepeats} setCustomerId={setCustomerId}
|
|
1269
|
-
value={value} file={file}
|
|
1274
|
+
value={value} file={file}
|
|
1270
1275
|
customInputs={customInputs}
|
|
1271
1276
|
onAddFile={onAddFile} onFieldChange={onFieldChange}
|
|
1272
1277
|
responses={responses} selectedFiles={selectedFiles}
|
|
@@ -1275,6 +1280,7 @@ export const TellescopeSinglePageForm: React.JSXElementConstructor<TellescopeFor
|
|
|
1275
1280
|
uploadingFiles={uploadingFiles} setUploadingFiles={setUploadingFiles}
|
|
1276
1281
|
handleFileUpload={handleFileUpload}
|
|
1277
1282
|
AddToDatabase={AddToDatabase}
|
|
1283
|
+
lastNavigationDirectionRef={lastNavigationDirectionRef}
|
|
1278
1284
|
/>
|
|
1279
1285
|
</Flex>
|
|
1280
1286
|
</Flex>
|