@kenyaemr/esm-patient-clinical-view-app 5.4.2-pre.2793 → 5.4.2-pre.2797
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/.turbo/turbo-build.log +4 -4
- package/dist/127.js +1 -1
- package/dist/40.js +1 -1
- package/dist/{189.js → 791.js} +1 -1
- package/dist/791.js.map +1 -0
- package/dist/916.js +1 -1
- package/dist/kenyaemr-esm-patient-clinical-view-app.js +3 -3
- package/dist/kenyaemr-esm-patient-clinical-view-app.js.buildmanifest.json +36 -36
- package/dist/main.js +3 -3
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/config-schema.ts +8 -2
- package/src/maternal-and-child-health/partography/forms/cervical-contractions-form.component.tsx +63 -28
- package/src/maternal-and-child-health/partography/forms/cervix-form.component.tsx +52 -108
- package/src/maternal-and-child-health/partography/forms/drugs-iv-fluids-form.component.tsx +110 -100
- package/src/maternal-and-child-health/partography/forms/fetal-heart-rate-form.component.tsx +7 -7
- package/src/maternal-and-child-health/partography/forms/membrane-amniotic-fluid-form.component.tsx +72 -75
- package/src/maternal-and-child-health/partography/forms/oxytocin-form.component.tsx +14 -19
- package/src/maternal-and-child-health/partography/forms/pulse-bp-form.component.tsx +34 -28
- package/src/maternal-and-child-health/partography/forms/temperature-form.component.tsx +10 -9
- package/src/maternal-and-child-health/partography/forms/time-picker-dropdown.component.tsx +10 -10
- package/src/maternal-and-child-health/partography/forms/urine-test-form.component.tsx +7 -6
- package/src/maternal-and-child-health/partography/graphs/cervical-contractions-graph.component.tsx +51 -61
- package/src/maternal-and-child-health/partography/graphs/drugs-iv-fluids-graph-wrapper.component.tsx +0 -6
- package/src/maternal-and-child-health/partography/graphs/drugs-iv-fluids-graph.component.tsx +3 -3
- package/src/maternal-and-child-health/partography/graphs/fetal-heart-rate-graph.component.tsx +53 -30
- package/src/maternal-and-child-health/partography/graphs/membrane-amniotic-fluid-graph.component.tsx +25 -3
- package/src/maternal-and-child-health/partography/graphs/oxytocin-graph-wrapper.component.tsx +102 -97
- package/src/maternal-and-child-health/partography/graphs/oxytocin-graph.component.tsx +21 -22
- package/src/maternal-and-child-health/partography/graphs/pulse-bp-graph.component.tsx +21 -24
- package/src/maternal-and-child-health/partography/graphs/temperature-graph.component.tsx +23 -26
- package/src/maternal-and-child-health/partography/graphs/urine-test-graph.component.tsx +1 -1
- package/src/maternal-and-child-health/partography/partograph.component.tsx +323 -171
- package/src/maternal-and-child-health/partography/partography-data-form.scss +23 -0
- package/src/maternal-and-child-health/partography/partography.resource.ts +70 -56
- package/src/maternal-and-child-health/partography/partography.scss +65 -6
- package/src/maternal-and-child-health/partography/resources/fetal-heart-rate.resource.ts +5 -10
- package/src/maternal-and-child-health/partography/resources/oxytocin.resource.ts +5 -3
- package/src/maternal-and-child-health/partography/types/index.ts +40 -37
- package/translations/am.json +1 -1
- package/translations/en.json +1 -1
- package/translations/sw.json +1 -1
- package/dist/189.js.map +0 -1
|
@@ -107,13 +107,26 @@ import {
|
|
|
107
107
|
PARTOGRAPHY_CONCEPTS,
|
|
108
108
|
PARTOGRAPHY_ENCOUNTER_TYPES,
|
|
109
109
|
MCH_PARTOGRAPHY_ENCOUNTER_UUID,
|
|
110
|
+
URINE_TEST_TIME_SLOT_CONCEPT,
|
|
110
111
|
getPartographyUnit,
|
|
111
112
|
type OpenMRSResponse,
|
|
112
113
|
type PartographyObservation,
|
|
113
114
|
type PartographyEncounter,
|
|
114
115
|
type PartographyGraphType,
|
|
115
116
|
} from './types';
|
|
116
|
-
import {
|
|
117
|
+
import {
|
|
118
|
+
configSchema,
|
|
119
|
+
type ConfigObject,
|
|
120
|
+
TIME_RESULTS_RETURNED,
|
|
121
|
+
TIME_SAMPLE_COLLECTED,
|
|
122
|
+
EVENT_DESCRIPTION_CONCEPT,
|
|
123
|
+
CONTRACTION_COUNT_CONCEPT,
|
|
124
|
+
UTERINE_CONTRACTIONS_CONCEPT,
|
|
125
|
+
CONTRACTION_LEVEL_MILD_CONCEPT,
|
|
126
|
+
CONTRACTION_LEVEL_MODERATE_CONCEPT,
|
|
127
|
+
CONTRACTION_LEVEL_STRONG_CONCEPT,
|
|
128
|
+
MOULDING_NONE_CONCEPT,
|
|
129
|
+
} from '../../config-schema';
|
|
117
130
|
|
|
118
131
|
export type { PartographyObservation, PartographyEncounter };
|
|
119
132
|
const defaultPartographyConfig = configSchema.partography._default;
|
|
@@ -359,6 +372,7 @@ export async function createPartographyEncounter(
|
|
|
359
372
|
formData: any,
|
|
360
373
|
locationUuid?: string,
|
|
361
374
|
providerUuid?: string,
|
|
375
|
+
signal?: AbortSignal,
|
|
362
376
|
t?: (key: string, fallback?: string) => string,
|
|
363
377
|
): Promise<{ success: boolean; message: string; encounter?: PartographyEncounter }> {
|
|
364
378
|
try {
|
|
@@ -408,7 +422,7 @@ export async function createPartographyEncounter(
|
|
|
408
422
|
location: finalLocationUuid,
|
|
409
423
|
encounterDatetime: encounterDatetime,
|
|
410
424
|
obs: observations,
|
|
411
|
-
encounterType: encounterTypeUuid,
|
|
425
|
+
encounterType: { uuid: encounterTypeUuid },
|
|
412
426
|
};
|
|
413
427
|
|
|
414
428
|
let finalProviderUuid = providerUuid;
|
|
@@ -475,13 +489,12 @@ export async function createPartographyEncounter(
|
|
|
475
489
|
throw new Error('At least one observation is required');
|
|
476
490
|
}
|
|
477
491
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
const response = await openmrsFetch(`${restBaseUrl}/encounter`, {
|
|
492
|
+
const encounterUrl = `${restBaseUrl}/encounter`;
|
|
493
|
+
const response = await openmrsFetch(encounterUrl, {
|
|
482
494
|
method: 'POST',
|
|
483
495
|
headers: { 'Content-Type': 'application/json' },
|
|
484
496
|
body: JSON.stringify(encounterPayload),
|
|
497
|
+
signal: signal, // Add abort signal support
|
|
485
498
|
});
|
|
486
499
|
|
|
487
500
|
if (!response.ok) {
|
|
@@ -651,14 +664,14 @@ function buildObservations(graphType: string, formData: any): any[] {
|
|
|
651
664
|
});
|
|
652
665
|
}
|
|
653
666
|
|
|
654
|
-
// Save hour as a
|
|
667
|
+
// Save hour as a string in the format 'Hour: X' to match backend expectations
|
|
655
668
|
if (formData.hour !== undefined && formData.hour !== '') {
|
|
656
669
|
try {
|
|
657
670
|
const hourValue = parseFloat(formData.hour);
|
|
658
|
-
if (hourValue >= 0 && hourValue <= 24) {
|
|
671
|
+
if (!isNaN(hourValue) && hourValue >= 0 && hourValue <= 24) {
|
|
659
672
|
observations.push({
|
|
660
673
|
concept: PARTOGRAPHY_CONCEPTS['fetal-heart-rate-hour'],
|
|
661
|
-
value: hourValue
|
|
674
|
+
value: `Hour: ${hourValue}`,
|
|
662
675
|
obsDatetime,
|
|
663
676
|
});
|
|
664
677
|
}
|
|
@@ -740,7 +753,7 @@ function buildObservations(graphType: string, formData: any): any[] {
|
|
|
740
753
|
}
|
|
741
754
|
if (formData.timeSlot) {
|
|
742
755
|
observations.push({
|
|
743
|
-
concept:
|
|
756
|
+
concept: URINE_TEST_TIME_SLOT_CONCEPT,
|
|
744
757
|
value: `Time: ${formData.timeSlot}`,
|
|
745
758
|
obsDatetime,
|
|
746
759
|
});
|
|
@@ -821,7 +834,22 @@ function buildObservations(graphType: string, formData: any): any[] {
|
|
|
821
834
|
obsDatetime,
|
|
822
835
|
});
|
|
823
836
|
}
|
|
824
|
-
|
|
837
|
+
|
|
838
|
+
if (formData.timeSlot) {
|
|
839
|
+
observations.push({
|
|
840
|
+
concept: URINE_TEST_TIME_SLOT_CONCEPT,
|
|
841
|
+
value: `Time: ${formData.timeSlot}`,
|
|
842
|
+
obsDatetime,
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
if (formData.timeSampleCollected) {
|
|
847
|
+
observations.push({
|
|
848
|
+
concept: TIME_SAMPLE_COLLECTED,
|
|
849
|
+
value: formData.timeSampleCollected,
|
|
850
|
+
obsDatetime,
|
|
851
|
+
});
|
|
852
|
+
}
|
|
825
853
|
if (formData.eventDescription) {
|
|
826
854
|
observations.push({
|
|
827
855
|
concept: PARTOGRAPHY_CONCEPTS['event-description'],
|
|
@@ -829,10 +857,11 @@ function buildObservations(graphType: string, formData: any): any[] {
|
|
|
829
857
|
obsDatetime,
|
|
830
858
|
});
|
|
831
859
|
}
|
|
860
|
+
|
|
832
861
|
if (formData.timeResultsReturned) {
|
|
833
862
|
observations.push({
|
|
834
|
-
concept:
|
|
835
|
-
value:
|
|
863
|
+
concept: TIME_RESULTS_RETURNED,
|
|
864
|
+
value: formData.timeResultsReturned,
|
|
836
865
|
obsDatetime,
|
|
837
866
|
});
|
|
838
867
|
}
|
|
@@ -853,7 +882,7 @@ function buildObservations(graphType: string, formData: any): any[] {
|
|
|
853
882
|
obsDatetime,
|
|
854
883
|
});
|
|
855
884
|
}
|
|
856
|
-
|
|
885
|
+
|
|
857
886
|
if (formData.route) {
|
|
858
887
|
observations.push({
|
|
859
888
|
concept: PARTOGRAPHY_CONCEPTS['event-description'],
|
|
@@ -934,22 +963,20 @@ export function transformEncounterToChartData(encounters: PartographyEncounter[]
|
|
|
934
963
|
const conceptUuid = obs.concept.uuid;
|
|
935
964
|
let value = null;
|
|
936
965
|
let groupName = '';
|
|
937
|
-
|
|
966
|
+
|
|
938
967
|
if (graphType === 'uterine-contractions') {
|
|
939
|
-
// Numeric value for graphing
|
|
940
968
|
if (conceptUuid === PARTOGRAPHY_CONCEPTS['uterine-contractions']) {
|
|
941
969
|
value = parseFloat(obs.value as string);
|
|
942
970
|
groupName = getGraphTypeDisplayName('uterine-contractions');
|
|
943
971
|
}
|
|
944
|
-
|
|
972
|
+
|
|
945
973
|
const contractionLevelMap: Record<string, string> = {
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
974
|
+
MOULDING_NONE_CONCEPT: 'none',
|
|
975
|
+
CONTRACTION_LEVEL_MILD_CONCEPT: 'mild',
|
|
976
|
+
CONTRACTION_LEVEL_MODERATE_CONCEPT: 'moderate',
|
|
977
|
+
CONTRACTION_LEVEL_STRONG_CONCEPT: 'strong',
|
|
950
978
|
};
|
|
951
979
|
if (contractionLevelMap[conceptUuid]) {
|
|
952
|
-
// For charting, you may want to assign a numeric value for each level
|
|
953
980
|
const contractionLevelValueMap: Record<string, number> = {
|
|
954
981
|
none: 0,
|
|
955
982
|
mild: 1,
|
|
@@ -1025,11 +1052,8 @@ export function transformEncounterToChartData(encounters: PartographyEncounter[]
|
|
|
1025
1052
|
});
|
|
1026
1053
|
|
|
1027
1054
|
return chartData.sort((a, b) => {
|
|
1028
|
-
// Parse time strings (HH:MM format) for comparison
|
|
1029
1055
|
const timeA = a.time.split(':').map(Number);
|
|
1030
1056
|
const timeB = b.time.split(':').map(Number);
|
|
1031
|
-
|
|
1032
|
-
// Convert to minutes for easy comparison
|
|
1033
1057
|
const minutesA = timeA[0] * 60 + (timeA[1] || 0);
|
|
1034
1058
|
const minutesB = timeB[0] * 60 + (timeB[1] || 0);
|
|
1035
1059
|
|
|
@@ -1104,16 +1128,14 @@ export function transformEncounterToTableData(
|
|
|
1104
1128
|
})}`;
|
|
1105
1129
|
|
|
1106
1130
|
if (graphType === 'uterine-contractions') {
|
|
1107
|
-
// Group obs by time for uterine contractions
|
|
1108
1131
|
let timeSlot = '';
|
|
1109
1132
|
let contractionCount = '';
|
|
1110
1133
|
let contractionLevel = 'none';
|
|
1111
1134
|
if (Array.isArray(encounter.obs)) {
|
|
1112
|
-
// Always use the last seen contraction level in the encounter (in case order is not guaranteed)
|
|
1113
1135
|
for (const obs of encounter.obs) {
|
|
1114
1136
|
// Time slot
|
|
1115
1137
|
if (
|
|
1116
|
-
obs.concept.uuid ===
|
|
1138
|
+
obs.concept.uuid === EVENT_DESCRIPTION_CONCEPT &&
|
|
1117
1139
|
typeof obs.value === 'string' &&
|
|
1118
1140
|
obs.value.startsWith('Time:')
|
|
1119
1141
|
) {
|
|
@@ -1122,30 +1144,30 @@ export function transformEncounterToTableData(
|
|
|
1122
1144
|
timeSlot = match[1].trim();
|
|
1123
1145
|
}
|
|
1124
1146
|
}
|
|
1125
|
-
|
|
1126
|
-
if (obs.concept.uuid ===
|
|
1147
|
+
|
|
1148
|
+
if (obs.concept.uuid === CONTRACTION_COUNT_CONCEPT) {
|
|
1127
1149
|
contractionCount = String(obs.value);
|
|
1128
1150
|
}
|
|
1129
1151
|
}
|
|
1130
|
-
|
|
1152
|
+
|
|
1131
1153
|
for (const obs of encounter.obs) {
|
|
1132
|
-
if (obs.concept.uuid ===
|
|
1154
|
+
if (obs.concept.uuid === UTERINE_CONTRACTIONS_CONCEPT) {
|
|
1133
1155
|
let levelUuid = '';
|
|
1134
1156
|
if (typeof obs.value === 'string') {
|
|
1135
1157
|
levelUuid = obs.value;
|
|
1136
1158
|
} else if (obs.value != null && typeof obs.value === 'object' && (obs.value as any)?.uuid) {
|
|
1137
1159
|
levelUuid = (obs.value as any).uuid;
|
|
1138
1160
|
}
|
|
1139
|
-
if (levelUuid ===
|
|
1161
|
+
if (levelUuid === MOULDING_NONE_CONCEPT) {
|
|
1140
1162
|
contractionLevel = 'none';
|
|
1141
1163
|
}
|
|
1142
|
-
if (levelUuid ===
|
|
1164
|
+
if (levelUuid === CONTRACTION_LEVEL_MILD_CONCEPT) {
|
|
1143
1165
|
contractionLevel = 'mild';
|
|
1144
1166
|
}
|
|
1145
|
-
if (levelUuid ===
|
|
1167
|
+
if (levelUuid === CONTRACTION_LEVEL_MODERATE_CONCEPT) {
|
|
1146
1168
|
contractionLevel = 'moderate';
|
|
1147
1169
|
}
|
|
1148
|
-
if (levelUuid ===
|
|
1170
|
+
if (levelUuid === CONTRACTION_LEVEL_STRONG_CONCEPT) {
|
|
1149
1171
|
contractionLevel = 'strong';
|
|
1150
1172
|
}
|
|
1151
1173
|
}
|
|
@@ -1162,7 +1184,6 @@ export function transformEncounterToTableData(
|
|
|
1162
1184
|
});
|
|
1163
1185
|
}
|
|
1164
1186
|
} else {
|
|
1165
|
-
// ...existing code for other graph types...
|
|
1166
1187
|
encounter.obs.forEach((obs, obsIndex) => {
|
|
1167
1188
|
try {
|
|
1168
1189
|
if (typeof obs.value === 'string') {
|
|
@@ -1266,11 +1287,9 @@ export function transformEncounterToTableData(
|
|
|
1266
1287
|
);
|
|
1267
1288
|
}
|
|
1268
1289
|
|
|
1269
|
-
// Hook to fetch and manage fetal heart rate data from OpenMRS
|
|
1270
1290
|
export function useFetalHeartRateData(patientUuid: string) {
|
|
1271
1291
|
const fetcher = (url: string) => openmrsFetch(url).then((res) => res.json());
|
|
1272
1292
|
|
|
1273
|
-
// Fetch encounters instead of just observations to get all related data
|
|
1274
1293
|
const { data, error, isLoading, mutate } = useSWR(
|
|
1275
1294
|
patientUuid
|
|
1276
1295
|
? `${restBaseUrl}/encounter?patient=${patientUuid}&encounterType=${MCH_PARTOGRAPHY_ENCOUNTER_UUID}&v=full&limit=100&order=desc`
|
|
@@ -1296,7 +1315,6 @@ export function useFetalHeartRateData(patientUuid: string) {
|
|
|
1296
1315
|
continue;
|
|
1297
1316
|
}
|
|
1298
1317
|
|
|
1299
|
-
// Look for fetal heart rate observations in this encounter
|
|
1300
1318
|
const fetalHeartRateObs = encounter.obs.find(
|
|
1301
1319
|
(obs) => obs.concept.uuid === PARTOGRAPHY_CONCEPTS['fetal-heart-rate'],
|
|
1302
1320
|
);
|
|
@@ -1304,11 +1322,9 @@ export function useFetalHeartRateData(patientUuid: string) {
|
|
|
1304
1322
|
if (fetalHeartRateObs) {
|
|
1305
1323
|
const encounterDatetime = new Date(encounter.encounterDatetime);
|
|
1306
1324
|
|
|
1307
|
-
// Look for hour and time observations with text format
|
|
1308
1325
|
let hour = 0;
|
|
1309
1326
|
let time = '';
|
|
1310
1327
|
|
|
1311
|
-
// Find hour observation (saved as "Hour: X.X")
|
|
1312
1328
|
const hourObs = encounter.obs.find(
|
|
1313
1329
|
(obs) =>
|
|
1314
1330
|
obs.concept.uuid === PARTOGRAPHY_CONCEPTS['fetal-heart-rate-hour'] &&
|
|
@@ -1317,7 +1333,6 @@ export function useFetalHeartRateData(patientUuid: string) {
|
|
|
1317
1333
|
obs.value.startsWith('Hour:'),
|
|
1318
1334
|
);
|
|
1319
1335
|
|
|
1320
|
-
// Find time observation (saved as "Time: XX:XX")
|
|
1321
1336
|
const timeObs = encounter.obs.find(
|
|
1322
1337
|
(obs) =>
|
|
1323
1338
|
obs.concept.uuid === PARTOGRAPHY_CONCEPTS['fetal-heart-rate-time'] &&
|
|
@@ -1326,7 +1341,6 @@ export function useFetalHeartRateData(patientUuid: string) {
|
|
|
1326
1341
|
obs.value.startsWith('Time:'),
|
|
1327
1342
|
);
|
|
1328
1343
|
|
|
1329
|
-
// Parse hour from text format "Hour: 2.5"
|
|
1330
1344
|
if (hourObs && typeof hourObs.value === 'string') {
|
|
1331
1345
|
const hourMatch = hourObs.value.match(/Hour:\s*([0-9.]+)/);
|
|
1332
1346
|
if (hourMatch) {
|
|
@@ -1334,7 +1348,6 @@ export function useFetalHeartRateData(patientUuid: string) {
|
|
|
1334
1348
|
}
|
|
1335
1349
|
}
|
|
1336
1350
|
|
|
1337
|
-
// Parse time from text format "Time: 03:15"
|
|
1338
1351
|
if (timeObs && typeof timeObs.value === 'string') {
|
|
1339
1352
|
const timeMatch = timeObs.value.match(/Time:\s*(.+)/);
|
|
1340
1353
|
if (timeMatch) {
|
|
@@ -1412,11 +1425,19 @@ export async function saveDrugOrderData(
|
|
|
1412
1425
|
route: string;
|
|
1413
1426
|
frequency: string;
|
|
1414
1427
|
},
|
|
1428
|
+
locationUuid?: string,
|
|
1429
|
+
providerUuid?: string,
|
|
1430
|
+
signal?: AbortSignal,
|
|
1415
1431
|
) {
|
|
1416
1432
|
try {
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1433
|
+
const result = await createPartographyEncounter(
|
|
1434
|
+
patientUuid,
|
|
1435
|
+
'drugs-fluids',
|
|
1436
|
+
formData,
|
|
1437
|
+
locationUuid,
|
|
1438
|
+
providerUuid,
|
|
1439
|
+
signal,
|
|
1440
|
+
);
|
|
1420
1441
|
return result;
|
|
1421
1442
|
} catch (error) {
|
|
1422
1443
|
console.error('Error saving drug order data:', error);
|
|
@@ -1427,9 +1448,7 @@ export async function saveDrugOrderData(
|
|
|
1427
1448
|
}
|
|
1428
1449
|
}
|
|
1429
1450
|
|
|
1430
|
-
// Function to fetch drug orders for a patient
|
|
1431
1451
|
export function useDrugOrders(patientUuid: string) {
|
|
1432
|
-
// Remove the problematic sort parameter that's causing 500 error
|
|
1433
1452
|
const apiUrl = patientUuid
|
|
1434
1453
|
? `${restBaseUrl}/order?patient=${patientUuid}&orderType=131168f4-15f5-102d-96e4-000c29c2a5d7&v=full&limit=50`
|
|
1435
1454
|
: null;
|
|
@@ -1440,23 +1459,18 @@ export function useDrugOrders(patientUuid: string) {
|
|
|
1440
1459
|
const responseData = data?.data as any;
|
|
1441
1460
|
|
|
1442
1461
|
if (!responseData?.results || !Array.isArray(responseData.results)) {
|
|
1443
|
-
// console.log('Drug Orders: No results found');
|
|
1444
1462
|
return [];
|
|
1445
1463
|
}
|
|
1446
1464
|
|
|
1447
1465
|
const allOrders = responseData.results;
|
|
1448
|
-
// Filter for active orders and manually sort by dateActivated (newest first)
|
|
1449
1466
|
const activeOrders = allOrders
|
|
1450
1467
|
.filter((order: any) => order.action === 'NEW' && !order.dateStopped)
|
|
1451
1468
|
.sort((a: any, b: any) => {
|
|
1452
|
-
// Manual sorting by dateActivated, handling null values
|
|
1453
1469
|
const dateA = a.dateActivated ? new Date(a.dateActivated).getTime() : 0;
|
|
1454
1470
|
const dateB = b.dateActivated ? new Date(b.dateActivated).getTime() : 0;
|
|
1455
|
-
return dateB - dateA;
|
|
1471
|
+
return dateB - dateA;
|
|
1456
1472
|
});
|
|
1457
1473
|
|
|
1458
|
-
// console.log(`Drug Orders: Found ${allOrders.length} total orders, ${activeOrders.length} active orders`);
|
|
1459
|
-
|
|
1460
1474
|
const processedOrders = activeOrders.map((order: any) => {
|
|
1461
1475
|
const processed = {
|
|
1462
1476
|
id: order.uuid,
|
|
@@ -204,10 +204,10 @@
|
|
|
204
204
|
|
|
205
205
|
.chartContainer {
|
|
206
206
|
background-color: colors.$white;
|
|
207
|
-
padding:
|
|
207
|
+
padding: 0;
|
|
208
208
|
border-radius: layout.$spacing-02;
|
|
209
209
|
border: 2px solid colors.$gray-40;
|
|
210
|
-
margin-bottom:
|
|
210
|
+
margin-bottom: 0;
|
|
211
211
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
212
212
|
|
|
213
213
|
// Medical chart styling
|
|
@@ -888,6 +888,13 @@
|
|
|
888
888
|
flex-wrap: wrap;
|
|
889
889
|
}
|
|
890
890
|
|
|
891
|
+
// Orange tag style for low range
|
|
892
|
+
.tagLowRange {
|
|
893
|
+
background-color: #ff9800 !important;
|
|
894
|
+
color: #fff !important;
|
|
895
|
+
border-color: #ff9800 !important;
|
|
896
|
+
}
|
|
897
|
+
|
|
891
898
|
.fetalHeartRateChart {
|
|
892
899
|
min-height: 600px; // Increased height to match cervix graph
|
|
893
900
|
width: 100%;
|
|
@@ -996,7 +1003,7 @@
|
|
|
996
1003
|
border-radius: layout.$spacing-02;
|
|
997
1004
|
overflow-x: auto;
|
|
998
1005
|
max-height: 300px; // Set a reasonable height
|
|
999
|
-
max-width:
|
|
1006
|
+
max-width: 1540px; // Show approximately 20 grids (140px label + 70px * 20 columns = 1540px)
|
|
1000
1007
|
}
|
|
1001
1008
|
|
|
1002
1009
|
.gridContainer {
|
|
@@ -1265,7 +1272,7 @@
|
|
|
1265
1272
|
}
|
|
1266
1273
|
}
|
|
1267
1274
|
|
|
1268
|
-
// Detailed
|
|
1275
|
+
// Detailed Contractions Grid - Partograph Format
|
|
1269
1276
|
.contractionsContainer {
|
|
1270
1277
|
display: flex;
|
|
1271
1278
|
flex-direction: column;
|
|
@@ -1515,13 +1522,13 @@
|
|
|
1515
1522
|
color: colors.$gray-80;
|
|
1516
1523
|
}
|
|
1517
1524
|
|
|
1518
|
-
// Grid container for
|
|
1525
|
+
// Grid container for contractions
|
|
1519
1526
|
.contractionsGrid {
|
|
1520
1527
|
position: relative;
|
|
1521
1528
|
margin: layout.$spacing-04 0;
|
|
1522
1529
|
}
|
|
1523
1530
|
|
|
1524
|
-
// Table view styles for
|
|
1531
|
+
// Table view styles for contractions
|
|
1525
1532
|
.contractionsTable {
|
|
1526
1533
|
background-color: colors.$white;
|
|
1527
1534
|
border: 1px solid colors.$gray-30;
|
|
@@ -1589,6 +1596,58 @@
|
|
|
1589
1596
|
margin: layout.$spacing-05 0;
|
|
1590
1597
|
}
|
|
1591
1598
|
|
|
1599
|
+
.oxytocinGraph {
|
|
1600
|
+
width: 100%;
|
|
1601
|
+
margin: 0;
|
|
1602
|
+
|
|
1603
|
+
.membraneGrid {
|
|
1604
|
+
max-height: fit-content;
|
|
1605
|
+
min-height: auto;
|
|
1606
|
+
margin-top: 0;
|
|
1607
|
+
margin-bottom: 0;
|
|
1608
|
+
padding-bottom: 0;
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
.gridContainer {
|
|
1612
|
+
padding: 0;
|
|
1613
|
+
margin-bottom: 0;
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
.gridRow {
|
|
1617
|
+
min-height: 50px;
|
|
1618
|
+
margin-bottom: 0;
|
|
1619
|
+
|
|
1620
|
+
&:last-child {
|
|
1621
|
+
border-bottom: 1px solid colors.$gray-20;
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
.gridCell {
|
|
1626
|
+
border: 1px solid colors.$gray-30;
|
|
1627
|
+
min-height: 50px;
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
// Oxytocin specific styling when inside fetalHeartRateContainer
|
|
1632
|
+
.fetalHeartRateContainer {
|
|
1633
|
+
.membraneGrid {
|
|
1634
|
+
margin: 0;
|
|
1635
|
+
padding: 0;
|
|
1636
|
+
max-height: fit-content;
|
|
1637
|
+
min-height: auto;
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
.gridContainer {
|
|
1641
|
+
padding: 0;
|
|
1642
|
+
margin: 0;
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
.gridRow {
|
|
1646
|
+
margin: 0;
|
|
1647
|
+
min-height: 50px;
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1592
1651
|
.drugsCell {
|
|
1593
1652
|
position: relative;
|
|
1594
1653
|
background-color: colors.$white;
|
|
@@ -4,6 +4,7 @@ import useSWR from 'swr';
|
|
|
4
4
|
import { MCH_PARTOGRAPHY_ENCOUNTER_UUID, PARTOGRAPHY_CONCEPTS } from '../types';
|
|
5
5
|
import { createPartographyEncounter } from '../partography.resource';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
|
+
import { FETAL_HEART_RATE_HOUR_CONCEPT } from '../../../config-schema';
|
|
7
8
|
|
|
8
9
|
export interface FetalHeartRateEntry {
|
|
9
10
|
id: string;
|
|
@@ -42,12 +43,9 @@ export function useFetalHeartRateData(patientUuid: string) {
|
|
|
42
43
|
const encounterDatetime = new Date(encounter.encounterDatetime);
|
|
43
44
|
let hour = 0;
|
|
44
45
|
let time = '';
|
|
46
|
+
// Find hour obs by concept and use its value as number
|
|
45
47
|
const hourObs = encounter.obs.find(
|
|
46
|
-
(obs) =>
|
|
47
|
-
obs.concept.uuid === PARTOGRAPHY_CONCEPTS['fetal-heart-rate-hour'] &&
|
|
48
|
-
obs.value &&
|
|
49
|
-
typeof obs.value === 'string' &&
|
|
50
|
-
obs.value.startsWith('Hour:'),
|
|
48
|
+
(obs) => obs.concept.uuid === FETAL_HEART_RATE_HOUR_CONCEPT && typeof obs.value === 'number',
|
|
51
49
|
);
|
|
52
50
|
const timeObs = encounter.obs.find(
|
|
53
51
|
(obs) =>
|
|
@@ -56,11 +54,8 @@ export function useFetalHeartRateData(patientUuid: string) {
|
|
|
56
54
|
typeof obs.value === 'string' &&
|
|
57
55
|
obs.value.startsWith('Time:'),
|
|
58
56
|
);
|
|
59
|
-
if (hourObs && typeof hourObs.value === '
|
|
60
|
-
|
|
61
|
-
if (hourMatch) {
|
|
62
|
-
hour = parseFloat(hourMatch[1]) || 0;
|
|
63
|
-
}
|
|
57
|
+
if (hourObs && typeof hourObs.value === 'number') {
|
|
58
|
+
hour = hourObs.value;
|
|
64
59
|
}
|
|
65
60
|
if (timeObs && typeof timeObs.value === 'string') {
|
|
66
61
|
const timeMatch = timeObs.value.match(/Time:\s*(.+)/);
|
|
@@ -96,13 +96,15 @@ export async function saveOxytocinFormData(
|
|
|
96
96
|
if (!finalLocationUuid) {
|
|
97
97
|
return { success: false, message: t('Location information is required'), error: 'NO_LOCATION' };
|
|
98
98
|
}
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
// Use utility to ensure encounterDatetime is always in the past
|
|
100
|
+
// Import getValidEncounterDatetime from types/index if not already imported
|
|
101
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
102
|
+
const { getValidEncounterDatetime } = require('../types');
|
|
101
103
|
const encounterPayload = {
|
|
102
104
|
patient: patientUuid,
|
|
103
105
|
encounterType: MCH_PARTOGRAPHY_ENCOUNTER_UUID,
|
|
104
106
|
location: finalLocationUuid,
|
|
105
|
-
encounterDatetime:
|
|
107
|
+
encounterDatetime: getValidEncounterDatetime(),
|
|
106
108
|
obs: observations,
|
|
107
109
|
encounterProviders: [
|
|
108
110
|
{
|
|
@@ -35,11 +35,13 @@ export const contractionLevelUuidMap: Record<string, string> = {
|
|
|
35
35
|
moderate: '1499AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
|
|
36
36
|
strong: '166788AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
|
|
37
37
|
};
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
export function getValidEncounterDatetime(): string {
|
|
39
|
+
const now = new Date();
|
|
40
|
+
now.setMinutes(now.getMinutes() - 4);
|
|
41
|
+
return now.toISOString();
|
|
42
|
+
}
|
|
43
|
+
export const URINE_TEST_TIME_SLOT_CONCEPT = 'bb3724c9-fbcc-49c5-9702-6cde0be325ca';
|
|
44
|
+
|
|
43
45
|
export const contractionLevelOptions = [
|
|
44
46
|
{
|
|
45
47
|
value: 'none',
|
|
@@ -140,6 +142,8 @@ import {
|
|
|
140
142
|
CONTRACTION_LEVEL_MILD_CONCEPT,
|
|
141
143
|
CONTRACTION_LEVEL_MODERATE_CONCEPT,
|
|
142
144
|
CONTRACTION_LEVEL_STRONG_CONCEPT,
|
|
145
|
+
OXYTOCIN_DROPS_PER_MINUTE_CONCEPT,
|
|
146
|
+
OXYTOCIN_TIME_CONCEPT,
|
|
143
147
|
} from '../../../config-schema';
|
|
144
148
|
|
|
145
149
|
const _CODE_TO_PLUS_MAP: Record<string, string> = {
|
|
@@ -151,10 +155,20 @@ const _CODE_TO_PLUS_MAP: Record<string, string> = {
|
|
|
151
155
|
'++': '++',
|
|
152
156
|
[MOULDING_SEVERE_CONCEPT]: '+++',
|
|
153
157
|
'+++': '+++',
|
|
158
|
+
NEGATIVE: '0',
|
|
159
|
+
ZERO: '0',
|
|
160
|
+
'ONE PLUS': '+',
|
|
161
|
+
'1+': '+',
|
|
162
|
+
'TWO PLUS': '++',
|
|
163
|
+
'2+': '++',
|
|
164
|
+
'THREE PLUS': '+++',
|
|
165
|
+
'3+': '+++',
|
|
166
|
+
TRACE: '+/-',
|
|
167
|
+
'+/-': '+/-',
|
|
154
168
|
};
|
|
155
169
|
|
|
156
170
|
export const codeToPlus = (raw?: string): string => {
|
|
157
|
-
const key = (raw ?? '').toString().trim();
|
|
171
|
+
const key = (raw ?? '').toString().trim().toUpperCase();
|
|
158
172
|
return _CODE_TO_PLUS_MAP[key] ?? raw ?? '';
|
|
159
173
|
};
|
|
160
174
|
const _partoConcepts = (configSchema as any)?.partography?._default?.conceptUuids ?? {};
|
|
@@ -232,11 +246,8 @@ export const PARTOGRAPHY_GRAPH_TYPES = [
|
|
|
232
246
|
export const PARTOGRAPHY_ENCOUNTER_TYPES = Object.fromEntries(
|
|
233
247
|
PARTOGRAPHY_GRAPH_TYPES.map((type) => [
|
|
234
248
|
type,
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
: type === 'pulse-bp-combined'
|
|
238
|
-
? MCH_PARTOGRAPHY_ENCOUNTER_UUID
|
|
239
|
-
: MCH_PARTOGRAPHY_ENCOUNTER_UUID,
|
|
249
|
+
/* For testing: set drugs-fluids to use the same encounter type as temperature/others */
|
|
250
|
+
MCH_PARTOGRAPHY_ENCOUNTER_UUID,
|
|
240
251
|
]),
|
|
241
252
|
) as Record<(typeof PARTOGRAPHY_GRAPH_TYPES)[number], string>;
|
|
242
253
|
|
|
@@ -535,16 +546,14 @@ export const getColorForGraph = (colorName: string): string => {
|
|
|
535
546
|
return PARTOGRAPHY_COLOR_MAPPINGS[colorName as keyof typeof PARTOGRAPHY_COLOR_MAPPINGS] || '#525252';
|
|
536
547
|
};
|
|
537
548
|
|
|
538
|
-
// Default Fetal Heart Rate Chart Data
|
|
539
|
-
export const defaultFetalHeartRateChartData
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
{ hour: 60, value: 140, group: 'Fetal Heart Rate', time: '60', color: getColorForGraph('green') },
|
|
547
|
-
];
|
|
549
|
+
// Default Fetal Heart Rate Chart Data - Empty for no data state
|
|
550
|
+
export const defaultFetalHeartRateChartData: Array<{
|
|
551
|
+
hour: number;
|
|
552
|
+
value: number;
|
|
553
|
+
group: string;
|
|
554
|
+
time: string;
|
|
555
|
+
color: string;
|
|
556
|
+
}> = [];
|
|
548
557
|
|
|
549
558
|
export const FORM_OPTION_CONCEPTS = {
|
|
550
559
|
AMNIOTIC_FLUID: {
|
|
@@ -787,20 +796,14 @@ export const TIME_SLOT_OPTIONS = [
|
|
|
787
796
|
] as const;
|
|
788
797
|
|
|
789
798
|
export const MEMBRANE_TIME_SLOT_OPTIONS = [
|
|
790
|
-
|
|
791
|
-
{ value: '
|
|
792
|
-
|
|
793
|
-
{
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
{ value: '00:00', label: '00:00' },
|
|
799
|
-
{ value: '01:00', label: '01:00' },
|
|
800
|
-
{ value: '02:00', label: '02:00' },
|
|
801
|
-
{ value: '03:00', label: '03:00' },
|
|
802
|
-
{ value: '04:00', label: '04:00' },
|
|
803
|
-
{ value: '05:00', label: '05:00' },
|
|
799
|
+
// Add 0hr option first
|
|
800
|
+
{ value: '0', label: '0hr' },
|
|
801
|
+
// Then add the existing 0.5hr increments
|
|
802
|
+
...Array.from({ length: 48 }, (_, i) => {
|
|
803
|
+
const value = ((i + 1) * 0.5).toString();
|
|
804
|
+
const label = i % 2 === 1 ? `${(i + 1) * 0.5}hr` : `${(i + 1) / 2}hr`;
|
|
805
|
+
return { value, label };
|
|
806
|
+
}),
|
|
804
807
|
] as const;
|
|
805
808
|
|
|
806
809
|
export const EVENT_TYPE_OPTIONS = [
|
|
@@ -1281,8 +1284,8 @@ export const MOULDING_SYMBOL_MAP: Record<string, string> = {
|
|
|
1281
1284
|
};
|
|
1282
1285
|
|
|
1283
1286
|
export const OXYTOCIN_FORM_CONCEPTS = {
|
|
1284
|
-
time:
|
|
1285
|
-
oxytocinDropsPerMinute:
|
|
1287
|
+
time: OXYTOCIN_TIME_CONCEPT,
|
|
1288
|
+
oxytocinDropsPerMinute: OXYTOCIN_DROPS_PER_MINUTE_CONCEPT,
|
|
1286
1289
|
} as const;
|
|
1287
1290
|
export const CERVIX_FORM_CONCEPTS = {
|
|
1288
1291
|
hour: FETAL_HEART_RATE_HOUR_CONCEPT,
|
package/translations/am.json
CHANGED
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"caseEncounter": "የጉዳይ አስተዳደር ግኝቶች",
|
|
48
48
|
"caseManagement": "Case Management",
|
|
49
49
|
"causeOfDeath": "የሞት መንስኤ",
|
|
50
|
-
"cervicalContractionsData": "
|
|
50
|
+
"cervicalContractionsData": "Contractions",
|
|
51
51
|
"cervicalDilation": "Cervical Dilation",
|
|
52
52
|
"chooseAnOption": "Choose an option",
|
|
53
53
|
"chooseCount": "Choose count",
|
package/translations/en.json
CHANGED
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"caseEncounter": "Case management encounters",
|
|
48
48
|
"caseManagement": "Case Management",
|
|
49
49
|
"causeOfDeath": "Cause of Death",
|
|
50
|
-
"cervicalContractionsData": "
|
|
50
|
+
"cervicalContractionsData": "Contractions",
|
|
51
51
|
"cervicalDilation": "Cervical Dilation",
|
|
52
52
|
"chooseAnOption": "Choose an option",
|
|
53
53
|
"chooseCount": "Choose count",
|