@tellescope/react-components 1.245.1 → 1.246.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tellescope/react-components",
3
- "version": "1.245.1",
3
+ "version": "1.246.1",
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.245.1",
55
- "@tellescope/sdk": "1.245.1",
56
- "@tellescope/types-client": "1.245.1",
57
- "@tellescope/types-models": "1.245.1",
58
- "@tellescope/types-utilities": "1.245.1",
59
- "@tellescope/utilities": "1.245.1",
60
- "@tellescope/validation": "1.245.1",
54
+ "@tellescope/constants": "1.246.1",
55
+ "@tellescope/sdk": "1.246.1",
56
+ "@tellescope/types-client": "1.246.1",
57
+ "@tellescope/types-models": "1.246.1",
58
+ "@tellescope/types-utilities": "1.246.1",
59
+ "@tellescope/utilities": "1.246.1",
60
+ "@tellescope/validation": "1.246.1",
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": "3484805db8e2d27d68b6e04c679ead95a06eed94",
87
+ "gitHead": "c18252ff9abffb701c1f4228ce2000acce3f660c",
88
88
  "publishConfig": {
89
89
  "access": "public"
90
90
  }
@@ -5,7 +5,7 @@ import { ChangeHandler, FormInputs } from "./types"
5
5
  import { AddToDatabaseProps, AddressInput, AllergiesInput, AppointmentBookingInput, BelugaPatientPreferenceInput, BridgeEligibilityInput, CandidEligibilityInput, ChargeebeeInput, ConditionsInput, DatabaseSelectInput, DateInput, DateStringInput, DropdownInput, EmailInput, EmotiiInput, FileInput, FilesInput, HeightInput, HiddenValueInput, InsuranceInput, LanguageSelect, MedicationsInput, MultipleChoiceInput, NumberInput, PharmacySearchInput, PhoneInput, Progress, RankingInput, RatingInput, RedirectInput, RelatedContactsInput, RichTextInput, SignatureInput, StringInput, StringLongInput, StripeInput, TableInput, TimeInput, TimezoneInput, defaultButtonStyles } from "./inputs"
6
6
  import { PRIMARY_HEX } from "@tellescope/constants"
7
7
  import { FormResponse, FormField, Form, Enduser } from "@tellescope/types-client"
8
- import { FormResponseAnswerFileValue, OrganizationTheme } from "@tellescope/types-models"
8
+ import { FormResponseAnswerFileValue, OrganizationTheme, HistoricalDataSource } from "@tellescope/types-models"
9
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
 
