@tellescope/react-components 1.224.0 → 1.225.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/CMS/ContentViewer.d.ts.map +1 -1
- package/lib/cjs/CMS/ContentViewer.js +28 -11
- package/lib/cjs/CMS/ContentViewer.js.map +1 -1
- package/lib/cjs/Forms/form_responses.d.ts.map +1 -1
- package/lib/cjs/Forms/form_responses.js +2 -2
- package/lib/cjs/Forms/form_responses.js.map +1 -1
- package/lib/cjs/Forms/forms.d.ts.map +1 -1
- package/lib/cjs/Forms/forms.js +31 -2
- package/lib/cjs/Forms/forms.js.map +1 -1
- package/lib/cjs/Forms/hooks.d.ts +4 -1
- package/lib/cjs/Forms/hooks.d.ts.map +1 -1
- package/lib/cjs/Forms/hooks.js +14 -9
- package/lib/cjs/Forms/hooks.js.map +1 -1
- package/lib/cjs/Forms/inputs.d.ts +1 -1
- package/lib/cjs/Forms/inputs.js +3 -3
- package/lib/cjs/Forms/inputs.js.map +1 -1
- package/lib/cjs/state.d.ts.map +1 -1
- package/lib/cjs/state.js +50 -25
- package/lib/cjs/state.js.map +1 -1
- package/lib/esm/CMS/ContentViewer.d.ts.map +1 -1
- package/lib/esm/CMS/ContentViewer.js +28 -11
- package/lib/esm/CMS/ContentViewer.js.map +1 -1
- package/lib/esm/Forms/form_responses.d.ts.map +1 -1
- package/lib/esm/Forms/form_responses.js +2 -2
- package/lib/esm/Forms/form_responses.js.map +1 -1
- package/lib/esm/Forms/forms.d.ts.map +1 -1
- package/lib/esm/Forms/forms.js +32 -3
- package/lib/esm/Forms/forms.js.map +1 -1
- package/lib/esm/Forms/hooks.d.ts +4 -1
- package/lib/esm/Forms/hooks.d.ts.map +1 -1
- package/lib/esm/Forms/hooks.js +14 -9
- package/lib/esm/Forms/hooks.js.map +1 -1
- package/lib/esm/Forms/inputs.d.ts +1 -1
- package/lib/esm/Forms/inputs.js +3 -3
- package/lib/esm/Forms/inputs.js.map +1 -1
- package/lib/esm/state.d.ts.map +1 -1
- package/lib/esm/state.js +50 -25
- package/lib/esm/state.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/src/CMS/ContentViewer.tsx +38 -10
- package/src/Forms/form_responses.tsx +16 -14
- package/src/Forms/forms.tsx +52 -7
- package/src/Forms/hooks.tsx +14 -6
- package/src/Forms/inputs.tsx +3 -3
- package/src/state.tsx +57 -29
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tellescope/react-components",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.225.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.225.0",
|
|
51
|
+
"@tellescope/sdk": "^1.225.0",
|
|
52
|
+
"@tellescope/types-client": "^1.225.0",
|
|
53
|
+
"@tellescope/types-models": "^1.225.0",
|
|
54
|
+
"@tellescope/types-utilities": "^1.225.0",
|
|
55
|
+
"@tellescope/utilities": "^1.225.0",
|
|
56
|
+
"@tellescope/validation": "^1.225.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",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
|
84
84
|
"react-native": "^0.65.0 || ^0.66.0 || ^0.67.0 || ^0.68.0 || ^0.71.0"
|
|
85
85
|
},
|
|
86
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "764982e316e8cf0dba3151e4d0ecf3741bc39725",
|
|
87
87
|
"publishConfig": {
|
|
88
88
|
"access": "public"
|
|
89
89
|
}
|
|
@@ -117,6 +117,28 @@ export const correct_youtube_link_for_embed = (link: string) => {
|
|
|
117
117
|
return link.replace('/watch?v=', '/embed/').split('&')[0]
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
const blockStyleToCSS = (style?: any): React.CSSProperties => {
|
|
121
|
+
if (!style) return {}
|
|
122
|
+
|
|
123
|
+
const cssStyle: React.CSSProperties = {}
|
|
124
|
+
|
|
125
|
+
if (style.width) cssStyle.width = `${style.width}px`
|
|
126
|
+
if (style.height) cssStyle.height = `${style.height}px`
|
|
127
|
+
if (style.backgroundColor) cssStyle.backgroundColor = style.backgroundColor
|
|
128
|
+
if (style.textColor) cssStyle.color = style.textColor
|
|
129
|
+
if (style.borderColor || style.borderWidth) {
|
|
130
|
+
cssStyle.border = `${style.borderWidth || 1}px solid ${style.borderColor || '#cccccc'}`
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Add default styling when any box style is applied
|
|
134
|
+
if (Object.keys(cssStyle).length > 0) {
|
|
135
|
+
cssStyle.padding = cssStyle.padding || '10px'
|
|
136
|
+
cssStyle.display = cssStyle.display || 'inline-block'
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return cssStyle
|
|
140
|
+
}
|
|
141
|
+
|
|
120
142
|
export const ArticleViewer = ({
|
|
121
143
|
article,
|
|
122
144
|
width,
|
|
@@ -186,13 +208,17 @@ export const ArticleViewer = ({
|
|
|
186
208
|
<Grid item key={i}>
|
|
187
209
|
{
|
|
188
210
|
block.type === 'h1' ? (
|
|
189
|
-
<Typography component="h1" sx={{ fontSize: 28, fontWeight: 'bold', m: 0, p: 0 }}>{block.info.text}</Typography>
|
|
211
|
+
<Typography component="h1" sx={{ fontSize: 28, fontWeight: 'bold', m: 0, p: 0 }} style={blockStyleToCSS(block.style)}>{block.info.text}</Typography>
|
|
190
212
|
)
|
|
191
213
|
: block.type === 'h2' ? (
|
|
192
|
-
<Typography component="h2" sx={{ fontSize: 23, m: 0, p: 0 }}>{block.info.text}</Typography>
|
|
214
|
+
<Typography component="h2" sx={{ fontSize: 23, m: 0, p: 0 }} style={blockStyleToCSS(block.style)}>{block.info.text}</Typography>
|
|
193
215
|
)
|
|
194
216
|
: block.type === 'html' ? (
|
|
195
|
-
<div style={{
|
|
217
|
+
<div style={{
|
|
218
|
+
fontSize: 18,
|
|
219
|
+
lineHeight: '25pt',
|
|
220
|
+
...blockStyleToCSS(block.style)
|
|
221
|
+
}}
|
|
196
222
|
className={css`p {
|
|
197
223
|
margin-top: 0;
|
|
198
224
|
margin-bottom: 0;
|
|
@@ -201,15 +227,16 @@ export const ArticleViewer = ({
|
|
|
201
227
|
__html: remove_script_tags(
|
|
202
228
|
block.info.html.replaceAll(/style="*"/g, '')
|
|
203
229
|
)
|
|
204
|
-
}}
|
|
230
|
+
}}
|
|
205
231
|
/>
|
|
206
232
|
)
|
|
207
233
|
: block.type === 'image' ? (
|
|
208
|
-
<img src={block.info.link} alt={''} style={{
|
|
209
|
-
maxWidth: block.info.maxWidth || '100%',
|
|
234
|
+
<img src={block.info.link} alt={block.info.alt || ''} style={{
|
|
235
|
+
maxWidth: block.info.maxWidth || '100%',
|
|
210
236
|
maxHeight: block.info.maxHeight || undefined, // '' => undefined
|
|
211
237
|
height: block.info.height || undefined, // '' => undefined
|
|
212
238
|
width: block.info.width || undefined, // '' => undefined
|
|
239
|
+
...blockStyleToCSS(block.style)
|
|
213
240
|
}} />
|
|
214
241
|
)
|
|
215
242
|
: block.type === 'youtube' ? (
|
|
@@ -254,12 +281,13 @@ export const ArticleViewer = ({
|
|
|
254
281
|
)
|
|
255
282
|
}
|
|
256
283
|
|
|
284
|
+
|
|
257
285
|
export const html_for_article = (article: ManagedContentRecord, options?: { rootWidth?: number }) => {
|
|
258
286
|
const rootWidth = options?.rootWidth || 400
|
|
259
287
|
|
|
260
288
|
const content = (
|
|
261
289
|
(article.blocks ?? [])
|
|
262
|
-
.map((block, i) =>
|
|
290
|
+
.map((block, i) =>
|
|
263
291
|
block.type === 'h1' ? (
|
|
264
292
|
`<h1>${block.info.text}</h1>`
|
|
265
293
|
)
|
|
@@ -268,11 +296,11 @@ export const html_for_article = (article: ManagedContentRecord, options?: { root
|
|
|
268
296
|
)
|
|
269
297
|
: block.type === 'html' ? (
|
|
270
298
|
`<div>${remove_script_tags(remove_script_tags(block.info.html))}</div>`
|
|
271
|
-
)
|
|
299
|
+
)
|
|
272
300
|
: block.type === 'image' ? (
|
|
273
|
-
// wrap with div to supporting centering later
|
|
301
|
+
// wrap with div to supporting centering later
|
|
274
302
|
`<div style="">
|
|
275
|
-
<img src="${block.info.link}" alt={''} style="max-width: ${block.info.maxWidth || '100%'}; max-height: ${block.info.maxHeight || undefined}; height: ${block.info.height || undefined}; width: ${block.info.width || undefined};" />
|
|
303
|
+
<img src="${block.info.link}" alt="${block.info.alt || ''}" style="max-width: ${block.info.maxWidth || '100%'}; max-height: ${block.info.maxHeight || undefined}; height: ${block.info.height || undefined}; width: ${block.info.width || undefined};" />
|
|
276
304
|
</div>`
|
|
277
305
|
)
|
|
278
306
|
: block.type === 'youtube' ? (
|
|
@@ -23,8 +23,8 @@ export const AddressDisplay = ({ value } : { value: Required<FormResponseAnswerA
|
|
|
23
23
|
</Grid>
|
|
24
24
|
)
|
|
25
25
|
|
|
26
|
-
export const ResponseAnswer = ({ formResponse, fieldId, isHTML, answer: a, printing, onImageClick } : {
|
|
27
|
-
answer: FormResponseValueAnswer,
|
|
26
|
+
export const ResponseAnswer = ({ formResponse, fieldId, isHTML, answer: a, printing, onImageClick } : {
|
|
27
|
+
answer: FormResponseValueAnswer,
|
|
28
28
|
formResponse: FormResponse,
|
|
29
29
|
fieldId: string,
|
|
30
30
|
printing?: boolean,
|
|
@@ -57,9 +57,9 @@ export const ResponseAnswer = ({ formResponse, fieldId, isHTML, answer: a, print
|
|
|
57
57
|
)}
|
|
58
58
|
</ol>
|
|
59
59
|
: a.type === 'file'
|
|
60
|
-
? a.value.secureName
|
|
60
|
+
? a.value.secureName
|
|
61
61
|
? <Typography>
|
|
62
|
-
{!printing
|
|
62
|
+
{!printing
|
|
63
63
|
? <DownloadFileIconButton secureName={a.value.secureName} onDownload={url => window.open(url, '_blank')} />
|
|
64
64
|
: (
|
|
65
65
|
<SecureImage secureName={a.value.secureName} onImageClick={onImageClick}
|
|
@@ -74,10 +74,10 @@ export const ResponseAnswer = ({ formResponse, fieldId, isHTML, answer: a, print
|
|
|
74
74
|
: a.type === 'files'
|
|
75
75
|
? a.value.map(file => (
|
|
76
76
|
<Typography key={file.secureName}>
|
|
77
|
-
{!printing
|
|
77
|
+
{!printing
|
|
78
78
|
? <DownloadFileIconButton secureName={file.secureName} onDownload={url => window.open(url, '_blank')} />
|
|
79
79
|
: (
|
|
80
|
-
<SecureImage secureName={file.secureName}
|
|
80
|
+
<SecureImage secureName={file.secureName}
|
|
81
81
|
style={{ maxHeight: 400, maxWidth: 400 }}
|
|
82
82
|
/>
|
|
83
83
|
)
|
|
@@ -308,14 +308,14 @@ export const FormResponseView = ({ showAnswerInline=true, logoURL, enduser, onCl
|
|
|
308
308
|
|
|
309
309
|
<div style={{ flexDirection: "column", display: 'flex', flex: 1 }}>
|
|
310
310
|
{response.responses.map((r, i) => (
|
|
311
|
-
<div key={i} style={{ marginBottom:
|
|
311
|
+
<div key={i} style={{ marginBottom: 36 }}>
|
|
312
312
|
<div style={{ display: 'flex', flex: 1, flexDirection: "row", justifyContent: 'space-between', flexWrap: 'nowrap' }}>
|
|
313
|
-
{r.fieldTitle &&
|
|
313
|
+
{r.fieldTitle &&
|
|
314
314
|
<div style={{ }}>
|
|
315
315
|
<Typography style={{
|
|
316
316
|
fontWeight: 'bold',
|
|
317
317
|
width: (
|
|
318
|
-
showAnswerInline
|
|
318
|
+
showAnswerInline
|
|
319
319
|
? '400px'
|
|
320
320
|
: undefined
|
|
321
321
|
)
|
|
@@ -326,10 +326,10 @@ export const FormResponseView = ({ showAnswerInline=true, logoURL, enduser, onCl
|
|
|
326
326
|
}
|
|
327
327
|
|
|
328
328
|
<div style={{ }}>
|
|
329
|
-
{showAnswerInline && r.answer.type !== 'Question Group'
|
|
329
|
+
{showAnswerInline && r.answer.type !== 'Question Group'
|
|
330
330
|
&& !(typeof r.answer.value === 'string' && r.answer.value.includes('{TELLESCOPE')) // hidden field for matching, not to display
|
|
331
331
|
&& (
|
|
332
|
-
(r.answerIsHTML && typeof r.answer.value === 'string')
|
|
332
|
+
(r.answerIsHTML && typeof r.answer.value === 'string')
|
|
333
333
|
? <div dangerouslySetInnerHTML={{ __html: remove_script_tags(r.answer.value) }} />
|
|
334
334
|
: <ResponseAnswer fieldId={r.fieldId} formResponse={response} answer={r.answer} printing={printing} />
|
|
335
335
|
)
|
|
@@ -339,7 +339,7 @@ export const FormResponseView = ({ showAnswerInline=true, logoURL, enduser, onCl
|
|
|
339
339
|
|
|
340
340
|
{r.fieldDescription
|
|
341
341
|
? (
|
|
342
|
-
<Typography style={{
|
|
342
|
+
<Typography style={{ }}>
|
|
343
343
|
{r.fieldDescription}
|
|
344
344
|
</Typography>
|
|
345
345
|
): r.fieldHtmlDescription
|
|
@@ -349,10 +349,12 @@ export const FormResponseView = ({ showAnswerInline=true, logoURL, enduser, onCl
|
|
|
349
349
|
}} />
|
|
350
350
|
)
|
|
351
351
|
: null
|
|
352
|
-
}
|
|
352
|
+
}
|
|
353
353
|
|
|
354
354
|
{!showAnswerInline &&
|
|
355
|
-
<
|
|
355
|
+
<div style={{ }}>
|
|
356
|
+
<ResponseAnswer answer={r.answer} formResponse={response} fieldId={r.fieldId} />
|
|
357
|
+
</div>
|
|
356
358
|
}
|
|
357
359
|
</div>
|
|
358
360
|
)
|
package/src/Forms/forms.tsx
CHANGED
|
@@ -6,7 +6,7 @@ import { AddToDatabaseProps, AddressInput, AllergiesInput, AppointmentBookingInp
|
|
|
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"
|
|
9
|
-
import { field_can_autosubmit, form_response_value_to_string, formatted_date, object_is_empty, objects_equivalent, read_local_storage, remove_script_tags, responses_satisfy_conditions, truncate_string } from "@tellescope/utilities"
|
|
9
|
+
import { calculate_form_scoring, field_can_autosubmit, form_response_value_to_string, formatted_date, object_is_empty, objects_equivalent, read_local_storage, remove_script_tags, responses_satisfy_conditions, truncate_string } from "@tellescope/utilities"
|
|
10
10
|
import { Divider } from "@mui/material"
|
|
11
11
|
|
|
12
12
|
export const TellescopeFormContainer = ({ businessId, organizationIds, ...props } : {
|
|
@@ -315,7 +315,7 @@ export const QuestionForField = ({
|
|
|
315
315
|
<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} />
|
|
316
316
|
)
|
|
317
317
|
: field.type === 'Stripe' ? (
|
|
318
|
-
<Stripe field={field} value={value.answer.value as string} onChange={onFieldChange as ChangeHandler<any>} setCustomerId={setCustomerId} form={form} />
|
|
318
|
+
<Stripe enduserId={enduserId} field={field} value={value.answer.value as string} onChange={onFieldChange as ChangeHandler<any>} setCustomerId={setCustomerId} form={form} />
|
|
319
319
|
)
|
|
320
320
|
: field.type === 'Chargebee' ? (
|
|
321
321
|
<Chargebee field={field} value={value.answer.value as any} onChange={onFieldChange as ChangeHandler<'Chargebee'>} setCustomerId={setCustomerId} form={form} />
|
|
@@ -574,6 +574,16 @@ export const TellescopeSingleQuestionFlow: typeof TellescopeForm = ({
|
|
|
574
574
|
|
|
575
575
|
const numRemainingPages = getNumberOfRemainingPages()
|
|
576
576
|
|
|
577
|
+
// Calculate current score if real-time scoring is enabled
|
|
578
|
+
const currentScores = useMemo(() => {
|
|
579
|
+
if (!form?.realTimeScoring || !form.scoring?.length) return null
|
|
580
|
+
|
|
581
|
+
return calculate_form_scoring({
|
|
582
|
+
response: { responses },
|
|
583
|
+
form: { scoring: form.scoring }
|
|
584
|
+
})
|
|
585
|
+
}, [form?.realTimeScoring, form?.scoring, responses])
|
|
586
|
+
|
|
577
587
|
if (!(currentValue && currentFileValue)) return <></>
|
|
578
588
|
return (
|
|
579
589
|
submitted
|
|
@@ -590,7 +600,7 @@ export const TellescopeSingleQuestionFlow: typeof TellescopeForm = ({
|
|
|
590
600
|
handleDatabaseSelect={handleDatabaseSelect}
|
|
591
601
|
setCustomerId={setCustomerId}
|
|
592
602
|
repeats={repeats} onRepeatsChange={setRepeats}
|
|
593
|
-
value={currentValue} file={currentFileValue}
|
|
603
|
+
value={currentValue} file={currentFileValue}
|
|
594
604
|
customInputs={customInputs}
|
|
595
605
|
onAddFile={onAddFile} onFieldChange={onFieldChange}
|
|
596
606
|
responses={responses} selectedFiles={selectedFiles}
|
|
@@ -658,14 +668,49 @@ export const TellescopeSingleQuestionFlow: typeof TellescopeForm = ({
|
|
|
658
668
|
}
|
|
659
669
|
</Flex>
|
|
660
670
|
|
|
661
|
-
{!customization?.hideProgressBar &&
|
|
662
|
-
<Progress
|
|
663
|
-
numerator={currentPageIndex + (validateCurrentField() ? 0 : 1)}
|
|
664
|
-
denominator={currentPageIndex + 1 + numRemainingPages}
|
|
671
|
+
{!customization?.hideProgressBar &&
|
|
672
|
+
<Progress
|
|
673
|
+
numerator={currentPageIndex + (validateCurrentField() ? 0 : 1)}
|
|
674
|
+
denominator={currentPageIndex + 1 + numRemainingPages}
|
|
665
675
|
style={{ marginTop: '15px' }}
|
|
666
676
|
/>
|
|
667
677
|
}
|
|
668
678
|
|
|
679
|
+
{/* Real-time scoring display */}
|
|
680
|
+
{currentScores && currentScores.length > 0 && (
|
|
681
|
+
<Flex style={{ marginTop: 10, marginBottom: 5, width: '100%' }}>
|
|
682
|
+
{currentScores.map((score, index) => (
|
|
683
|
+
<Flex key={index} style={{
|
|
684
|
+
padding: '10px 14px',
|
|
685
|
+
backgroundColor: '#f8f9fa',
|
|
686
|
+
borderRadius: 8,
|
|
687
|
+
border: `1px solid ${theme?.themeColor || PRIMARY_HEX}20`,
|
|
688
|
+
marginRight: index < currentScores.length - 1 ? 12 : 0,
|
|
689
|
+
minWidth: 120,
|
|
690
|
+
flexDirection: 'column',
|
|
691
|
+
alignItems: 'center'
|
|
692
|
+
}}>
|
|
693
|
+
<Typography style={{
|
|
694
|
+
fontSize: 12,
|
|
695
|
+
fontWeight: 'medium',
|
|
696
|
+
textAlign: 'center',
|
|
697
|
+
lineHeight: 1.2,
|
|
698
|
+
marginBottom: 4
|
|
699
|
+
}}>
|
|
700
|
+
{score.title}
|
|
701
|
+
</Typography>
|
|
702
|
+
<Typography style={{
|
|
703
|
+
fontWeight: 'bold',
|
|
704
|
+
color: theme?.themeColor || PRIMARY_HEX,
|
|
705
|
+
fontSize: 18
|
|
706
|
+
}}>
|
|
707
|
+
{score.value}
|
|
708
|
+
</Typography>
|
|
709
|
+
</Flex>
|
|
710
|
+
))}
|
|
711
|
+
</Flex>
|
|
712
|
+
)}
|
|
713
|
+
|
|
669
714
|
<Typography color="error" style={{ alignText: 'center', marginTop: 3 }}>
|
|
670
715
|
{submitErrorMessage}
|
|
671
716
|
</Typography>
|
package/src/Forms/hooks.tsx
CHANGED
|
@@ -364,6 +364,9 @@ interface UseTellescopeFormOptions {
|
|
|
364
364
|
enduser?: Partial<Enduser>,
|
|
365
365
|
isPublicForm?: boolean,
|
|
366
366
|
dontAutoadvance?: boolean,
|
|
367
|
+
groupId?: string,
|
|
368
|
+
groupInstance?: string,
|
|
369
|
+
groupPosition?: number,
|
|
367
370
|
}
|
|
368
371
|
|
|
369
372
|
const OrganizationThemeContext = createContext(null as any as {
|
|
@@ -512,7 +515,7 @@ const shouldCallout = (field: FormField | undefined, value: FormResponseValueAns
|
|
|
512
515
|
|
|
513
516
|
export type Response = FormResponseValue & { touched: boolean, includeInSubmit: boolean, field: FormField }
|
|
514
517
|
export type FileResponse = { fieldId: string, fieldTitle: string, blobs?: FileBlob[] }
|
|
515
|
-
export const useTellescopeForm = ({ dontAutoadvance, isPublicForm, form, urlLogicValue, customization, carePlanId, calendarEventId, context, ga4measurementId, rootResponseId, parentResponseId, accessCode, existingResponses, automationStepId, enduserId, formResponseId, fields, isInternalNote, formTitle, submitRedirectURL,enduser }: UseTellescopeFormOptions) => {
|
|
518
|
+
export const useTellescopeForm = ({ dontAutoadvance, isPublicForm, form, urlLogicValue, customization, carePlanId, calendarEventId, context, ga4measurementId, rootResponseId, parentResponseId, accessCode, existingResponses, automationStepId, enduserId, formResponseId, fields, isInternalNote, formTitle, submitRedirectURL, enduser, groupId, groupInstance, groupPosition }: UseTellescopeFormOptions) => {
|
|
516
519
|
const { amPm, hoursAmPm, minutes } = get_time_values(new Date())
|
|
517
520
|
|
|
518
521
|
const root = useTreeForFormFields(fields)
|
|
@@ -1139,6 +1142,7 @@ export const useTellescopeForm = ({ dontAutoadvance, isPublicForm, form, urlLogi
|
|
|
1139
1142
|
|
|
1140
1143
|
const handleFileUpload = useCallback(async (blob: FileBlob, fieldId: string) => {
|
|
1141
1144
|
const responseIndex = responses.findIndex(f => f.fieldId === fieldId)
|
|
1145
|
+
const field = fields.find(f => f.id === fieldId)
|
|
1142
1146
|
const result: FormResponseAnswerFileValue = { name: blob.name, secureName: '' }
|
|
1143
1147
|
const { secureName } = await handleUpload(
|
|
1144
1148
|
{
|
|
@@ -1146,7 +1150,8 @@ export const useTellescopeForm = ({ dontAutoadvance, isPublicForm, form, urlLogi
|
|
|
1146
1150
|
size: blob.size,
|
|
1147
1151
|
type: blob.type,
|
|
1148
1152
|
enduserId,
|
|
1149
|
-
|
|
1153
|
+
hiddenFromEnduser: field?.options?.hideFromPortal,
|
|
1154
|
+
},
|
|
1150
1155
|
blob
|
|
1151
1156
|
)
|
|
1152
1157
|
|
|
@@ -1160,7 +1165,7 @@ export const useTellescopeForm = ({ dontAutoadvance, isPublicForm, form, urlLogi
|
|
|
1160
1165
|
} else {
|
|
1161
1166
|
responses[responseIndex].answer.value = { ...result, type: blob.type, secureName, name: result.name ?? '' }
|
|
1162
1167
|
}
|
|
1163
|
-
}, [responses, handleUpload])
|
|
1168
|
+
}, [responses, handleUpload, fields])
|
|
1164
1169
|
|
|
1165
1170
|
const submit = useCallback(async (options?: { onPreRedirect?: () => void, onFileUploadsDone?: () => void, onSuccess?: (r: FormResponse) => void, includedFieldIds?: string[], otherEnduserIds?: string[], onBulkErrors?: (errors: { enduserId: string, message: string }[]) => void }) => {
|
|
1166
1171
|
setSubmitErrorMessage('')
|
|
@@ -1230,13 +1235,16 @@ export const useTellescopeForm = ({ dontAutoadvance, isPublicForm, form, urlLogi
|
|
|
1230
1235
|
accessCode : (
|
|
1231
1236
|
accessCode
|
|
1232
1237
|
|| (await (
|
|
1233
|
-
session as any as Session).api.form_responses.prepare_form_response({
|
|
1238
|
+
session as any as Session).api.form_responses.prepare_form_response({
|
|
1234
1239
|
formId, enduserId: eId, isInternalNote, title: formTitle,
|
|
1235
1240
|
parentResponseId, rootResponseId,
|
|
1236
|
-
|
|
1237
|
-
carePlanId,
|
|
1241
|
+
|
|
1242
|
+
carePlanId,
|
|
1238
1243
|
context,
|
|
1239
1244
|
calendarEventId,
|
|
1245
|
+
groupId,
|
|
1246
|
+
groupInstance,
|
|
1247
|
+
groupPosition,
|
|
1240
1248
|
})
|
|
1241
1249
|
).accessCode
|
|
1242
1250
|
),
|
package/src/Forms/inputs.tsx
CHANGED
|
@@ -1605,7 +1605,7 @@ export const MultipleChoiceInput = ({ field, form, value: _value, onChange }: Fo
|
|
|
1605
1605
|
)
|
|
1606
1606
|
}
|
|
1607
1607
|
|
|
1608
|
-
export const StripeInput = ({ field, value, onChange, setCustomerId }: FormInputProps<'Stripe'> & {
|
|
1608
|
+
export const StripeInput = ({ field, value, onChange, setCustomerId, enduserId }: FormInputProps<'Stripe'> & {
|
|
1609
1609
|
setCustomerId: React.Dispatch<React.SetStateAction<string | undefined>>,
|
|
1610
1610
|
}) => {
|
|
1611
1611
|
const session = useResolvedSession()
|
|
@@ -1625,7 +1625,7 @@ export const StripeInput = ({ field, value, onChange, setCustomerId }: FormInput
|
|
|
1625
1625
|
}
|
|
1626
1626
|
fetchRef.current = true
|
|
1627
1627
|
|
|
1628
|
-
session.api.form_responses.stripe_details({ fieldId: field.id })
|
|
1628
|
+
session.api.form_responses.stripe_details({ fieldId: field.id, enduserId })
|
|
1629
1629
|
.then(({ clientSecret, publishableKey, stripeAccount, businessName, customerId, isCheckout, answerText }) => {
|
|
1630
1630
|
setAnswertext(answerText || '')
|
|
1631
1631
|
setIsCheckout(!!isCheckout)
|
|
@@ -1640,7 +1640,7 @@ export const StripeInput = ({ field, value, onChange, setCustomerId }: FormInput
|
|
|
1640
1640
|
setError(e.message)
|
|
1641
1641
|
}
|
|
1642
1642
|
})
|
|
1643
|
-
}, [session, value, field.id])
|
|
1643
|
+
}, [session, value, field.id, enduserId])
|
|
1644
1644
|
|
|
1645
1645
|
const cost = (
|
|
1646
1646
|
(field.options?.productIds || []).map(id => findProduct(id, { batch: false })) // seems to be having issues with bulk read
|
package/src/state.tsx
CHANGED
|
@@ -887,32 +887,29 @@ export const useListStateHook = <T extends { id: string | number }, ADD extends
|
|
|
887
887
|
// prevent frequent refetches
|
|
888
888
|
if (value && options?.reload && didFetch('findById' + modelName + id, true)) return value
|
|
889
889
|
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
batchRef.current.nextBatch.push(id.toString()) // fetch in next batch if currently fetching
|
|
898
|
-
} else {
|
|
899
|
-
batchRef.current.ids.push(id.toString())
|
|
900
|
-
}
|
|
890
|
+
// Handle batch requests separately - always allow queueing for batch
|
|
891
|
+
if (options?.batch) {
|
|
892
|
+
if (!batchRef.current.nextBatch.includes(id.toString()) && !batchRef.current.ids.includes(id.toString())) {
|
|
893
|
+
if (batchRef.current.fetching) {
|
|
894
|
+
batchRef.current.nextBatch.push(id.toString()) // fetch in next batch if currently fetching
|
|
895
|
+
} else {
|
|
896
|
+
batchRef.current.ids.push(id.toString())
|
|
901
897
|
}
|
|
902
|
-
} else {
|
|
903
|
-
findOne?.(id.toString())
|
|
904
|
-
.then(found => {
|
|
905
|
-
// prevent unnecessary re-renders by calling addLocalElement, when the exact value already exists
|
|
906
|
-
const existingUnchanged = value_is_loaded(state) && state.value.find(v => v.id === id && objects_equivalent(v, found))
|
|
907
|
-
if (existingUnchanged) return
|
|
908
|
-
|
|
909
|
-
addLocalElement(found, { replaceIfMatch: true })
|
|
910
|
-
})
|
|
911
|
-
.catch(e => {
|
|
912
|
-
setFetched('recordNotFound' + modelName + id, true) // mark record not found for id
|
|
913
|
-
console.error(e)
|
|
914
|
-
})
|
|
915
898
|
}
|
|
899
|
+
} else if (options?.reload || (value === undefined && !didFetch('findById' + modelName + id))) {
|
|
900
|
+
setFetched('findById' + modelName + id, true) // prevent multiple API calls
|
|
901
|
+
findOne?.(id.toString())
|
|
902
|
+
.then(found => {
|
|
903
|
+
// prevent unnecessary re-renders by calling addLocalElement, when the exact value already exists
|
|
904
|
+
const existingUnchanged = value_is_loaded(state) && state.value.find(v => v.id === id && objects_equivalent(v, found))
|
|
905
|
+
if (existingUnchanged) return
|
|
906
|
+
|
|
907
|
+
addLocalElement(found, { replaceIfMatch: true })
|
|
908
|
+
})
|
|
909
|
+
.catch(e => {
|
|
910
|
+
setFetched('recordNotFound' + modelName + id, true) // mark record not found for id
|
|
911
|
+
console.error(e)
|
|
912
|
+
})
|
|
916
913
|
}
|
|
917
914
|
|
|
918
915
|
return value
|
|
@@ -931,30 +928,61 @@ export const useListStateHook = <T extends { id: string | number }, ADD extends
|
|
|
931
928
|
}
|
|
932
929
|
|
|
933
930
|
batchRef.current.fetching = true
|
|
931
|
+
const currentBatchIds = batchRef.current.ids.slice(0, BULK_READ_DEFAULT_LIMIT)
|
|
932
|
+
|
|
933
|
+
// Filter out IDs that are already loaded to prevent unnecessary fetching
|
|
934
|
+
const idsToFetch = currentBatchIds.filter(id => {
|
|
935
|
+
const alreadyLoaded = state.status === LoadingStatus.Loaded &&
|
|
936
|
+
state.value.find(v => v.id.toString() === id.toString())
|
|
937
|
+
return !alreadyLoaded
|
|
938
|
+
})
|
|
939
|
+
|
|
940
|
+
// Only make API call if there are actually IDs to fetch
|
|
941
|
+
if (idsToFetch.length === 0) {
|
|
942
|
+
// Mark all IDs as processed even though they were already loaded
|
|
943
|
+
currentBatchIds.forEach(id => {
|
|
944
|
+
setFetched('findById' + modelName + id, true)
|
|
945
|
+
})
|
|
946
|
+
|
|
947
|
+
// Continue to next batch
|
|
948
|
+
batchRef.current.ids = batchRef.current.nextBatch
|
|
949
|
+
batchRef.current.nextBatch = []
|
|
950
|
+
batchRef.current.fetching = false
|
|
951
|
+
return
|
|
952
|
+
}
|
|
953
|
+
|
|
934
954
|
findByIds({
|
|
935
|
-
ids:
|
|
955
|
+
ids: idsToFetch,
|
|
936
956
|
})
|
|
937
957
|
.then(({ matches }) => {
|
|
938
|
-
if (matches.length) {
|
|
958
|
+
if (matches.length) {
|
|
939
959
|
addLocalElements(matches, { replaceIfMatch: true })
|
|
940
|
-
options?.onBulkRead?.(matches)
|
|
960
|
+
options?.onBulkRead?.(matches)
|
|
941
961
|
}
|
|
962
|
+
// Mark all fetched IDs as complete (both successful and failed)
|
|
963
|
+
currentBatchIds.forEach(id => {
|
|
964
|
+
setFetched('findById' + modelName + id, true)
|
|
965
|
+
})
|
|
942
966
|
})
|
|
943
967
|
.catch(err => {
|
|
944
968
|
console.error(err)
|
|
969
|
+
// Mark failed IDs as not found
|
|
970
|
+
currentBatchIds.forEach(id => {
|
|
971
|
+
setFetched('recordNotFound' + modelName + id, true)
|
|
972
|
+
})
|
|
945
973
|
})
|
|
946
974
|
.finally(() => {
|
|
947
975
|
// ensure we make progress to prevent looping on an error
|
|
948
976
|
batchRef.current.ids = batchRef.current.nextBatch
|
|
949
977
|
batchRef.current.nextBatch = []
|
|
950
|
-
|
|
978
|
+
|
|
951
979
|
// allow next fetch
|
|
952
980
|
batchRef.current.fetching = false
|
|
953
981
|
})
|
|
954
982
|
}, 333)
|
|
955
983
|
|
|
956
984
|
return () => { clearInterval(i) }
|
|
957
|
-
}, [findByIds, addLocalElements, options?.onBulkRead])
|
|
985
|
+
}, [findByIds, addLocalElements, options?.onBulkRead, setFetched, modelName])
|
|
958
986
|
|
|
959
987
|
const findByFilter: ListUpdateMethods<T, ADD>['findByFilter'] = useCallback((filter, options) => {
|
|
960
988
|
const loadFilter = options?.loadFilter
|