@imranq2/fhirpatientsummary 1.0.11 → 1.0.12

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/dist/index.cjs CHANGED
@@ -29,22 +29,21 @@ module.exports = __toCommonJS(index_exports);
29
29
  // src/structures/ips_sections.ts
30
30
  var IPSSections = /* @__PURE__ */ ((IPSSections2) => {
31
31
  IPSSections2["PATIENT"] = "Patient";
32
- IPSSections2["ALLERGIES"] = "AllergyIntoleranceSection";
33
- IPSSections2["MEDICATIONS"] = "MedicationSection";
34
32
  IPSSections2["PROBLEMS"] = "ProblemSection";
33
+ IPSSections2["ALLERGIES"] = "AllergyIntoleranceSection";
34
+ IPSSections2["MEDICATIONS"] = "MedicationSummarySection";
35
35
  IPSSections2["IMMUNIZATIONS"] = "ImmunizationSection";
36
- IPSSections2["VITAL_SIGNS"] = "VitalSignsSection";
36
+ IPSSections2["DIAGNOSTIC_REPORTS"] = "ResultsSection";
37
+ IPSSections2["PROCEDURES"] = "HistoryOfProceduresSection";
37
38
  IPSSections2["MEDICAL_DEVICES"] = "MedicalDeviceSection";
38
- IPSSections2["DIAGNOSTIC_REPORTS"] = "DiagnosticReportSection";
39
- IPSSections2["PROCEDURES"] = "ProcedureSection";
40
- IPSSections2["FAMILY_HISTORY"] = "FamilyHistorySection";
41
- IPSSections2["SOCIAL_HISTORY"] = "SocialHistorySection";
42
- IPSSections2["PREGNANCY_HISTORY"] = "PregnancyHistorySection";
43
- IPSSections2["FUNCTIONAL_STATUS"] = "FunctionalStatusSection";
44
- IPSSections2["MEDICAL_HISTORY"] = "MedicalHistorySection";
45
- IPSSections2["CARE_PLAN"] = "CarePlanSection";
46
- IPSSections2["CLINICAL_IMPRESSION"] = "ClinicalImpressionSection";
47
39
  IPSSections2["ADVANCE_DIRECTIVES"] = "AdvanceDirectivesSection";
40
+ IPSSections2["FUNCTIONAL_STATUS"] = "FunctionalStatusSection";
41
+ IPSSections2["PREGNANCY_HISTORY"] = "HistoryOfPregnancySection";
42
+ IPSSections2["CARE_PLAN"] = "PlanOfCareSection";
43
+ IPSSections2["MEDICAL_HISTORY"] = "HistoryOfPastIllnessSection";
44
+ IPSSections2["SOCIAL_HISTORY"] = "SocialHistorySection";
45
+ IPSSections2["VITAL_SIGNS"] = "VitalSignsSection";
46
+ IPSSections2["FAMILY_HISTORY"] = "FamilyHistorySection";
48
47
  return IPSSections2;
49
48
  })(IPSSections || {});
50
49
 