@@ -236,7 +236,7 @@ export const QuestionForField = ({
236
236
  <div style={{ marginTop: 15 }}></div>
237
237
  }
238
238
 
239
- <Description field={field} style={{ fontSize: 16 }} />
239
+ <Description field={field} style={{ fontSize: 16 }} enduserId={enduserId} />
240
240
 
241
241
  {feedback.length > 0 &&
242
242
  <Flex column style={{ marginBottom: 11, marginTop: 3, }}>
@@ -1017,20 +1017,168 @@ export const UpdateResponse = ({
1017
1017
  )
1018
1018
  }
1019
1019
 
1020
- export const Description = ({ field, color="primary", style } : { field: FormField, color?: string } & Styled) => {
1021
- if (!field.htmlDescription && field.description) {
1020
+ const HistoricalDataSection = ({ sources, enduserId } : { sources: HistoricalDataSource[], enduserId: string }) => {
1021
+ const session = useSession({ throwIfMissingContext: false })
1022
+ const [observations, setObservations] = useState<any[]>([])
1023
+ const [medications, setMedications] = useState<any[]>([])
1024
+ const [loading, setLoading] = useState(true)
1025
+ const [error, setError] = useState('')
1026
+
1027
+ useEffect(() => {
1028
+ if (!session) return
1029
+
1030
+ const loadData = async () => {
1031
+ setLoading(true)
1032
+ setError('')
1033
+ try {
1034
+ const promises: Promise<void>[] = []
1035
+
1036
+ for (const source of sources) {
1037
+ if (source.type === 'Observations') {
1038
+ promises.push(
1039
+ session.api.enduser_observations.getSome({
1040
+ filter: { enduserId, ...source.filter },
1041
+ limit: source.limit,
1042
+ })
1043
+ .then((obs: any[]) => setObservations(obs))
1044
+ )
1045
+ } else if (source.type === 'Medications') {
1046
+ promises.push(
1047
+ session.api.enduser_medications.getSome({
1048
+ filter: { enduserId, status: { _ne: 'draft' }, ...source.filter },
1049
+ limit: source.limit,
1050
+ })
1051
+ .then((meds: any[]) => setMedications(meds))
1052
+ )
1053
+ }
1054
+ }
1055
+
1056
+ await Promise.all(promises)
1057
+ } catch (err: any) {
1058
+ setError(err?.message || 'Failed to load historical data')
1059
+ } finally {
1060
+ setLoading(false)
1061
+ }
1062
+ }
1063
+
1064
+ loadData()
1065
+ }, [session, enduserId, sources])
1066
+
1067
+ if (!session) return null
1068
+
1069
+ if (loading) {
1070
+ return (
1071
+ <Flex style={{ padding: 10, justifyContent: 'center' }}>
1072
+ <CircularProgress size={24} />
1073
+ </Flex>
1074
+ )
1075
+ }
1076
+
1077
+ if (error) {
1022
1078
  return (
1079
+ <Typography color="error" style={{ padding: 10 }}>
1080
+ {error}
1081
+ </Typography>
1082
+ )
1083
+ }
1084
+
1085
+ const hasObservations = sources.some(s => s.type === 'Observations')
1086
+ const hasMedications = sources.some(s => s.type === 'Medications')
1087
+
1088
+ return (
1089
+ <div style={{ marginTop: 10 }}>
1090
+ {hasObservations && (
1091
+ <div style={{ marginBottom: 15 }}>
1092
+ <Typography style={{ fontWeight: 'bold', marginBottom: 5 }}>Observations</Typography>
1093
+ {observations.length === 0 ? (
1094
+ <Typography style={{ fontStyle: 'italic', color: '#888' }}>No observations found</Typography>
1095
+ ) : (
1096
+ <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 14 }}>
1097
+ <thead>
1098
+ <tr style={{ borderBottom: '2px solid #ccc', textAlign: 'left' }}>
1099
+ <th style={{ padding: '6px 8px' }}>Date</th>
1100
+ <th style={{ padding: '6px 8px' }}>Type</th>
1101
+ <th style={{ padding: '6px 8px' }}>Value</th>
1102
+ <th style={{ padding: '6px 8px' }}>Category</th>
1103
+ <th style={{ padding: '6px 8px' }}>Status</th>
1104
+ </tr>
1105
+ </thead>
1106
+ <tbody>
1107
+ {observations.map((obs: any) => (
1108
+ <tr key={obs.id} style={{ borderBottom: '1px solid #eee' }}>
1109
+ <td style={{ padding: '6px 8px' }}>{obs.timestamp ? formatted_date(new Date(obs.timestamp)) : '-'}</td>
1110
+ <td style={{ padding: '6px 8px' }}>{obs.type || obs.code || '-'}</td>
1111
+ <td style={{ padding: '6px 8px' }}>
1112
+ {obs.measurement ? `${obs.measurement.value} ${obs.measurement.unit}` : obs.qualitativeResult || '-'}
1113
+ </td>
1114
+ <td style={{ padding: '6px 8px' }}>{obs.category || '-'}</td>
1115
+ <td style={{ padding: '6px 8px' }}>{obs.status || '-'}</td>
1116
+ </tr>
1117
+ ))}
1118
+ </tbody>
1119
+ </table>
1120
+ )}
1121
+ </div>
1122
+ )}
1123
+
1124
+ {hasMedications && (
1125
+ <div style={{ marginBottom: 15 }}>
1126
+ <Typography style={{ fontWeight: 'bold', marginBottom: 5 }}>Medications</Typography>
1127
+ {medications.length === 0 ? (
1128
+ <Typography style={{ fontStyle: 'italic', color: '#888' }}>No medications found</Typography>
1129
+ ) : (
1130
+ <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 14 }}>
1131
+ <thead>
1132
+ <tr style={{ borderBottom: '2px solid #ccc', textAlign: 'left' }}>
1133
+ <th style={{ padding: '6px 8px' }}>Title</th>
1134
+ <th style={{ padding: '6px 8px' }}>Dosage</th>
1135
+ <th style={{ padding: '6px 8px' }}>Prescribed By</th>
1136
+ <th style={{ padding: '6px 8px' }}>Start Date</th>
1137
+ </tr>
1138
+ </thead>
1139
+ <tbody>
1140
+ {medications.map((med: any) => (
1141
+ <tr key={med.id} style={{ borderBottom: '1px solid #eee' }}>
1142
+ <td style={{ padding: '6px 8px' }}>{med.title || '-'}</td>
1143
+ <td style={{ padding: '6px 8px' }}>
1144
+ {med.dosage ? `${med.dosage.value} ${med.dosage.unit}` : '-'}
1145
+ </td>
1146
+ <td style={{ padding: '6px 8px' }}>{med.prescriberName || '-'}</td>
1147
+ <td style={{ padding: '6px 8px' }}>{med.startedTakingAt ? formatted_date(new Date(med.startedTakingAt)) : '-'}</td>
1148
+ </tr>
1149
+ ))}
1150
+ </tbody>
1151
+ </table>
1152
+ )}
1153
+ </div>
1154
+ )}
1155
+ </div>
1156
+ )
1157
+ }
1158
+
1159
+ export const Description = ({ field, color="primary", style, enduserId } : { field: FormField, color?: string, enduserId?: string } & Styled) => {
1160
+ const existingContent = (
1161
+ !field.htmlDescription && field.description ? (
1023
1162
  <Typography color={color as any} style={style}>
1024
1163
  {field.description}
1025
1164
  </Typography>
1026
- )
1027
- }
1028
- if (!field.htmlDescription) return null
1165
+ ) : field.htmlDescription ? (
1166
+ <span dangerouslySetInnerHTML={{
1167
+ __html: remove_script_tags(field.htmlDescription)
1168
+ }} />
1169
+ ) : null
1170
+ )
1029
1171
 
1030
1172
  return (
1031
- <span dangerouslySetInnerHTML={{
1032
- __html: remove_script_tags(field.htmlDescription)
1033
- }} />
1173
+ <>
1174
+ {existingContent}
1175
+ {field.type === 'description' && field.options?.historicalDataSources?.length && enduserId ? (
1176
+ <HistoricalDataSection
1177
+ sources={field.options.historicalDataSources}
1178
+ enduserId={enduserId}
1179
+ />
1180
+ ) : null}
1181
+ </>
1034
1182
  )
1035
1183
  }
1036
1184