@@ -52,99 +51,111 @@ var IPSSections = /* @__PURE__ */ ((IPSSections2) => {
52
51
  var IPS_SECTION_LOINC_CODES = {
53
52
  ["Patient" /* PATIENT */]: "54126-4",
54
53
  ["AllergyIntoleranceSection" /* ALLERGIES */]: "48765-2",
55
- ["MedicationSection" /* MEDICATIONS */]: "10160-0",
54
+ ["MedicationSummarySection" /* MEDICATIONS */]: "10160-0",
56
55
  ["ProblemSection" /* PROBLEMS */]: "11450-4",
57
56
  ["ImmunizationSection" /* IMMUNIZATIONS */]: "11369-6",
58
57
  ["VitalSignsSection" /* VITAL_SIGNS */]: "8716-3",
59
58
  ["MedicalDeviceSection" /* MEDICAL_DEVICES */]: "46264-8",
60
- ["DiagnosticReportSection" /* DIAGNOSTIC_REPORTS */]: "30954-2",
61
- ["ProcedureSection" /* PROCEDURES */]: "47519-4",
59
+ ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: "30954-2",
60
+ ["HistoryOfProceduresSection" /* PROCEDURES */]: "47519-4",
62
61
  ["FamilyHistorySection" /* FAMILY_HISTORY */]: "10157-6",
63
62
  ["SocialHistorySection" /* SOCIAL_HISTORY */]: "29762-2",
64
- ["PregnancyHistorySection" /* PREGNANCY_HISTORY */]: "10162-6",
63
+ ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: "10162-6",
65
64
  ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: "47420-5",
66
- ["MedicalHistorySection" /* MEDICAL_HISTORY */]: "11348-0",
67
- ["CarePlanSection" /* CARE_PLAN */]: "18776-5",
68
- ["ClinicalImpressionSection" /* CLINICAL_IMPRESSION */]: "51848-0",
65
+ ["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: "11348-0",
66
+ ["PlanOfCareSection" /* CARE_PLAN */]: "18776-5",
69
67
  ["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: "42348-3"
70
68
  };
71
69
  var IPS_SECTION_DISPLAY_NAMES = {
72
70
  ["Patient" /* PATIENT */]: "Patient summary Document",
73
- ["AllergyIntoleranceSection" /* ALLERGIES */]: "Allergies and adverse reactions Document",
74
- ["MedicationSection" /* MEDICATIONS */]: "History of Medication use Narrative",
75
- ["ProblemSection" /* PROBLEMS */]: "Problem list - Reported",
76
- ["ImmunizationSection" /* IMMUNIZATIONS */]: "History of Immunization Narrative",
77
- ["VitalSignsSection" /* VITAL_SIGNS */]: "Vital signs",
78
- ["MedicalDeviceSection" /* MEDICAL_DEVICES */]: "History of medical device use",
79
- ["DiagnosticReportSection" /* DIAGNOSTIC_REPORTS */]: "Relevant diagnostic tests/laboratory data Narrative",
80
- ["ProcedureSection" /* PROCEDURES */]: "History of Procedures Document",
81
- ["FamilyHistorySection" /* FAMILY_HISTORY */]: "History of family member diseases Narrative",
82
- ["SocialHistorySection" /* SOCIAL_HISTORY */]: "Social history Narrative",
83
- ["PregnancyHistorySection" /* PREGNANCY_HISTORY */]: "History of pregnancies Narrative",
84
- ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: "Functional status assessment note",
85
- ["MedicalHistorySection" /* MEDICAL_HISTORY */]: "History of Past illness NarrativeHistory and physical note Document",
86
- ["CarePlanSection" /* CARE_PLAN */]: "Plan of care note",
87
- ["ClinicalImpressionSection" /* CLINICAL_IMPRESSION */]: "Evaluation note",
88
- ["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: "Advance directives Document"
71
+ ["AllergyIntoleranceSection" /* ALLERGIES */]: "Allergies and Intolerances",
72
+ ["MedicationSummarySection" /* MEDICATIONS */]: "Medication Summary",
73
+ ["ProblemSection" /* PROBLEMS */]: "Problem List",
74
+ ["ImmunizationSection" /* IMMUNIZATIONS */]: "Immunizations",
75
+ ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: "Results Summary",
76
+ ["HistoryOfProceduresSection" /* PROCEDURES */]: "History of Procedures",
77
+ ["MedicalDeviceSection" /* MEDICAL_DEVICES */]: "History of Medical Devices",
78
+ ["VitalSignsSection" /* VITAL_SIGNS */]: "Vital Signs",
79
+ ["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: "Advance Directives",
80
+ ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: "Functional Status",
81
+ ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: "History of Pregnancies",
82
+ ["PlanOfCareSection" /* CARE_PLAN */]: "Plan of Care",
83
+ ["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: "History of Past Illness",
84
+ ["SocialHistorySection" /* SOCIAL_HISTORY */]: "Social History",
85
+ ["FamilyHistorySection" /* FAMILY_HISTORY */]: "History of Family Member Diseases"
86
+ };
87
+ var PREGNANCY_LONIC_CODES = {
88
+ PREGNANCY_STATUS: {
89
+ "LA15173-0": "Pregnant",
90
+ "LA26683-5": "Not pregnant",
91
+ "LA4489-6": "Unknown"
92
+ },
93
+ PREGNANCY_OUTCOME: {
94
+ "11636-8": "Live Birth",
95
+ "11637-6": "Preterm Birth",
96
+ "11638-4": "Still Living Birth",
97
+ "11639-2": "Term Birth",
98
+ "11640-0": "Total Births",
99
+ "11612-9": "Abortions",
100
+ "11613-7": "Induced Abortions",
101
+ "11614-5": "Spontaneous Abortions",
102
+ "33065-4": "Ectopic Pregnancy"
103
+ }
104
+ };
105
+ var SOCIAL_HISTORY_LONIC_CODES = {
106
+ "72166-2": "Tobacco Use",
107
+ "74013-4": "Alcohol Use"
89
108
  };
90
109
 
91
110
  // src/structures/ips_section_resource_map.ts
92
111
  var IPSSectionResourceMap = {
93
112
  ["Patient" /* PATIENT */]: ["Patient"],
94
113
  ["AllergyIntoleranceSection" /* ALLERGIES */]: ["AllergyIntolerance"],
95
- ["MedicationSection" /* MEDICATIONS */]: ["MedicationRequest", "MedicationStatement"],
114
+ ["MedicationSummarySection" /* MEDICATIONS */]: ["MedicationRequest", "MedicationStatement", "Medication"],
115
+ // Medication resource is needed for identifying name of medication
96
116
  ["ProblemSection" /* PROBLEMS */]: ["Condition"],
97
- ["ImmunizationSection" /* IMMUNIZATIONS */]: ["Immunization"],
117
+ ["ImmunizationSection" /* IMMUNIZATIONS */]: ["Immunization", "Organization"],
118
+ // Immunization can include Organization as a related resource
98
119
  ["VitalSignsSection" /* VITAL_SIGNS */]: ["Observation"],
99
- ["MedicalDeviceSection" /* MEDICAL_DEVICES */]: ["Device"],
100
- ["DiagnosticReportSection" /* DIAGNOSTIC_REPORTS */]: ["DiagnosticReport", "Observation"],
101
- // Diagnostic reports can include Observations
102
- ["ProcedureSection" /* PROCEDURES */]: ["Procedure"],
120
+ ["MedicalDeviceSection" /* MEDICAL_DEVICES */]: ["DeviceUseStatement", "Device"],
121
+ // Device resource is used for medical devices name
122
+ ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: ["DiagnosticReport", "Observation"],
123
+ ["HistoryOfProceduresSection" /* PROCEDURES */]: ["Procedure"],
103
124
  ["FamilyHistorySection" /* FAMILY_HISTORY */]: ["FamilyMemberHistory"],
104
125
  ["SocialHistorySection" /* SOCIAL_HISTORY */]: ["Observation"],
105
- // Social history is often Observation
106
- ["PregnancyHistorySection" /* PREGNANCY_HISTORY */]: ["Observation"],
107
- // Pregnancy history is often Observation
108
- ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: ["Observation"],
109
- // Functional status is often Observation
110
- ["MedicalHistorySection" /* MEDICAL_HISTORY */]: ["Condition"],
111
- // Medical history is often Condition
112
- ["CarePlanSection" /* CARE_PLAN */]: ["CarePlan"],
113
- ["ClinicalImpressionSection" /* CLINICAL_IMPRESSION */]: ["ClinicalImpression"],
114
- ["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: ["DocumentReference"]
115
- // Advance directives are often stored as DocumentReference
126
+ ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: ["Observation"],
127
+ ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: ["Condition", "ClinicalImpression"],
128
+ ["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: ["Condition"],
129
+ ["PlanOfCareSection" /* CARE_PLAN */]: ["CarePlan"],
130
+ ["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: ["Consent"]
116
131
  };
117
132
  var IPSSectionResourceFilters = {
118
133
  // Only include active allergies
119
- ["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "AllergyIntolerance" && resource.clinicalStatus?.coding?.some((c) => c.code === "active"),
120
- // Only include active medication requests/statements
121
- ["MedicationSection" /* MEDICATIONS */]: (resource) => resource.resourceType === "MedicationRequest" && resource.status === "active" || resource.resourceType === "MedicationStatement" && resource.status === "active",
134
+ ["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "AllergyIntolerance" && resource.clinicalStatus?.coding?.some((c) => typeof c.code === "string"),
122
135
  // Only include active problems/conditions
123
- ["ProblemSection" /* PROBLEMS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => c.code === "active"),
136
+ ["ProblemSection" /* PROBLEMS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => !["inactive", "resolved"].includes(c.code)),
124
137
  // Only include completed immunizations
125
- ["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Immunization" && resource.status === "completed",
138
+ ["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Immunization" && resource.status === "completed" || resource.resourceType === "Organization",
126
139
  // Only include vital sign Observations (category.coding contains 'vital-signs')
127
140
  ["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Observation" && resource.category?.some((cat) => cat.coding?.some((c) => c.code === "vital-signs")),
128
- // Only include active devices
129
- ["MedicalDeviceSection" /* MEDICAL_DEVICES */]: (resource) => resource.resourceType === "Device" && resource.status === "active",
130
141
  // Only include finalized diagnostic reports
131
- ["DiagnosticReportSection" /* DIAGNOSTIC_REPORTS */]: (resource) => ["DiagnosticReport", "Observation"].includes(resource.resourceType) && resource.status === "final",
142
+ ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => ["DiagnosticReport", "Observation"].includes(resource.resourceType) && resource.status === "final",
132
143
  // Only include completed procedures
133
- ["ProcedureSection" /* PROCEDURES */]: (resource) => resource.resourceType === "Procedure" && resource.status === "completed",
144
+ ["HistoryOfProceduresSection" /* PROCEDURES */]: (resource) => resource.resourceType === "Procedure" && resource.status === "completed",
134
145
  // Only include family history resources
135
146
  ["FamilyHistorySection" /* FAMILY_HISTORY */]: (resource) => resource.resourceType === "FamilyMemberHistory",
136
147
  // Only include social history Observations (category.coding contains 'social-history')
137
- ["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Observation" && resource.category?.some((cat) => cat.coding?.some((c) => c.code === "social-history")),
148
+ ["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Observation" && resource.code?.coding?.some((c) => Object.keys(SOCIAL_HISTORY_LONIC_CODES).includes(c.code)),
138
149
  // Only include pregnancy history Observations (category.coding contains 'pregnancy')
139
- ["PregnancyHistorySection" /* PREGNANCY_HISTORY */]: (resource) => resource.resourceType === "Observation" && resource.category?.some((cat) => cat.coding?.some((c) => c.code === "pregnancy")),
140
- // Only include functional status Observations (category.coding contains 'functional-status')
141
- ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "Observation" && resource.category?.some((cat) => cat.coding?.some((c) => c.code === "functional-status")),
142
- // Only include active medical history Conditions
143
- ["MedicalHistorySection" /* MEDICAL_HISTORY */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => c.code === "active"),
150
+ ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: (resource) => resource.resourceType === "Observation" && (resource.code?.coding?.some((c) => Object.keys(PREGNANCY_LONIC_CODES.PREGNANCY_STATUS).includes(c.code)) || resource.valueCodeableConcept?.coding?.some((c) => Object.keys(PREGNANCY_LONIC_CODES.PREGNANCY_OUTCOME).includes(c.code))),
151
+ // Only include active functional status Conditions or ClinicalImpressions
152
+ ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => typeof c.code === "string") || resource.resourceType === "ClinicalImpression" && resource.status === "completed",
153
+ // Only include resolved medical history Conditions
154
+ ["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => ["inactive", "resolved"].includes(c.code)),
144
155
  // Only include active care plans
145
- ["CarePlanSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "CarePlan" && resource.status === "active",
146
- // Only include ClinicalImpression resources
147
- ["ClinicalImpressionSection" /* CLINICAL_IMPRESSION */]: (resource) => resource.resourceType === "ClinicalImpression",
156
+ ["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "CarePlan" && resource.status === "active",
157
+ // Only include active advance directives (Consent resources)
158
+ ["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Consent" && resource.status === "active",
148
159
  // Patient section: only Patient resource
149
160
  ["Patient" /* PATIENT */]: (resource) => resource.resourceType === "Patient"
150
161
  };
@@ -186,8 +197,6 @@ var TemplateUtilities = class {
186
197
  }
187
198
  if (cc.text) {
188
199
  return cc.text;
189
- } else if ("display" in cc && cc.display) {
190
- return cc.display;
191
200
  } else if (cc.coding && cc.coding[0]) {
192
201
  if (cc.coding[0].display) {
193
202
  return cc.coding[0].display;
@@ -219,8 +228,8 @@ var TemplateUtilities = class {
219
228
  */
220
229
  renderDevice(deviceRef) {
221
230
  const device = deviceRef && this.resolveReference(deviceRef);
222
- if (device && device.resourceType === "Device" && device.type) {
223
- return this.codeableConcept(device.type, "display");
231
+ if (device && device.resourceType === "Device" && device.deviceName && device.deviceName.length > 0) {
232
+ return this.safeConcat(device.deviceName, "name");
224
233
  }
225
234
  return "";
226
235
  }
@@ -401,8 +410,12 @@ var TemplateUtilities = class {
401
410
  }
402
411
  const texts = [];
403
412
  for (const item of list) {
404
- if (item && item.manifestation && item.manifestation[0] && item.manifestation[0].text) {
405
- texts.push(item.manifestation[0].text);
413
+ if (item && item.manifestation && item.manifestation[0]) {
414
+ if (item.manifestation[0].text) {
415
+ texts.push(item.manifestation[0].text);
416
+ } else if (item.manifestation[0].coding && item.manifestation[0].coding[0]?.display) {
417
+ texts.push(item.manifestation[0].coding[0].display);
418
+ }
406
419
  }
407
420
  }
408
421
  return texts.join(", ");
@@ -561,6 +574,25 @@ var TemplateUtilities = class {
561
574
  }
562
575
  return null;
563
576
  }
577
+ extractPregnancyStatus(observation) {
578
+ let status = "";
579
+ observation.code?.coding?.forEach((c) => {
580
+ if (c.code && Object.keys(PREGNANCY_LONIC_CODES.PREGNANCY_STATUS).includes(c.code)) {
581
+ status = PREGNANCY_LONIC_CODES.PREGNANCY_STATUS[c.code];
582
+ }
583
+ });
584
+ if (observation.valueCodeableConcept) {
585
+ observation.valueCodeableConcept.coding?.forEach((c) => {
586
+ if (c.code && Object.keys(PREGNANCY_LONIC_CODES.PREGNANCY_OUTCOME).includes(c.code)) {
587
+ if (status) {
588
+ status += " - ";
589
+ }
590
+ status += PREGNANCY_LONIC_CODES.PREGNANCY_OUTCOME[c.code];
591
+ }
592
+ });
593
+ }
594
+ return status;
595
+ }
564
596
  formatQuantityValue(quantity) {
565
597
  if (!quantity) return "";
566
598
  const parts = [];
@@ -799,7 +831,6 @@ var PatientTemplate = class _PatientTemplate {
799
831
  const patient = entry.resource;
800
832
  html += `
801
833
  <div>
802
- <h2>Patient Summary</h2>
803
834
  <ul>
804
835
  <li><strong>Name(s):</strong>${this.renderNames(patient)}</li>
805
836
  <li><strong>Gender:</strong>${patient.gender ? this.capitalize(patient.gender) : ""}</li>
@@ -949,10 +980,20 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
949
980
  }
950
981
  }
951
982
  }
983
+ activeAllergies.sort((a, b) => {
984
+ const dateA = a.onsetDateTime;
985
+ const dateB = b.onsetDateTime;
986
+ return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
987
+ });
988
+ resolvedAllergies.sort((a, b) => {
989
+ const dateA = a.onsetDateTime;
990
+ const dateB = b.onsetDateTime;
991
+ return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
992
+ });
952
993
  let html = "";
953
994
  html += `
954
995
  <div class="ActiveAllergies">
955
- <h3>Active Allergies and Intolerances</h3>
996
+ <h3>Active</h3>
956
997
  <table class="ActiveAllergyTable">
957
998
  <thead>
958
999
  <tr>
@@ -980,7 +1021,7 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
980
1021
  </div>`;
981
1022
  html += `
982
1023
  <div class="ResolvedAllergies">
983
- <h3>Resolved Allergies and Intolerances</h3>
1024
+ <h3>Resolved</h3>
984
1025
  <table class="ResolvedAllergyTable">
985
1026
  <thead>
986
1027
  <tr>
@@ -1024,7 +1065,7 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
1024
1065
  <tr id="${templateUtilities.narrativeLinkId(allergy.extension)}">
1025
1066
  <td class="Name"><span class="AllergenName">${templateUtilities.codeableConcept(allergy.code)}</span></td>
1026
1067
  <td class="Status">${templateUtilities.codeableConcept(allergy.clinicalStatus) || "-"}</td>
1027
- <td class="Category">${templateUtilities.safeConcat(allergy.category, "value") || "-"}</td>
1068
+ <td class="Category">${templateUtilities.safeConcat(allergy.category) || "-"}</td>
1028
1069
  <td class="Reaction">${templateUtilities.concatReactionManifestation(allergy.reaction) || "-"}</td>
1029
1070
  <td class="Severity">${templateUtilities.safeConcat(allergy.reaction, "severity") || "-"}</td>
1030
1071
  <td class="OnsetDate">${templateUtilities.renderTime(allergy.onsetDateTime, timezone) || "-"}</td>
@@ -1059,6 +1100,67 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
1059
1100
  generateNarrative(resource, timezone) {
1060
1101
  return _MedicationSummaryTemplate.generateStaticNarrative(resource, timezone);
1061
1102
  }
1103
+ /**
1104
+ * Safely parse a date string and return a valid Date object or null
1105
+ * @param dateString - The date string to parse
1106
+ * @returns Date object or null if invalid
1107
+ */
1108
+ static parseDate(dateString) {
1109
+ if (!dateString || dateString.trim() === "") {
1110
+ return null;
1111
+ }
1112
+ const date = new Date(dateString);
1113
+ return !isNaN(date.getTime()) ? date : null;
1114
+ }
1115
+ /**
1116
+ * Determine if a MedicationRequest is active
1117
+ * @param medicationRequest - The MedicationRequest resource
1118
+ * @returns boolean indicating if the medication request is active
1119
+ */
1120
+ static isActiveMedicationRequest(medicationRequest) {
1121
+ const status = medicationRequest.status?.toLowerCase();
1122
+ if (status === "active" || status === "unknown") {
1123
+ return true;
1124
+ }
1125
+ if (status === "completed" || status === "cancelled" || status === "stopped" || status === "draft") {
1126
+ return false;
1127
+ }
1128
+ const endDate = medicationRequest.dispenseRequest?.validityPeriod?.end;
1129
+ if (!endDate) {
1130
+ return true;
1131
+ }
1132
+ const parsedEndDate = this.parseDate(endDate);
1133
+ if (!parsedEndDate) {
1134
+ return true;
1135
+ }
1136
+ return parsedEndDate.getTime() > Date.now();
1137
+ }
1138
+ /**
1139
+ * Determine if a MedicationStatement is active
1140
+ * @param medicationStatement - The MedicationStatement resource
1141
+ * @returns boolean indicating if the medication statement is active
1142
+ */
1143
+ static isActiveMedicationStatement(medicationStatement) {
1144
+ const status = medicationStatement.status?.toLowerCase();
1145
+ if (status === "active" || status === "intended" || status === "unknown") {
1146
+ return true;
1147
+ }
1148
+ if (status === "completed" || status === "stopped" || status === "not-taken") {
1149
+ return false;
1150
+ }
1151
+ let endDate;
1152
+ if (medicationStatement.effectivePeriod?.end) {
1153
+ endDate = medicationStatement.effectivePeriod.end;
1154
+ }
1155
+ if (!endDate) {
1156
+ return true;
1157
+ }
1158
+ const parsedEndDate = this.parseDate(endDate);
1159
+ if (!parsedEndDate) {
1160
+ return true;
1161
+ }
1162
+ return parsedEndDate.getTime() > Date.now();
1163
+ }
1062
1164
  /**
1063
1165
  * Internal static implementation that actually generates the narrative
1064
1166
  * @param resource - FHIR Bundle containing Medication resources
@@ -1070,12 +1172,56 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
1070
1172
  const templateUtilities = new TemplateUtilities(resource);
1071
1173
  let html = "";
1072
1174
  const medicationRequests = this.getMedicationRequests(templateUtilities, resource);
1073
- if (medicationRequests.length > 0) {
1074
- html += this.renderMedicationRequests(templateUtilities, medicationRequests);
1075
- }
1076
1175
  const medicationStatements = this.getMedicationStatements(templateUtilities, resource);
1077
- if (medicationStatements.length > 0) {
1078
- html += this.renderMedicationStatements(templateUtilities, medicationStatements);
1176
+ const allActiveMedications = [];
1177
+ const allInactiveMedications = [];
1178
+ medicationRequests.forEach((mr) => {
1179
+ if (this.isActiveMedicationRequest(mr.resource)) {
1180
+ allActiveMedications.push({ type: "request", resource: mr.resource, extension: mr.extension });
1181
+ } else {
1182
+ allInactiveMedications.push({ type: "request", resource: mr.resource, extension: mr.extension });
1183
+ }
1184
+ });
1185
+ medicationStatements.forEach((ms) => {
1186
+ if (this.isActiveMedicationStatement(ms.resource)) {
1187
+ allActiveMedications.push({ type: "statement", resource: ms.resource, extension: ms.extension });
1188
+ } else {
1189
+ allInactiveMedications.push({ type: "statement", resource: ms.resource, extension: ms.extension });
1190
+ }
1191
+ });
1192
+ const sortMedications = (medications) => {
1193
+ medications.sort((a, b) => {
1194
+ let dateStringA;
1195
+ let dateStringB;
1196
+ if (a.type === "request") {
1197
+ const mr = a.resource;
1198
+ dateStringA = mr.dispenseRequest?.validityPeriod?.start || mr.authoredOn;
1199
+ } else {
1200
+ const ms = a.resource;
1201
+ dateStringA = ms.effectiveDateTime || ms.effectivePeriod?.start;
1202
+ }
1203
+ if (b.type === "request") {
1204
+ const mr = b.resource;
1205
+ dateStringB = mr.dispenseRequest?.validityPeriod?.start || mr.authoredOn;
1206
+ } else {
1207
+ const ms = b.resource;
1208
+ dateStringB = ms.effectiveDateTime || ms.effectivePeriod?.start;
1209
+ }
1210
+ const dateA = this.parseDate(dateStringA);
1211
+ const dateB = this.parseDate(dateStringB);
1212
+ if (!dateA && !dateB) return 0;
1213
+ if (!dateA) return 1;
1214
+ if (!dateB) return -1;
1215
+ return dateB.getTime() - dateA.getTime();
1216
+ });
1217
+ };
1218
+ if (allActiveMedications.length > 0) {
1219
+ sortMedications(allActiveMedications);
1220
+ html += this.renderCombinedMedications(templateUtilities, allActiveMedications, "Active Medications");
1221
+ }
1222
+ if (allInactiveMedications.length > 0) {
1223
+ sortMedications(allInactiveMedications);
1224
+ html += this.renderCombinedMedications(templateUtilities, allInactiveMedications, "Inactive Medications");
1079
1225
  }
1080
1226
  return html;
1081
1227
  }
@@ -1110,16 +1256,19 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
1110
1256
  }));
1111
1257
  }
1112
1258
  /**
1113
- * Render HTML table for MedicationRequest resources
1259
+ * Render HTML table for combined MedicationRequest and MedicationStatement resources
1114
1260
  * @param templateUtilities - Instance of TemplateUtilities for utility functions
1115
- * @param medications - Array of MedicationRequest resources
1261
+ * @param medications - Array of combined medication resources
1262
+ * @param sectionTitle - Title for the section
1116
1263
  * @returns HTML string for rendering
1117
1264
  */
1118
- static renderMedicationRequests(templateUtilities, medications) {
1265
+ static renderCombinedMedications(templateUtilities, medications, sectionTitle) {
1119
1266
  let html = `
1267
+ <h3>${sectionTitle}</h3>
1120
1268
  <table>
1121
1269
  <thead>
1122
1270
  <tr>
1271
+ <th>Type</th>
1123
1272
  <th>Medication</th>
1124
1273
  <th>Sig</th>
1125
1274
  <th>Dispense Quantity</th>
@@ -1130,86 +1279,56 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
1130
1279
  </tr>
1131
1280
  </thead>
1132
1281
  <tbody>`;
1133
- for (const { resource: mr, extension } of medications) {
1134
- const narrativeLinkId = templateUtilities.narrativeLinkId(extension);
1135
- const status = mr.status ? String(mr.status) : "-";
1136
- const medication = templateUtilities.getMedicationName(
1137
- mr.medicationReference || mr.medicationCodeableConcept
1138
- );
1139
- const sig = templateUtilities.concat(mr.dosageInstruction, "text") || "-";
1282
+ for (const medication of medications) {
1283
+ const narrativeLinkId = templateUtilities.narrativeLinkId(medication.extension);
1284
+ let type;
1285
+ let medicationName;
1286
+ let sig;
1140
1287
  let dispenseQuantity = "-";
1141
- if (mr.dispenseRequest?.quantity) {
1142
- const quantity = mr.dispenseRequest.quantity;
1143
- if (quantity.value) {
1144
- dispenseQuantity = `${quantity.value} ${quantity.unit || quantity.code || ""}`.trim();
1145
- }
1146
- }
1147
- const refills = mr.dispenseRequest?.numberOfRepeatsAllowed?.toString() || "-";
1288
+ let refills = "-";
1148
1289
  let startDate = "-";
1149
1290
  let endDate = "-";
1150
- if (mr.dispenseRequest?.validityPeriod) {
1151
- startDate = mr.dispenseRequest.validityPeriod.start || "-";
1152
- endDate = mr.dispenseRequest.validityPeriod.end || "-";
1291
+ let status;
1292
+ if (medication.type === "request") {
1293
+ const mr = medication.resource;
1294
+ type = "Request";
1295
+ status = mr.status ? String(mr.status) : "-";
1296
+ medicationName = templateUtilities.getMedicationName(
1297
+ mr.medicationReference || mr.medicationCodeableConcept
1298
+ );
1299
+ sig = templateUtilities.concat(mr.dosageInstruction, "text") || "-";
1300
+ if (mr.dispenseRequest?.quantity) {
1301
+ const quantity = mr.dispenseRequest.quantity;
1302
+ if (quantity.value) {
1303
+ dispenseQuantity = `${quantity.value} ${quantity.unit || quantity.code || ""}`.trim();
1304
+ }
1305
+ }
1306
+ refills = mr.dispenseRequest?.numberOfRepeatsAllowed?.toString() || "-";
1307
+ if (mr.dispenseRequest?.validityPeriod) {
1308
+ startDate = mr.dispenseRequest.validityPeriod.start || "-";
1309
+ endDate = mr.dispenseRequest.validityPeriod.end || "-";
1310
+ } else {
1311
+ startDate = mr.authoredOn || "-";
1312
+ }
1153
1313
  } else {
1154
- startDate = mr.authoredOn || "-";
1155
- }
1156
- html += `
1157
- <tr${narrativeLinkId ? ` id="${narrativeLinkId}"` : ""}>
1158
- <td>${medication}<ul></ul></td>
1159
- <td>${sig}</td>
1160
- <td>${dispenseQuantity}</td>
1161
- <td>${refills}</td>
1162
- <td>${startDate}</td>
1163
- <td>${endDate}</td>
1164
- <td>${status}</td>
1165
- </tr>`;
1166
- }
1167
- html += `
1168
- </tbody>
1169
- </table>`;
1170
- return html;
1171
- }
1172
- /**
1173
- * Render HTML table for MedicationStatement resources
1174
- * @param templateUtilities - Instance of TemplateUtilities for utility functions
1175
- * @param medications - Array of MedicationStatement resources
1176
- * @returns HTML string for rendering
1177
- */
1178
- static renderMedicationStatements(templateUtilities, medications) {
1179
- let html = `
1180
- <table>
1181
- <thead>
1182
- <tr>
1183
- <th>Medication</th>
1184
- <th>Sig</th>
1185
- <th>Dispense Quantity</th>
1186
- <th>Refills</th>
1187
- <th>Start Date</th>
1188
- <th>End Date</th>
1189
- <th>Status</th>
1190
- </tr>
1191
- </thead>
1192
- <tbody>`;
1193
- for (const { resource: ms, extension } of medications) {
1194
- const narrativeLinkId = templateUtilities.narrativeLinkId(extension);
1195
- const status = ms.status ? String(ms.status) : "-";
1196
- const medication = templateUtilities.getMedicationName(
1197
- ms.medicationReference || ms.medicationCodeableConcept
1198
- );
1199
- const sig = templateUtilities.concat(ms.dosage, "text") || "-";
1200
- const dispenseQuantity = "-";
1201
- const refills = "-";
1202
- let startDate = "-";
1203
- let endDate = "-";
1204
- if (ms.effectiveDateTime) {
1205
- startDate = ms.effectiveDateTime;
1206
- } else if (ms.effectivePeriod) {
1207
- startDate = ms.effectivePeriod.start || "-";
1208
- endDate = ms.effectivePeriod.end || "-";
1314
+ const ms = medication.resource;
1315
+ type = "Statement";
1316
+ status = ms.status ? String(ms.status) : "-";
1317
+ medicationName = templateUtilities.getMedicationName(
1318
+ ms.medicationReference || ms.medicationCodeableConcept
1319
+ );
1320
+ sig = templateUtilities.concat(ms.dosage, "text") || "-";
1321
+ if (ms.effectiveDateTime) {
1322
+ startDate = ms.effectiveDateTime;
1323
+ } else if (ms.effectivePeriod) {
1324
+ startDate = ms.effectivePeriod.start || "-";
1325
+ endDate = ms.effectivePeriod.end || "-";
1326
+ }
1209
1327
  }
1210
1328
  html += `
1211
1329
  <tr${narrativeLinkId ? ` id="${narrativeLinkId}"` : ""}>
1212
- <td>${medication}<ul></ul></td>
1330
+ <td>${type}</td>
1331
+ <td>${medicationName}<ul></ul></td>
1213
1332
  <td>${sig}</td>
1214
1333
  <td>${dispenseQuantity}</td>
1215
1334
  <td>${refills}</td>
@@ -1234,6 +1353,13 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
1234
1353
  * @returns HTML string for rendering
1235
1354
  */
1236
1355
  generateNarrative(resource, timezone) {
1356
+ if (resource.entry && Array.isArray(resource.entry)) {
1357
+ resource.entry.sort((a, b) => {
1358
+ const dateA = a.resource?.occurrenceDateTime;
1359
+ const dateB = b.resource?.occurrenceDateTime;
1360
+ return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
1361
+ });
1362
+ }
1237
1363
  return _ImmunizationsTemplate.generateStaticNarrative(resource, timezone);
1238
1364
  }
1239
1365
  /**
@@ -1245,7 +1371,6 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
1245
1371
  static generateStaticNarrative(resource, timezone) {
1246
1372
  const templateUtilities = new TemplateUtilities(resource);
1247
1373
  let html = `
1248
- <h5>Immunizations</h5>
1249
1374
  <table>
1250
1375
  <thead>
1251
1376
  <tr>
@@ -1303,81 +1428,35 @@ var ProblemListTemplate = class _ProblemListTemplate {
1303
1428
  static generateStaticNarrative(resource, timezone) {
1304
1429
  const templateUtilities = new TemplateUtilities(resource);
1305
1430
  let html = ``;
1306
- const activeConditions = [];
1307
- const resolvedConditions = [];
1308
- if (resource.entry && Array.isArray(resource.entry)) {
1309
- for (const entry of resource.entry) {
1310
- const cond = entry.resource;
1311
- if (cond.resourceType === "Composition") {
1312
- continue;
1313
- }
1314
- const isResolved = cond.clinicalStatus?.coding?.some((c) => c.code === "resolved" || c.code === "inactive" || c.display?.toLowerCase().includes("resolved"));
1315
- if (isResolved) {
1316
- resolvedConditions.push(cond);
1317
- } else {
1318
- activeConditions.push(cond);
1319
- }
1320
- }
1321
- }
1322
- if (activeConditions.length > 0) {
1323
- html += `<div class="ActiveProblems">
1324
- <h3>Active Problems</h3>
1325
- <table class="ActiveProblemTable">
1326
- <thead>
1327
- <tr>
1328
- <th>Problem</th>
1329
- <th>Priority</th>
1330
- <th>Noted Date</th>
1331
- <th>Diagnosed Date</th>
1332
- <th>Notes</th>
1333
- </tr>
1334
- </thead>
1335
- <tbody>`;
1336
- for (const cond of activeConditions) {
1337
- html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
1338
- <td class="Name">${templateUtilities.codeableConcept(cond.code)}</td>
1339
- <td class="Priority">${templateUtilities.codeableConcept(cond.severity)}</td>
1340
- <td class="NotedDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
1341
- <td class="DiagnosedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
1342
- <td class="Notes">${templateUtilities.renderNotes(cond.note, timezone, { styled: true, warning: true })}</td>
1343
- </tr>`;
1344
- }
1345
- html += `</tbody>
1346
- </table>
1347
- </div>`;
1348
- }
1349
- if (activeConditions.length > 0 && resolvedConditions.length > 0) {
1350
- html += `<br />`;
1351
- }
1352
- if (resolvedConditions.length > 0) {
1353
- html += `<div class="ResolvedProblems">
1354
- <h3>Resolved Problems</h3>
1355
- <table class="ResolvedProblemTable">
1431
+ const activeConditions = resource.entry?.map((entry) => entry.resource) || [];
1432
+ activeConditions.sort((a, b) => {
1433
+ const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
1434
+ const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
1435
+ return dateB - dateA;
1436
+ });
1437
+ html += `
1438
+ <table>
1356
1439
  <thead>
1357
1440
  <tr>
1358
1441
  <th>Problem</th>
1359
- <th>Priority</th>
1360
- <th>Noted Date</th>
1361
- <th>Diagnosed Date</th>
1362
- <th>Resolved Date</th>
1442
+ <th>Severity</th>
1443
+ <th>Onset Date</th>
1444
+ <th>Recorded Date</th>
1363
1445
  <th>Notes</th>
1364
1446
  </tr>
1365
1447
  </thead>
1366
1448
  <tbody>`;
1367
- for (const cond of resolvedConditions) {
1368
- html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
1449
+ for (const cond of activeConditions) {
1450
+ html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
1369
1451
  <td class="Name">${templateUtilities.codeableConcept(cond.code)}</td>
1370
- <td class="Priority">${templateUtilities.codeableConcept(cond.severity)}</td>
1371
- <td class="NotedDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
1372
- <td class="DiagnosedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
1373
- <td class="ResolvedDate">${templateUtilities.renderDate(cond.abatementDateTime)}</td>
1374
- <td class="Notes">${templateUtilities.renderNotes(cond.note, timezone, { styled: true, warning: true })}</td>
1452
+ <td class="Severity">${templateUtilities.codeableConcept(cond.severity)}</td>
1453
+ <td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
1454
+ <td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
1455
+ <td class="Notes">${templateUtilities.renderNotes(cond.note, timezone)}</td>
1375
1456
  </tr>`;
1376
- }
1377
- html += `</tbody>
1378
- </table>
1379
- </div>`;
1380
1457
  }
1458
+ html += `</tbody>
1459
+ </table>`;
1381
1460
  return html;
1382
1461
  }
1383
1462
  };
@@ -1401,8 +1480,13 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
1401
1480
  */
1402
1481
  static generateStaticNarrative(resource, timezone) {
1403
1482
  const templateUtilities = new TemplateUtilities(resource);
1483
+ const observations = resource.entry?.map((entry) => entry.resource) || [];
1484
+ observations.sort((a, b) => {
1485
+ const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
1486
+ const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
1487
+ return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
1488
+ });
1404
1489
  let html = `
1405
- <h5>Vital Signs</h5>
1406
1490
  <table>
1407
1491
  <thead>
1408
1492
  <tr>
@@ -1416,13 +1500,8 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
1416
1500
  </tr>
1417
1501
  </thead>
1418
1502
  <tbody>`;
1419
- if (resource.entry && Array.isArray(resource.entry)) {
1420
- for (const entry of resource.entry) {
1421
- const obs = entry.resource;
1422
- if (obs.resourceType === "Composition") {
1423
- continue;
1424
- }
1425
- html += `
1503
+ for (const obs of observations) {
1504
+ html += `
1426
1505
  <tr id="${templateUtilities.narrativeLinkId(obs)}">
1427
1506
  <td>${templateUtilities.codeableConcept(obs.code, "display")}</td>
1428
1507
  <td>${templateUtilities.extractObservationValue(obs)}</td>
@@ -1430,9 +1509,8 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
1430
1509
  <td>${templateUtilities.firstFromCodeableConceptList(obs.interpretation)}</td>
1431
1510
  <td>${templateUtilities.renderComponent(obs.component)}</td>
1432
1511
  <td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
1433
- <td>${templateUtilities.renderEffective(obs.effectiveDateTime, timezone)}</td>
1512
+ <td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
1434
1513
  </tr>`;
1435
- }
1436
1514
  }
1437
1515
  html += `
1438
1516
  </tbody>
@@ -1450,6 +1528,13 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
1450
1528
  * @returns HTML string for rendering
1451
1529
  */
1452
1530
  generateNarrative(resource, timezone) {
1531
+ if (resource.entry && Array.isArray(resource.entry)) {
1532
+ resource.entry.sort((a, b) => {
1533
+ const dateA = a.resource?.recordedOn;
1534
+ const dateB = b.resource?.recordedOn;
1535
+ return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
1536
+ });
1537
+ }
1453
1538
  return _MedicalDevicesTemplate.generateStaticNarrative(resource, timezone);
1454
1539
  }
1455
1540
  /**
@@ -1461,7 +1546,6 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
1461
1546
  static generateStaticNarrative(resource, timezone) {
1462
1547
  const templateUtilities = new TemplateUtilities(resource);
1463
1548
  let html = `
1464
- <h5>Medical Devices</h5>
1465
1549
  <table>
1466
1550
  <thead>
1467
1551
  <tr>
@@ -1515,10 +1599,20 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
1515
1599
  let html = "";
1516
1600
  const observations = this.getObservations(resource);
1517
1601
  if (observations.length > 0) {
1602
+ observations.sort((a, b) => {
1603
+ const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
1604
+ const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
1605
+ return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
1606
+ });
1518
1607
  html += this.renderObservations(templateUtilities, observations, timezone);
1519
1608
  }
1520
1609
  const diagnosticReports = this.getDiagnosticReports(resource);
1521
1610
  if (diagnosticReports.length > 0) {
1611
+ diagnosticReports.sort((a, b) => {
1612
+ const dateA = a.issued;
1613
+ const dateB = b.issued;
1614
+ return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
1615
+ });
1522
1616
  html += this.renderDiagnosticReports(templateUtilities, diagnosticReports, timezone);
1523
1617
  }
1524
1618
  return html;
@@ -1554,7 +1648,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
1554
1648
  */
1555
1649
  static renderObservations(templateUtilities, observations, timezone) {
1556
1650
  let html = `
1557
- <h5>Diagnostic Results: Observations</h5>
1651
+ <h5>Observations</h5>
1558
1652
  <table>
1559
1653
  <thead>
1560
1654
  <tr>
@@ -1577,7 +1671,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
1577
1671
  <td>${templateUtilities.firstFromCodeableConceptList(obs.interpretation)}</td>
1578
1672
  <td>${templateUtilities.concatReferenceRange(obs.referenceRange)}</td>
1579
1673
  <td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
1580
- <td>${templateUtilities.renderTime(obs.effectiveDateTime, timezone)}</td>
1674
+ <td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
1581
1675
  </tr>`;
1582
1676
  }
1583
1677
  html += `
@@ -1594,7 +1688,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
1594
1688
  */
1595
1689
  static renderDiagnosticReports(templateUtilities, reports, timezone) {
1596
1690
  let html = `
1597
- <h5>Diagnostic Results: Reports</h5>
1691
+ <h5>Diagnostic Reports</h5>
1598
1692
  <table>
1599
1693
  <thead>
1600
1694
  <tr>
@@ -1636,6 +1730,11 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
1636
1730
  * @returns HTML string for rendering
1637
1731
  */
1638
1732
  generateNarrative(resource, timezone) {
1733
+ resource.entry?.sort((a, b) => {
1734
+ const dateA = a.resource.performedDateTime || a.resource.performedPeriod?.start;
1735
+ const dateB = b.resource.performedDateTime || b.resource.performedPeriod?.start;
1736
+ return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
1737
+ });
1639
1738
  return _HistoryOfProceduresTemplate.generateStaticNarrative(resource, timezone);
1640
1739
  }
1641
1740
  /**
@@ -1647,7 +1746,6 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
1647
1746
  static generateStaticNarrative(resource, timezone) {
1648
1747
  const templateUtilities = new TemplateUtilities(resource);
1649
1748
  let html = `
1650
- <h5>History Of Procedures</h5>
1651
1749
  <table>
1652
1750
  <thead>
1653
1751
  <tr>
@@ -1660,14 +1758,11 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
1660
1758
  if (resource.entry && Array.isArray(resource.entry)) {
1661
1759
  for (const entry of resource.entry) {
1662
1760
  const proc = entry.resource;
1663
- if (proc.resourceType === "Composition") {
1664
- continue;
1665
- }
1666
1761
  html += `
1667
1762
  <tr id="${templateUtilities.narrativeLinkId(proc)}">
1668
1763
  <td>${templateUtilities.codeableConcept(proc.code, "display")}</td>
1669
1764
  <td>${templateUtilities.renderNotes(proc.note, timezone)}</td>
1670
- <td>${templateUtilities.renderTime(proc.performedDateTime, timezone)}</td>
1765
+ <td>${proc.performedDateTime ? templateUtilities.renderTime(proc.performedDateTime, timezone) : proc.performedPeriod ? templateUtilities.renderPeriod(proc.performedPeriod, timezone) : ""}</td>
1671
1766
  </tr>`;
1672
1767
  }
1673
1768
  }
@@ -1697,8 +1792,13 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
1697
1792
  */
1698
1793
  static generateStaticNarrative(resource, timezone) {
1699
1794
  const templateUtilities = new TemplateUtilities(resource);
1795
+ const observations = resource.entry?.map((entry) => entry.resource) || [];
1796
+ observations.sort((a, b) => {
1797
+ const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
1798
+ const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
1799
+ return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
1800
+ });
1700
1801
  let html = `
1701
- <h5>Social History</h5>
1702
1802
  <table>
1703
1803
  <thead>
1704
1804
  <tr>
@@ -1710,21 +1810,15 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
1710
1810
  </tr>
1711
1811
  </thead>
1712
1812
  <tbody>`;
1713
- if (resource.entry && Array.isArray(resource.entry)) {
1714
- for (const entry of resource.entry) {
1715
- const obs = entry.resource;
1716
- if (obs.resourceType === "Composition") {
1717
- continue;
1718
- }
1719
- html += `
1813
+ for (const obs of observations) {
1814
+ html += `
1720
1815
  <tr id="${templateUtilities.narrativeLinkId(obs)}">
1721
- <td>${templateUtilities.codeableConcept(obs.code, "display")}</td>
1816
+ <td>${templateUtilities.codeableConcept(obs.code)}</td>
1722
1817
  <td>${templateUtilities.extractObservationValue(obs)}</td>
1723
1818
  <td>${templateUtilities.extractObservationValueUnit(obs)}</td>
1724
1819
  <td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
1725
- <td>${templateUtilities.renderTime(obs.effectiveDateTime, timezone)}</td>
1820
+ <td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
1726
1821
  </tr>`;
1727
- }
1728
1822
  }
1729
1823
  html += `
1730
1824
  </tbody>
@@ -1743,36 +1837,38 @@ var PastHistoryOfIllnessTemplate = class {
1743
1837
  */
1744
1838
  generateNarrative(resource, timezone) {
1745
1839
  const templateUtilities = new TemplateUtilities(resource);
1746
- let html = `
1747
- <h5>Past History of Illnesses</h5>
1748
- <table>
1749
- <thead>
1750
- <tr>
1751
- <th>Medical Problems</th>
1752
- <th>Status</th>
1753
- <th>Comments</th>
1754
- <th>Date</th>
1755
- </tr>
1756
- </thead>
1757
- <tbody>`;
1758
- if (resource.entry && Array.isArray(resource.entry)) {
1759
- for (const entry of resource.entry) {
1760
- const cond = entry.resource;
1761
- if (cond.resourceType === "Composition") {
1762
- continue;
1763
- }
1764
- html += `
1765
- <tr id="${templateUtilities.narrativeLinkId(cond)}">
1766
- <td>${templateUtilities.codeableConcept(cond.code, "display")}</td>
1767
- <td>${templateUtilities.codeableConcept(cond.clinicalStatus, "code")}</td>
1768
- <td>${templateUtilities.renderNotes(cond.note, timezone)}</td>
1769
- <td>${templateUtilities.renderTime(cond.onsetDateTime, timezone)}</td>
1770
- </tr>`;
1771
- }
1772
- }
1840
+ let html = ``;
1841
+ const resolvedConditions = resource.entry?.map((entry) => entry.resource) || [];
1842
+ resolvedConditions.sort((a, b) => {
1843
+ const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
1844
+ const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
1845
+ return dateB - dateA;
1846
+ });
1773
1847
  html += `
1774
- </tbody>
1775
- </table>`;
1848
+ <table>
1849
+ <thead>
1850
+ <tr>
1851
+ <th>Problem</th>
1852
+ <th>Severity</th>
1853
+ <th>Onset Date</th>
1854
+ <th>Recorded Date</th>
1855
+ <th>Resolved Date</th>
1856
+ <th>Notes</th>
1857
+ </tr>
1858
+ </thead>
1859
+ <tbody>`;
1860
+ for (const cond of resolvedConditions) {
1861
+ html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
1862
+ <td class="Name">${templateUtilities.codeableConcept(cond.code)}</td>
1863
+ <td class="Severity">${templateUtilities.codeableConcept(cond.severity)}</td>
1864
+ <td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
1865
+ <td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
1866
+ <td class="ResolvedDate">${templateUtilities.renderDate(cond.abatementDateTime)}</td>
1867
+ <td class="Notes">${templateUtilities.renderNotes(cond.note, timezone)}</td>
1868
+ </tr>`;
1869
+ }
1870
+ html += `</tbody>
1871
+ </table>`;
1776
1872
  return html;
1777
1873
  }
1778
1874
  };
@@ -1785,15 +1881,19 @@ var PlanOfCareTemplate = class {
1785
1881
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1786
1882
  * @returns HTML string for rendering
1787
1883
  */
1788
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1789
1884
  generateNarrative(resource, timezone) {
1790
1885
  const templateUtilities = new TemplateUtilities(resource);
1886
+ const carePlans = resource.entry?.map((entry) => entry.resource) || [];
1887
+ carePlans.sort((a, b) => {
1888
+ const endA = a.period?.end ? new Date(a.period?.end).getTime() : 0;
1889
+ const endB = b.period?.end ? new Date(b.period?.end).getTime() : 0;
1890
+ return endB - endA;
1891
+ });
1791
1892
  let html = `
1792
- <h5>Plan of Care</h5>
1793
1893
  <table>
1794
1894
  <thead>
1795
1895
  <tr>
1796
- <th>Activity</th>
1896
+ <th>Description</th>
1797
1897
  <th>Intent</th>
1798
1898
  <th>Comments</th>
1799
1899
  <th>Planned Start</th>
@@ -1801,21 +1901,15 @@ var PlanOfCareTemplate = class {
1801
1901
  </tr>
1802
1902
  </thead>
1803
1903
  <tbody>`;
1804
- if (resource.entry && Array.isArray(resource.entry)) {
1805
- for (const entry of resource.entry) {
1806
- const cp = entry.resource;
1807
- if (cp.resourceType === "Composition") {
1808
- continue;
1809
- }
1810
- html += `
1904
+ for (const cp of carePlans) {
1905
+ html += `
1811
1906
  <tr id="${templateUtilities.narrativeLinkId(cp)}">
1812
- <td>${cp.description || ""}</td>
1813
- <td>${cp.intent || cp.intent || ""}</td>
1907
+ <td>${cp.description || cp.title || ""}</td>
1908
+ <td>${cp.intent || ""}</td>
1814
1909
  <td>${templateUtilities.concat(cp.note, "text")}</td>
1815
- <td>${cp.period?.start || ""}</td>
1816
- <td>${cp.period?.end || ""}</td>
1910
+ <td>${cp.period?.start ? templateUtilities.renderTime(cp.period?.start, timezone) : ""}</td>
1911
+ <td>${cp.period?.end ? templateUtilities.renderTime(cp.period?.end, timezone) : ""}</td>
1817
1912
  </tr>`;
1818
- }
1819
1913
  }
1820
1914
  html += `
1821
1915
  </tbody>
@@ -1843,38 +1937,111 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
1843
1937
  */
1844
1938
  static generateStaticNarrative(resource, timezone) {
1845
1939
  const templateUtilities = new TemplateUtilities(resource);
1846
- let html = `
1847
- <h5>Functional Status</h5>
1940
+ let html = ``;
1941
+ const activeConditions = [];
1942
+ const clinicalImpressions = [];
1943
+ if (resource.entry && Array.isArray(resource.entry)) {
1944
+ for (const entry of resource.entry) {
1945
+ if (entry.resource?.resourceType === "Condition") {
1946
+ const cond = entry.resource;
1947
+ const isResolved = cond.clinicalStatus?.coding?.some(
1948
+ (c) => c.code === "resolved" || c.code === "inactive" || c.display?.toLowerCase().includes("resolved")
1949
+ );
1950
+ if (!isResolved) {
1951
+ activeConditions.push(cond);
1952
+ }
1953
+ } else if (entry.resource?.resourceType === "ClinicalImpression") {
1954
+ clinicalImpressions.push(entry.resource);
1955
+ }
1956
+ }
1957
+ }
1958
+ activeConditions.sort((a, b) => {
1959
+ const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
1960
+ const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
1961
+ return dateB - dateA;
1962
+ });
1963
+ clinicalImpressions.sort((a, b) => {
1964
+ const dateA = a.effectiveDateTime ? new Date(a.effectiveDateTime).getTime() : a.effectivePeriod?.start ? new Date(a.effectivePeriod.start).getTime() : a.date ? new Date(a.date).getTime() : 0;
1965
+ const dateB = b.effectiveDateTime ? new Date(b.effectiveDateTime).getTime() : b.effectivePeriod?.start ? new Date(b.effectivePeriod.start).getTime() : b.date ? new Date(b.date).getTime() : 0;
1966
+ return dateB - dateA;
1967
+ });
1968
+ if (activeConditions.length > 0) {
1969
+ html += `<h3>Conditions</h3>
1970
+ <table>
1971
+ <thead>
1972
+ <tr>
1973
+ <th>Problem</th>
1974
+ <th>Severity</th>
1975
+ <th>Onset Date</th>
1976
+ <th>Recorded Date</th>
1977
+ <th>Notes</th>
1978
+ </tr>
1979
+ </thead>
1980
+ <tbody>`;
1981
+ for (const cond of activeConditions) {
1982
+ html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
1983
+ <td class="Name">${templateUtilities.codeableConcept(cond.code)}</td>
1984
+ <td class="Severity">${templateUtilities.codeableConcept(cond.severity)}</td>
1985
+ <td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
1986
+ <td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
1987
+ <td class="Notes">${templateUtilities.renderNotes(cond.note, timezone, { styled: true, warning: true })}</td>
1988
+ </tr>`;
1989
+ }
1990
+ html += `</tbody>
1991
+ </table>
1992
+ </div>`;
1993
+ }
1994
+ if (clinicalImpressions.length > 0) {
1995
+ html += `<h3>Clinical Impressions</h3>
1848
1996
  <table>
1849
1997
  <thead>
1850
1998
  <tr>
1851
- <th>Assessment</th>
1852
- <th>Status</th>
1853
- <th>Finding</th>
1854
- <th>Comments</th>
1855
1999
  <th>Date</th>
2000
+ <th>Status</th>
2001
+ <th>Description</th>
2002
+ <th>Summary</th>
2003
+ <th>Findings</th>
2004
+ <th>Notes</th>
1856
2005
  </tr>
1857
2006
  </thead>
1858
2007
  <tbody>`;
1859
- if (resource.entry && Array.isArray(resource.entry)) {
1860
- for (const entry of resource.entry) {
1861
- const ci = entry.resource;
1862
- if (ci.resourceType === "Composition") {
1863
- continue;
2008
+ for (const impression of clinicalImpressions) {
2009
+ let formattedDate = "";
2010
+ if (impression.effectiveDateTime) {
2011
+ formattedDate = templateUtilities.renderTime(
2012
+ impression.effectiveDateTime,
2013
+ timezone
2014
+ );
2015
+ } else if (impression.effectivePeriod) {
2016
+ formattedDate = templateUtilities.renderPeriod(
2017
+ impression.effectivePeriod,
2018
+ timezone
2019
+ );
2020
+ } else if (impression.date) {
2021
+ formattedDate = templateUtilities.renderDate(impression.date);
2022
+ }
2023
+ let findingsHtml = "";
2024
+ if (impression.finding && impression.finding.length > 0) {
2025
+ findingsHtml = "<ul>";
2026
+ for (const finding of impression.finding) {
2027
+ const findingText = finding.itemCodeableConcept ? templateUtilities.codeableConcept(finding.itemCodeableConcept) : finding.itemReference ? templateUtilities.renderReference(finding.itemReference) : "";
2028
+ const cause = finding.basis || "";
2029
+ findingsHtml += `<li>${findingText}${cause ? ` - ${cause}` : ""}</li>`;
2030
+ }
2031
+ findingsHtml += "</ul>";
1864
2032
  }
2033
+ const notes = templateUtilities.renderNotes(impression.note, timezone);
1865
2034
  html += `
1866
- <tr id="${templateUtilities.narrativeLinkId(ci)}">
1867
- <td>${templateUtilities.codeableConcept(ci.code, "display")}</td>
1868
- <td>${ci.status || ""}</td>
1869
- <td>${ci.summary || ""}</td>
1870
- <td>${templateUtilities.renderNotes(ci.note, timezone)}</td>
1871
- <td>${templateUtilities.renderEffective(ci.effectiveDateTime, timezone)}</td>
2035
+ <tr id="${templateUtilities.narrativeLinkId(impression)}">
2036
+ <td>${formattedDate}</td>
2037
+ <td>${impression.status || ""}</td>
2038
+ <td>${impression.description || ""}</td>
2039
+ <td>${impression.summary || ""}</td>
2040
+ <td>${findingsHtml}</td>
2041
+ <td>${notes}</td>
1872
2042
  </tr>`;
1873
2043
  }
1874
2044
  }
1875
- html += `
1876
- </tbody>
1877
- </table>`;
1878
2045
  return html;
1879
2046
  }
1880
2047
  };
@@ -1898,32 +2065,30 @@ var PregnancyTemplate = class _PregnancyTemplate {
1898
2065
  */
1899
2066
  static generateStaticNarrative(resource, timezone) {
1900
2067
  const templateUtilities = new TemplateUtilities(resource);
2068
+ const observations = resource.entry?.map((entry) => entry.resource) || [];
2069
+ observations.sort((a, b) => {
2070
+ const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
2071
+ const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
2072
+ return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
2073
+ });
1901
2074
  let html = `
1902
- <h5>Pregnancy</h5>
1903
- <table>
1904
- <thead>
1905
- <tr>
1906
- <th>Code</th>
1907
- <th>Result</th>
1908
- <th>Comments</th>
1909
- <th>Date</th>
1910
- </tr>
1911
- </thead>
1912
- <tbody>`;
1913
- if (resource.entry && Array.isArray(resource.entry)) {
1914
- for (const entry of resource.entry) {
1915
- const obs = entry.resource;
1916
- if (obs.resourceType === "Composition") {
1917
- continue;
1918
- }
1919
- html += `
2075
+ <table>
2076
+ <thead>
2077
+ <tr>
2078
+ <th>Result</th>
2079
+ <th>Comments</th>
2080
+ <th>Date</th>
2081
+ </tr>
2082
+ </thead>
2083
+ <tbody>`;
2084
+ for (const resource2 of observations) {
2085
+ const obs = resource2;
2086
+ html += `
1920
2087
  <tr id="${templateUtilities.narrativeLinkId(obs)}">
1921
- <td>${templateUtilities.codeableConcept(obs.code, "display")}</td>
1922
- <td>${templateUtilities.extractObservationValue(obs)}</td>
2088
+ <td>${templateUtilities.extractPregnancyStatus(obs)}</td>
1923
2089
  <td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
1924
- <td>${templateUtilities.renderEffective(obs.effectiveDateTime, timezone)}</td>
2090
+ <td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
1925
2091
  </tr>`;
1926
- }
1927
2092
  }
1928
2093
  html += `
1929
2094
  </tbody>
@@ -1941,6 +2106,13 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
1941
2106
  * @returns HTML string for rendering
1942
2107
  */
1943
2108
  generateNarrative(resource, timezone) {
2109
+ if (resource.entry && Array.isArray(resource.entry)) {
2110
+ resource.entry.sort((a, b) => {
2111
+ const dateA = new Date(a.resource.dateTime || 0);
2112
+ const dateB = new Date(b.resource.dateTime || 0);
2113
+ return dateB.getTime() - dateA.getTime();
2114
+ });
2115
+ }
1944
2116
  return _AdvanceDirectivesTemplate.generateStaticNarrative(resource, timezone);
1945
2117
  }
1946
2118
  /**
@@ -1953,7 +2125,6 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
1953
2125
  static generateStaticNarrative(resource, timezone) {
1954
2126
  const templateUtilities = new TemplateUtilities(resource);
1955
2127
  let html = `
1956
- <h5>Advance Directives</h5>
1957
2128
  <table>
1958
2129
  <thead>
1959
2130
  <tr>
@@ -1967,9 +2138,6 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
1967
2138
  if (resource.entry && Array.isArray(resource.entry)) {
1968
2139
  for (const entry of resource.entry) {
1969
2140
  const consent = entry.resource;
1970
- if (consent.resourceType === "Composition") {
1971
- continue;
1972
- }
1973
2141
  html += `
1974
2142
  <tr id="${templateUtilities.narrativeLinkId(consent)}">
1975
2143
  <td>${templateUtilities.codeableConcept(consent.scope, "display")}</td>
@@ -2067,90 +2235,6 @@ var FamilyHistoryTemplate = class {
2067
2235
  }
2068
2236
  };
2069
2237
 
2070
- // src/narratives/templates/typescript/ClinicalImpressionTemplate.ts
2071
- var ClinicalImpressionTemplate = class {
2072
- /**
2073
- * Generate HTML narrative for Clinical Impressions
2074
- * @param resource - FHIR Bundle containing ClinicalImpression resources
2075
- * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
2076
- * @returns HTML string for rendering
2077
- */
2078
- generateNarrative(resource, timezone) {
2079
- const templateUtilities = new TemplateUtilities(resource);
2080
- let html = `
2081
- <h5>Clinical Impressions</h5>
2082
- <table>
2083
- <thead>
2084
- <tr>
2085
- <th>Date</th>
2086
- <th>Status</th>
2087
- <th>Description</th>
2088
- <th>Summary</th>
2089
- <th>Findings</th>
2090
- <th>Notes</th>
2091
- </tr>
2092
- </thead>
2093
- <tbody>`;
2094
- if (resource.entry && Array.isArray(resource.entry)) {
2095
- let hasClinicalImpressions = false;
2096
- for (const entry of resource.entry) {
2097
- const res = entry.resource;
2098
- if (!res || res.resourceType !== "ClinicalImpression") {
2099
- continue;
2100
- }
2101
- hasClinicalImpressions = true;
2102
- const impression = res;
2103
- let formattedDate = "";
2104
- if (impression.effectiveDateTime) {
2105
- formattedDate = templateUtilities.renderTime(impression.effectiveDateTime, timezone);
2106
- } else if (impression.effectivePeriod) {
2107
- formattedDate = templateUtilities.renderPeriod(impression.effectivePeriod, timezone);
2108
- } else if (impression.date) {
2109
- formattedDate = templateUtilities.renderDate(impression.date);
2110
- }
2111
- const status = impression.status || "";
2112
- const description = impression.description || "";
2113
- const summary = impression.summary || "";
2114
- let findingsHtml = "";
2115
- if (impression.finding && impression.finding.length > 0) {
2116
- findingsHtml = "<ul>";
2117
- for (const finding of impression.finding) {
2118
- const findingText = finding.itemCodeableConcept ? templateUtilities.codeableConcept(finding.itemCodeableConcept, "display") : finding.itemReference ? templateUtilities.renderReference(finding.itemReference) : "";
2119
- const cause = finding.basis || "";
2120
- findingsHtml += `<li>${findingText}${cause ? ` - ${cause}` : ""}</li>`;
2121
- }
2122
- findingsHtml += "</ul>";
2123
- }
2124
- const notes = templateUtilities.renderNotes(impression.note, timezone);
2125
- html += `
2126
- <tr id="${templateUtilities.narrativeLinkId(impression)}">
2127
- <td>${formattedDate}</td>
2128
- <td>${status}</td>
2129
- <td>${description}</td>
2130
- <td>${summary}</td>
2131
- <td>${findingsHtml}</td>
2132
- <td>${notes}</td>
2133
- </tr>`;
2134
- }
2135
- if (!hasClinicalImpressions) {
2136
- html += `
2137
- <tr>
2138
- <td colspan="6">No clinical impressions recorded</td>
2139
- </tr>`;
2140
- }
2141
- } else {
2142
- html += `
2143
- <tr>
2144
- <td colspan="6">No clinical impressions recorded</td>
2145
- </tr>`;
2146
- }
2147
- html += `
2148
- </tbody>
2149
- </table>`;
2150
- return html;
2151
- }
2152
- };
2153
-
2154
2238
  // src/narratives/templates/typescript/TypeScriptTemplateMapper.ts
2155
2239
  var TypeScriptTemplateMapper = class {
2156
2240
  /**
@@ -2173,20 +2257,19 @@ var TypeScriptTemplateMapper = class {
2173
2257
  TypeScriptTemplateMapper.sectionToTemplate = {
2174
2258
  ["Patient" /* PATIENT */]: new PatientTemplate(),
2175
2259
  ["AllergyIntoleranceSection" /* ALLERGIES */]: new AllergyIntoleranceTemplate(),
2176
- ["MedicationSection" /* MEDICATIONS */]: new MedicationSummaryTemplate(),
2260
+ ["MedicationSummarySection" /* MEDICATIONS */]: new MedicationSummaryTemplate(),
2177
2261
  ["ImmunizationSection" /* IMMUNIZATIONS */]: new ImmunizationsTemplate(),
2178
2262
  ["ProblemSection" /* PROBLEMS */]: new ProblemListTemplate(),
2179
2263
  ["VitalSignsSection" /* VITAL_SIGNS */]: new VitalSignsTemplate(),
2180
2264
  ["MedicalDeviceSection" /* MEDICAL_DEVICES */]: new MedicalDevicesTemplate(),
2181
- ["DiagnosticReportSection" /* DIAGNOSTIC_REPORTS */]: new DiagnosticResultsTemplate(),
2182
- ["ProcedureSection" /* PROCEDURES */]: new HistoryOfProceduresTemplate(),
2265
+ ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: new DiagnosticResultsTemplate(),
2266
+ ["HistoryOfProceduresSection" /* PROCEDURES */]: new HistoryOfProceduresTemplate(),
2183
2267
  ["FamilyHistorySection" /* FAMILY_HISTORY */]: new FamilyHistoryTemplate(),
2184
2268
  ["SocialHistorySection" /* SOCIAL_HISTORY */]: new SocialHistoryTemplate(),
2185
- ["PregnancyHistorySection" /* PREGNANCY_HISTORY */]: new PregnancyTemplate(),
2269
+ ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: new PregnancyTemplate(),
2186
2270
  ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: new FunctionalStatusTemplate(),
2187
- ["MedicalHistorySection" /* MEDICAL_HISTORY */]: new PastHistoryOfIllnessTemplate(),
2188
- ["CarePlanSection" /* CARE_PLAN */]: new PlanOfCareTemplate(),
2189
- ["ClinicalImpressionSection" /* CLINICAL_IMPRESSION */]: new ClinicalImpressionTemplate(),
2271
+ ["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: new PastHistoryOfIllnessTemplate(),
2272
+ ["PlanOfCareSection" /* CARE_PLAN */]: new PlanOfCareTemplate(),
2190
2273
  ["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: new AdvanceDirectivesTemplate()
2191
2274
  };
2192
2275
 
@@ -2223,10 +2306,9 @@ var NarrativeGenerator = class {
2223
2306
  * @param section - IPS section type
2224
2307
  * @param resources - Array of domain resources
2225
2308
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
2226
- * @param wrapInXhtml - Whether to wrap the content in XHTML div
2227
2309
  * @returns Generated HTML content or undefined if no resources
2228
2310
  */
2229
- static async generateNarrativeContentAsync(section, resources, timezone, wrapInXhtml = true) {
2311
+ static async generateNarrativeContentAsync(section, resources, timezone) {
2230
2312
  if (!resources || resources.length === 0) {
2231
2313
  return void 0;
2232
2314
  }
@@ -2242,11 +2324,7 @@ var NarrativeGenerator = class {
2242
2324
  if (!content) {
2243
2325
  return void 0;
2244
2326
  }
2245
- if (wrapInXhtml) {
2246
- return await this.wrapInXhtmlAsync(content);
2247
- } else {
2248
- return await this.minifyHtmlAsync(content);
2249
- }
2327
+ return content;
2250
2328
  } catch (error) {
2251
2329
  console.error(`Error generating narrative for section ${section}:`, error);
2252
2330
  return `<div class="error">Error generating narrative: ${error instanceof Error ? error.message : String(error)}</div>`;
@@ -2293,35 +2371,35 @@ var NarrativeGenerator = class {
2293
2371
  * @param resources - Array of domain resources
2294
2372
  * @param timezone - Optional timezone to use for date formatting
2295
2373
  * @param minify - Whether to minify the HTML content (default: true)
2296
- * @param wrapInXhtml - Whether to wrap the content in XHTML div
2297
2374
  * @returns Promise that resolves to a FHIR Narrative object or undefined if no resources
2298
2375
  */
2299
- static async generateNarrativeAsync(section, resources, timezone, minify = true, wrapInXhtml) {
2300
- const content = await this.generateNarrativeContentAsync(section, resources, timezone, wrapInXhtml);
2376
+ static async generateNarrativeAsync(section, resources, timezone, minify = true) {
2377
+ const content = await this.generateNarrativeContentAsync(section, resources, timezone);
2301
2378
  if (!content) {
2302
2379
  return void 0;
2303
2380
  }
2304
2381
  return await this.createNarrativeAsync(content, minify);
2305
2382
  }
2306
- /**
2307
- * Wrap content in XHTML div with FHIR namespace asynchronously
2308
- * @param content - HTML content to wrap
2309
- * @param minify - Whether to minify the HTML content before wrapping (default: false)
2310
- * @returns Promise that resolves to XHTML div string
2311
- */
2312
- static async wrapInXhtmlAsync(content, minify = false) {
2313
- if (minify) {
2314
- content = await this.minifyHtmlAsync(content);
2315
- }
2316
- return `<div xmlns="http://www.w3.org/1999/xhtml">${content}</div>`;
2317
- }
2383
+ };
2384
+
2385
+ // src/structures/ips_mandatory_sections.ts
2386
+ var IPSMandatorySections = ((IPSMandatorySections2) => {
2387
+ IPSMandatorySections2[IPSMandatorySections2["PATIENT"] = "Patient" /* PATIENT */] = "PATIENT";
2388
+ IPSMandatorySections2[IPSMandatorySections2["PROBLEMS"] = "ProblemSection" /* PROBLEMS */] = "PROBLEMS";
2389
+ IPSMandatorySections2[IPSMandatorySections2["ALLERGIES"] = "AllergyIntoleranceSection" /* ALLERGIES */] = "ALLERGIES";
2390
+ IPSMandatorySections2[IPSMandatorySections2["MEDICATIONS"] = "MedicationSummarySection" /* MEDICATIONS */] = "MEDICATIONS";
2391
+ return IPSMandatorySections2;
2392
+ })(IPSMandatorySections || {});
2393
+ var IPSMissingMandatorySectionContent = {
2394
+ ["ProblemSection" /* PROBLEMS */]: "There is no information available about the subject's health problems or disabilities.",
2395
+ ["AllergyIntoleranceSection" /* ALLERGIES */]: "There is no information available regarding the subject's allergy conditions.",
2396
+ ["MedicationSummarySection" /* MEDICATIONS */]: "There is no information available about the subject's medication use or administration."
2318
2397
  };
2319
2398
 
2320
2399
  // src/generators/fhir_summary_generator.ts
2321
2400
  var ComprehensiveIPSCompositionBuilder = class {
2322
2401
  constructor() {
2323
2402
  this.sections = [];
2324
- this.mandatorySectionsAdded = /* @__PURE__ */ new Set();
2325
2403
  this.resources = /* @__PURE__ */ new Set();
2326
2404
  }
2327
2405
  /**
@@ -2339,35 +2417,35 @@ var ComprehensiveIPSCompositionBuilder = class {
2339
2417
  /**
2340
2418
  * Adds a section to the composition with async HTML minification
2341
2419
  * @param sectionType - IPS section type
2342
- * @param resources - Array of domain resources
2420
+ * @param validResources - Array of domain resources
2343
2421
  * @param timezone - Optional timezone to use for date formatting
2344
- * @param options - Optional configuration options
2345
2422
  */
2346
- async addSectionAsync(sectionType, resources, timezone, options) {
2347
- const validResources = resources;
2423
+ async addSectionAsync(sectionType, validResources, timezone) {
2348
2424
  for (const resource of validResources) {
2349
2425
  this.resources.add(resource);
2350
2426
  }
2351
- if (validResources.length === 0) {
2352
- if (!options?.isOptional) {
2353
- throw new Error(`No valid resources for mandatory section: ${sectionType}`);
2354
- }
2355
- return this;
2356
- }
2357
2427
  if (sectionType !== "Patient" /* PATIENT */) {
2358
- const narrative = await NarrativeGenerator.generateNarrativeAsync(
2359
- sectionType,
2360
- validResources,
2361
- timezone,
2362
- true,
2363
- false
2364
- );
2428
+ let narrative = void 0;
2429
+ if (validResources.length > 0) {
2430
+ narrative = await NarrativeGenerator.generateNarrativeAsync(
2431
+ sectionType,
2432
+ validResources,
2433
+ timezone,
2434
+ true
2435
+ );
2436
+ } else if (sectionType in IPSMandatorySections) {
2437
+ narrative = await NarrativeGenerator.createNarrativeAsync(
2438
+ IPSMissingMandatorySectionContent[sectionType]
2439
+ );
2440
+ } else {
2441
+ return this;
2442
+ }
2365
2443
  const sectionEntry = {
2366
2444
  title: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType,
2367
2445
  code: {
2368
2446
  coding: [{
2369
2447
  system: "http://loinc.org",
2370
- code: options?.customLoincCode || IPS_SECTION_LOINC_CODES[sectionType],
2448
+ code: IPS_SECTION_LOINC_CODES[sectionType],
2371
2449
  display: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType
2372
2450
  }],
2373
2451
  text: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType
@@ -2378,9 +2456,6 @@ var ComprehensiveIPSCompositionBuilder = class {
2378
2456
  display: resource.resourceType
2379
2457
  }))
2380
2458
  };
2381
- if (!options?.isOptional) {
2382
- this.mandatorySectionsAdded.add(sectionType);
2383
- }
2384
2459
  this.sections.push(sectionEntry);
2385
2460
  }
2386
2461
  return this;
@@ -2399,43 +2474,20 @@ var ComprehensiveIPSCompositionBuilder = class {
2399
2474
  throw new Error("Patient resource not found in the bundle");
2400
2475
  }
2401
2476
  this.patient = patientEntry.resource;
2477
+ const resources = bundle.entry.map((e) => e.resource);
2402
2478
  for (const sectionType of Object.values(IPSSections)) {
2403
2479
  const resourceTypesForSection = IPSSectionResourceHelper.getResourceTypesForSection(sectionType);
2404
2480
  const customFilter = IPSSectionResourceHelper.getResourceFilterForSection(sectionType);
2405
- let resources = bundle.entry.map((e) => e.resource).filter((r) => typeof r?.resourceType === "string" && resourceTypesForSection.includes(r.resourceType));
2481
+ let sectionResources = resources.filter(
2482
+ (r) => r && typeof r.resourceType === "string" && resourceTypesForSection.includes(r.resourceType)
2483
+ );
2406
2484
  if (customFilter) {
2407
- resources = resources.filter(customFilter);
2408
- }
2409
- if (resources.length > 0) {
2410
- await this.addSectionAsync(sectionType, resources, timezone, {
2411
- isOptional: true
2412
- });
2485
+ sectionResources = sectionResources.filter((resource) => resource && customFilter(resource));
2413
2486
  }
2487
+ await this.addSectionAsync(sectionType, sectionResources, timezone);
2414
2488
  }
2415
2489
  return this;
2416
2490
  }
2417
- /**
2418
- * Builds the final Composition sections, ensuring all mandatory sections are present.
2419
- * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
2420
- */
2421
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2422
- build(timezone) {
2423
- const mandatorySections = [
2424
- "AllergyIntoleranceSection" /* ALLERGIES */,
2425
- "MedicationSection" /* MEDICATIONS */,
2426
- "ProblemSection" /* PROBLEMS */,
2427
- "ImmunizationSection" /* IMMUNIZATIONS */
2428
- ];
2429
- const missingMandatorySections = mandatorySections.filter(
2430
- (section) => !this.mandatorySectionsAdded.has(section)
2431
- );
2432
- if (missingMandatorySections.length > 0) {
2433
- throw new Error(
2434
- `Missing mandatory IPS sections: ${missingMandatorySections.join(", ")}`
2435
- );
2436
- }
2437
- return this.sections;
2438
- }
2439
2491
  /**
2440
2492
  * Builds a complete FHIR Bundle containing the Composition and all resources.
2441
2493
  * @param authorOrganizationId - ID of the authoring organization (e.g., hospital or clinic)
@@ -2472,7 +2524,12 @@ var ComprehensiveIPSCompositionBuilder = class {
2472
2524
  date: (/* @__PURE__ */ new Date()).toISOString(),
2473
2525
  title: "International Patient Summary",
2474
2526
  section: this.sections,
2475
- text: await this.createCompositionNarrativeAsync(timezone)
2527
+ text: await NarrativeGenerator.generateNarrativeAsync(
2528
+ "Patient" /* PATIENT */,
2529
+ [this.patient],
2530
+ timezone,
2531
+ false
2532
+ )
2476
2533
  };
2477
2534
  const bundle = {
2478
2535
  resourceType: "Bundle",
@@ -2512,43 +2569,6 @@ var ComprehensiveIPSCompositionBuilder = class {
2512
2569
  });
2513
2570
  return bundle;
2514
2571
  }
2515
- /**
2516
- * Creates a narrative for the composition based on the patient and sections.
2517
- * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
2518
- * @private
2519
- */
2520
- async createCompositionNarrativeAsync(timezone) {
2521
- const patient = this.patient;
2522
- let fullNarrativeContent = "";
2523
- const patientNarrative = await NarrativeGenerator.generateNarrativeContentAsync(
2524
- "Patient" /* PATIENT */,
2525
- [patient],
2526
- timezone,
2527
- false
2528
- );
2529
- fullNarrativeContent = fullNarrativeContent.concat(patientNarrative || "");
2530
- for (const sectionType of Object.values(IPSSections)) {
2531
- if (sectionType === "Patient" /* PATIENT */) {
2532
- continue;
2533
- }
2534
- const resourceTypesForSection = IPSSectionResourceHelper.getResourceTypesForSection(sectionType);
2535
- const allResources = Array.from(this.resources);
2536
- const resources = allResources.filter((r) => resourceTypesForSection.includes(r.resourceType));
2537
- if (resources.length > 0) {
2538
- const sectionNarrative = await NarrativeGenerator.generateNarrativeContentAsync(
2539
- sectionType,
2540
- resources,
2541
- timezone,
2542
- false
2543
- );
2544
- fullNarrativeContent = fullNarrativeContent.concat(sectionNarrative || "");
2545
- }
2546
- }
2547
- return {
2548
- status: "generated",
2549
- div: await NarrativeGenerator.wrapInXhtmlAsync(fullNarrativeContent, true)
2550
- };
2551
- }
2552
2572
  };
2553
2573
 
2554
2574
  // src/index.ts