@imranq2/fhirpatientsummary 1.0.11 → 1.0.13

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,20 @@ 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";
48
46
  return IPSSections2;
49
47
  })(IPSSections || {});
50
48
 
@@ -52,99 +50,106 @@ var IPSSections = /* @__PURE__ */ ((IPSSections2) => {
52
50
  var IPS_SECTION_LOINC_CODES = {
53
51
  ["Patient" /* PATIENT */]: "54126-4",
54
52
  ["AllergyIntoleranceSection" /* ALLERGIES */]: "48765-2",
55
- ["MedicationSection" /* MEDICATIONS */]: "10160-0",
53
+ ["MedicationSummarySection" /* MEDICATIONS */]: "10160-0",
56
54
  ["ProblemSection" /* PROBLEMS */]: "11450-4",
57
55
  ["ImmunizationSection" /* IMMUNIZATIONS */]: "11369-6",
58
56
  ["VitalSignsSection" /* VITAL_SIGNS */]: "8716-3",
59
57
  ["MedicalDeviceSection" /* MEDICAL_DEVICES */]: "46264-8",
60
- ["DiagnosticReportSection" /* DIAGNOSTIC_REPORTS */]: "30954-2",
61
- ["ProcedureSection" /* PROCEDURES */]: "47519-4",
62
- ["FamilyHistorySection" /* FAMILY_HISTORY */]: "10157-6",
58
+ ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: "30954-2",
59
+ ["HistoryOfProceduresSection" /* PROCEDURES */]: "47519-4",
63
60
  ["SocialHistorySection" /* SOCIAL_HISTORY */]: "29762-2",
64
- ["PregnancyHistorySection" /* PREGNANCY_HISTORY */]: "10162-6",
61
+ ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: "10162-6",
65
62
  ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: "47420-5",
66
- ["MedicalHistorySection" /* MEDICAL_HISTORY */]: "11348-0",
67
- ["CarePlanSection" /* CARE_PLAN */]: "18776-5",
68
- ["ClinicalImpressionSection" /* CLINICAL_IMPRESSION */]: "51848-0",
63
+ ["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: "11348-0",
64
+ ["PlanOfCareSection" /* CARE_PLAN */]: "18776-5",
69
65
  ["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: "42348-3"
70
66
  };
71
67
  var IPS_SECTION_DISPLAY_NAMES = {
72
68
  ["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"
69
+ ["AllergyIntoleranceSection" /* ALLERGIES */]: "Allergies and Intolerances",
70
+ ["MedicationSummarySection" /* MEDICATIONS */]: "Medication Summary",
71
+ ["ProblemSection" /* PROBLEMS */]: "Problem List",
72
+ ["ImmunizationSection" /* IMMUNIZATIONS */]: "Immunizations",
73
+ ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: "Results Summary",
74
+ ["HistoryOfProceduresSection" /* PROCEDURES */]: "History of Procedures",
75
+ ["MedicalDeviceSection" /* MEDICAL_DEVICES */]: "History of Medical Devices",
76
+ ["VitalSignsSection" /* VITAL_SIGNS */]: "Vital Signs",
77
+ ["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: "Advance Directives",
78
+ ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: "Functional Status",
79
+ ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: "History of Pregnancies",
80
+ ["PlanOfCareSection" /* CARE_PLAN */]: "Plan of Care",
81
+ ["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: "History of Past Illness",
82
+ ["SocialHistorySection" /* SOCIAL_HISTORY */]: "Social History"
83
+ };
84
+ var PREGNANCY_LOINC_CODES = {
85
+ PREGNANCY_STATUS: {
86
+ "LA15173-0": "Pregnant",
87
+ "LA26683-5": "Not pregnant",
88
+ "LA4489-6": "Unknown"
89
+ },
90
+ PREGNANCY_OUTCOME: {
91
+ "11636-8": "Live Birth",
92
+ "11637-6": "Preterm Birth",
93
+ "11638-4": "Still Living Birth",
94
+ "11639-2": "Term Birth",
95
+ "11640-0": "Total Births",
96
+ "11612-9": "Abortions",
97
+ "11613-7": "Induced Abortions",
98
+ "11614-5": "Spontaneous Abortions",
99
+ "33065-4": "Ectopic Pregnancy"
100
+ }
101
+ };
102
+ var SOCIAL_HISTORY_LOINC_CODES = {
103
+ "72166-2": "Tobacco Use",
104
+ "74013-4": "Alcohol Use"
89
105
  };
90
106
 
91
107
  // src/structures/ips_section_resource_map.ts
92
108
  var IPSSectionResourceMap = {
93
109
  ["Patient" /* PATIENT */]: ["Patient"],
94
110
  ["AllergyIntoleranceSection" /* ALLERGIES */]: ["AllergyIntolerance"],
95
- ["MedicationSection" /* MEDICATIONS */]: ["MedicationRequest", "MedicationStatement"],
111
+ ["MedicationSummarySection" /* MEDICATIONS */]: ["MedicationRequest", "MedicationStatement", "Medication"],
112
+ // Medication resource is needed for identifying name of medication
96
113
  ["ProblemSection" /* PROBLEMS */]: ["Condition"],
97
- ["ImmunizationSection" /* IMMUNIZATIONS */]: ["Immunization"],
114
+ ["ImmunizationSection" /* IMMUNIZATIONS */]: ["Immunization", "Organization"],
115
+ // Immunization can include Organization as a related resource
98
116
  ["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"],
103
- ["FamilyHistorySection" /* FAMILY_HISTORY */]: ["FamilyMemberHistory"],
117
+ ["MedicalDeviceSection" /* MEDICAL_DEVICES */]: ["DeviceUseStatement", "Device"],
118
+ // Device resource is used for medical devices name
119
+ ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: ["DiagnosticReport", "Observation"],
120
+ ["HistoryOfProceduresSection" /* PROCEDURES */]: ["Procedure"],
104
121
  ["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
122
+ ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: ["Observation"],
123
+ ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: ["Condition", "ClinicalImpression"],
124
+ ["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: ["Condition"],
125
+ ["PlanOfCareSection" /* CARE_PLAN */]: ["CarePlan"],
126
+ ["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: ["Consent"]
116
127
  };
117
128
  var IPSSectionResourceFilters = {
118
129
  // 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",
130
+ ["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "AllergyIntolerance" && resource.clinicalStatus?.coding?.some((c) => typeof c.code === "string"),
122
131
  // Only include active problems/conditions
123
- ["ProblemSection" /* PROBLEMS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => c.code === "active"),
132
+ ["ProblemSection" /* PROBLEMS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => !["inactive", "resolved"].includes(c.code)),
124
133
  // Only include completed immunizations
125
- ["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Immunization" && resource.status === "completed",
134
+ ["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Immunization" && resource.status === "completed" || resource.resourceType === "Organization",
126
135
  // Only include vital sign Observations (category.coding contains 'vital-signs')
127
136
  ["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
137
  // Only include finalized diagnostic reports
131
- ["DiagnosticReportSection" /* DIAGNOSTIC_REPORTS */]: (resource) => ["DiagnosticReport", "Observation"].includes(resource.resourceType) && resource.status === "final",
138
+ ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => ["DiagnosticReport", "Observation"].includes(resource.resourceType) && resource.status === "final",
132
139
  // Only include completed procedures
133
- ["ProcedureSection" /* PROCEDURES */]: (resource) => resource.resourceType === "Procedure" && resource.status === "completed",
134
- // Only include family history resources
135
- ["FamilyHistorySection" /* FAMILY_HISTORY */]: (resource) => resource.resourceType === "FamilyMemberHistory",
140
+ ["HistoryOfProceduresSection" /* PROCEDURES */]: (resource) => resource.resourceType === "Procedure" && resource.status === "completed",
136
141
  // 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")),
142
+ ["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Observation" && resource.code?.coding?.some((c) => Object.keys(SOCIAL_HISTORY_LOINC_CODES).includes(c.code)),
138
143
  // 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"),
144
+ ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: (resource) => resource.resourceType === "Observation" && (resource.code?.coding?.some((c) => Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_STATUS).includes(c.code)) || resource.valueCodeableConcept?.coding?.some((c) => Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_OUTCOME).includes(c.code))),
145
+ // Only include active functional status Conditions or ClinicalImpressions
146
+ ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => typeof c.code === "string") || resource.resourceType === "ClinicalImpression" && resource.status === "completed",
147
+ // Only include resolved medical history Conditions
148
+ ["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => ["inactive", "resolved"].includes(c.code)),
144
149
  // 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",
150
+ ["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "CarePlan" && resource.status === "active",
151
+ // Only include active advance directives (Consent resources)
152
+ ["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Consent" && resource.status === "active",
148
153
  // Patient section: only Patient resource
149
154
  ["Patient" /* PATIENT */]: (resource) => resource.resourceType === "Patient"
150
155
  };
@@ -186,8 +191,6 @@ var TemplateUtilities = class {
186
191
  }
187
192
  if (cc.text) {
188
193
  return cc.text;
189
- } else if ("display" in cc && cc.display) {
190
- return cc.display;
191
194
  } else if (cc.coding && cc.coding[0]) {
192
195
  if (cc.coding[0].display) {
193
196
  return cc.coding[0].display;
@@ -219,8 +222,8 @@ var TemplateUtilities = class {
219
222
  */
220
223
  renderDevice(deviceRef) {
221
224
  const device = deviceRef && this.resolveReference(deviceRef);
222
- if (device && device.resourceType === "Device" && device.type) {
223
- return this.codeableConcept(device.type, "display");
225
+ if (device && device.resourceType === "Device" && device.deviceName && device.deviceName.length > 0) {
226
+ return this.safeConcat(device.deviceName, "name");
224
227
  }
225
228
  return "";
226
229
  }
@@ -401,8 +404,12 @@ var TemplateUtilities = class {
401
404
  }
402
405
  const texts = [];
403
406
  for (const item of list) {
404
- if (item && item.manifestation && item.manifestation[0] && item.manifestation[0].text) {
405
- texts.push(item.manifestation[0].text);
407
+ if (item && item.manifestation && item.manifestation[0]) {
408
+ if (item.manifestation[0].text) {
409
+ texts.push(item.manifestation[0].text);
410
+ } else if (item.manifestation[0].coding && item.manifestation[0].coding[0]?.display) {
411
+ texts.push(item.manifestation[0].coding[0].display);
412
+ }
406
413
  }
407
414
  }
408
415
  return texts.join(", ");
@@ -561,6 +568,25 @@ var TemplateUtilities = class {
561
568
  }
562
569
  return null;
563
570
  }
571
+ extractPregnancyStatus(observation) {
572
+ let status = "";
573
+ observation.code?.coding?.forEach((c) => {
574
+ if (c.code && Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_STATUS).includes(c.code)) {
575
+ status = PREGNANCY_LOINC_CODES.PREGNANCY_STATUS[c.code];
576
+ }
577
+ });
578
+ if (observation.valueCodeableConcept) {
579
+ observation.valueCodeableConcept.coding?.forEach((c) => {
580
+ if (c.code && Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_OUTCOME).includes(c.code)) {
581
+ if (status) {
582
+ status += " - ";
583
+ }
584
+ status += PREGNANCY_LOINC_CODES.PREGNANCY_OUTCOME[c.code];
585
+ }
586
+ });
587
+ }
588
+ return status;
589
+ }
564
590
  formatQuantityValue(quantity) {
565
591
  if (!quantity) return "";
566
592
  const parts = [];
@@ -799,7 +825,6 @@ var PatientTemplate = class _PatientTemplate {
799
825
  const patient = entry.resource;
800
826
  html += `
801
827
  <div>
802
- <h2>Patient Summary</h2>
803
828
  <ul>
804
829
  <li><strong>Name(s):</strong>${this.renderNames(patient)}</li>
805
830
  <li><strong>Gender:</strong>${patient.gender ? this.capitalize(patient.gender) : ""}</li>
@@ -949,10 +974,20 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
949
974
  }
950
975
  }
951
976
  }
977
+ activeAllergies.sort((a, b) => {
978
+ const dateA = a.onsetDateTime;
979
+ const dateB = b.onsetDateTime;
980
+ return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
981
+ });
982
+ resolvedAllergies.sort((a, b) => {
983
+ const dateA = a.onsetDateTime;
984
+ const dateB = b.onsetDateTime;
985
+ return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
986
+ });
952
987
  let html = "";
953
988
  html += `
954
989
  <div class="ActiveAllergies">
955
- <h3>Active Allergies and Intolerances</h3>
990
+ <h3>Active</h3>
956
991
  <table class="ActiveAllergyTable">
957
992
  <thead>
958
993
  <tr>
@@ -980,7 +1015,7 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
980
1015
  </div>`;
981
1016
  html += `
982
1017
  <div class="ResolvedAllergies">
983
- <h3>Resolved Allergies and Intolerances</h3>
1018
+ <h3>Resolved</h3>
984
1019
  <table class="ResolvedAllergyTable">
985
1020
  <thead>
986
1021
  <tr>
@@ -1024,7 +1059,7 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
1024
1059
  <tr id="${templateUtilities.narrativeLinkId(allergy.extension)}">
1025
1060
  <td class="Name"><span class="AllergenName">${templateUtilities.codeableConcept(allergy.code)}</span></td>
1026
1061
  <td class="Status">${templateUtilities.codeableConcept(allergy.clinicalStatus) || "-"}</td>
1027
- <td class="Category">${templateUtilities.safeConcat(allergy.category, "value") || "-"}</td>
1062
+ <td class="Category">${templateUtilities.safeConcat(allergy.category) || "-"}</td>
1028
1063
  <td class="Reaction">${templateUtilities.concatReactionManifestation(allergy.reaction) || "-"}</td>
1029
1064
  <td class="Severity">${templateUtilities.safeConcat(allergy.reaction, "severity") || "-"}</td>
1030
1065
  <td class="OnsetDate">${templateUtilities.renderTime(allergy.onsetDateTime, timezone) || "-"}</td>
@@ -1059,6 +1094,67 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
1059
1094
  generateNarrative(resource, timezone) {
1060
1095
  return _MedicationSummaryTemplate.generateStaticNarrative(resource, timezone);
1061
1096
  }
1097
+ /**
1098
+ * Safely parse a date string and return a valid Date object or null
1099
+ * @param dateString - The date string to parse
1100
+ * @returns Date object or null if invalid
1101
+ */
1102
+ static parseDate(dateString) {
1103
+ if (!dateString || dateString.trim() === "") {
1104
+ return null;
1105
+ }
1106
+ const date = new Date(dateString);
1107
+ return !isNaN(date.getTime()) ? date : null;
1108
+ }
1109
+ /**
1110
+ * Determine if a MedicationRequest is active
1111
+ * @param medicationRequest - The MedicationRequest resource
1112
+ * @returns boolean indicating if the medication request is active
1113
+ */
1114
+ static isActiveMedicationRequest(medicationRequest) {
1115
+ const status = medicationRequest.status?.toLowerCase();
1116
+ if (status === "active" || status === "unknown") {
1117
+ return true;
1118
+ }
1119
+ if (status === "completed" || status === "cancelled" || status === "stopped" || status === "draft") {
1120
+ return false;
1121
+ }
1122
+ const endDate = medicationRequest.dispenseRequest?.validityPeriod?.end;
1123
+ if (!endDate) {
1124
+ return true;
1125
+ }
1126
+ const parsedEndDate = this.parseDate(endDate);
1127
+ if (!parsedEndDate) {
1128
+ return true;
1129
+ }
1130
+ return parsedEndDate.getTime() > Date.now();
1131
+ }
1132
+ /**
1133
+ * Determine if a MedicationStatement is active
1134
+ * @param medicationStatement - The MedicationStatement resource
1135
+ * @returns boolean indicating if the medication statement is active
1136
+ */
1137
+ static isActiveMedicationStatement(medicationStatement) {
1138
+ const status = medicationStatement.status?.toLowerCase();
1139
+ if (status === "active" || status === "intended" || status === "unknown") {
1140
+ return true;
1141
+ }
1142
+ if (status === "completed" || status === "stopped" || status === "not-taken") {
1143
+ return false;
1144
+ }
1145
+ let endDate;
1146
+ if (medicationStatement.effectivePeriod?.end) {
1147
+ endDate = medicationStatement.effectivePeriod.end;
1148
+ }
1149
+ if (!endDate) {
1150
+ return true;
1151
+ }
1152
+ const parsedEndDate = this.parseDate(endDate);
1153
+ if (!parsedEndDate) {
1154
+ return true;
1155
+ }
1156
+ return parsedEndDate.getTime() > Date.now();
1157
+ }
1062
1158
  /**
1063
1159
  * Internal static implementation that actually generates the narrative
1064
1160
  * @param resource - FHIR Bundle containing Medication resources
@@ -1070,12 +1166,56 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
1070
1166
  const templateUtilities = new TemplateUtilities(resource);
1071
1167
  let html = "";
1072
1168
  const medicationRequests = this.getMedicationRequests(templateUtilities, resource);
1073
- if (medicationRequests.length > 0) {
1074
- html += this.renderMedicationRequests(templateUtilities, medicationRequests);
1075
- }
1076
1169
  const medicationStatements = this.getMedicationStatements(templateUtilities, resource);
1077
- if (medicationStatements.length > 0) {
1078
- html += this.renderMedicationStatements(templateUtilities, medicationStatements);
1170
+ const allActiveMedications = [];
1171
+ const allInactiveMedications = [];
1172
+ medicationRequests.forEach((mr) => {
1173
+ if (this.isActiveMedicationRequest(mr.resource)) {
1174
+ allActiveMedications.push({ type: "request", resource: mr.resource, extension: mr.extension });
1175
+ } else {
1176
+ allInactiveMedications.push({ type: "request", resource: mr.resource, extension: mr.extension });
1177
+ }
1178
+ });
1179
+ medicationStatements.forEach((ms) => {
1180
+ if (this.isActiveMedicationStatement(ms.resource)) {
1181
+ allActiveMedications.push({ type: "statement", resource: ms.resource, extension: ms.extension });
1182
+ } else {
1183
+ allInactiveMedications.push({ type: "statement", resource: ms.resource, extension: ms.extension });
1184
+ }
1185
+ });
1186
+ const sortMedications = (medications) => {
1187
+ medications.sort((a, b) => {
1188
+ let dateStringA;
1189
+ let dateStringB;
1190
+ if (a.type === "request") {
1191
+ const mr = a.resource;
1192
+ dateStringA = mr.dispenseRequest?.validityPeriod?.start || mr.authoredOn;
1193
+ } else {
1194
+ const ms = a.resource;
1195
+ dateStringA = ms.effectiveDateTime || ms.effectivePeriod?.start;
1196
+ }
1197
+ if (b.type === "request") {
1198
+ const mr = b.resource;
1199
+ dateStringB = mr.dispenseRequest?.validityPeriod?.start || mr.authoredOn;
1200
+ } else {
1201
+ const ms = b.resource;
1202
+ dateStringB = ms.effectiveDateTime || ms.effectivePeriod?.start;
1203
+ }
1204
+ const dateA = this.parseDate(dateStringA);
1205
+ const dateB = this.parseDate(dateStringB);
1206
+ if (!dateA && !dateB) return 0;
1207
+ if (!dateA) return 1;
1208
+ if (!dateB) return -1;
1209
+ return dateB.getTime() - dateA.getTime();
1210
+ });
1211
+ };
1212
+ if (allActiveMedications.length > 0) {
1213
+ sortMedications(allActiveMedications);
1214
+ html += this.renderCombinedMedications(templateUtilities, allActiveMedications, "Active Medications");
1215
+ }
1216
+ if (allInactiveMedications.length > 0) {
1217
+ sortMedications(allInactiveMedications);
1218
+ html += this.renderCombinedMedications(templateUtilities, allInactiveMedications, "Inactive Medications");
1079
1219
  }
1080
1220
  return html;
1081
1221
  }
@@ -1110,16 +1250,19 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
1110
1250
  }));
1111
1251
  }
1112
1252
  /**
1113
- * Render HTML table for MedicationRequest resources
1253
+ * Render HTML table for combined MedicationRequest and MedicationStatement resources
1114
1254
  * @param templateUtilities - Instance of TemplateUtilities for utility functions
1115
- * @param medications - Array of MedicationRequest resources
1255
+ * @param medications - Array of combined medication resources
1256
+ * @param sectionTitle - Title for the section
1116
1257
  * @returns HTML string for rendering
1117
1258
  */
1118
- static renderMedicationRequests(templateUtilities, medications) {
1259
+ static renderCombinedMedications(templateUtilities, medications, sectionTitle) {
1119
1260
  let html = `
1261
+ <h3>${sectionTitle}</h3>
1120
1262
  <table>
1121
1263
  <thead>
1122
1264
  <tr>
1265
+ <th>Type</th>
1123
1266
  <th>Medication</th>
1124
1267
  <th>Sig</th>
1125
1268
  <th>Dispense Quantity</th>
@@ -1130,86 +1273,56 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
1130
1273
  </tr>
1131
1274
  </thead>
1132
1275
  <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") || "-";
1276
+ for (const medication of medications) {
1277
+ const narrativeLinkId = templateUtilities.narrativeLinkId(medication.extension);
1278
+ let type;
1279
+ let medicationName;
1280
+ let sig;
1140
1281
  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() || "-";
1282
+ let refills = "-";
1148
1283
  let startDate = "-";
1149
1284
  let endDate = "-";
1150
- if (mr.dispenseRequest?.validityPeriod) {
1151
- startDate = mr.dispenseRequest.validityPeriod.start || "-";
1152
- endDate = mr.dispenseRequest.validityPeriod.end || "-";
1285
+ let status;
1286
+ if (medication.type === "request") {
1287
+ const mr = medication.resource;
1288
+ type = "Request";
1289
+ status = mr.status ? String(mr.status) : "-";
1290
+ medicationName = templateUtilities.getMedicationName(
1291
+ mr.medicationReference || mr.medicationCodeableConcept
1292
+ );
1293
+ sig = templateUtilities.concat(mr.dosageInstruction, "text") || "-";
1294
+ if (mr.dispenseRequest?.quantity) {
1295
+ const quantity = mr.dispenseRequest.quantity;
1296
+ if (quantity.value) {
1297
+ dispenseQuantity = `${quantity.value} ${quantity.unit || quantity.code || ""}`.trim();
1298
+ }
1299
+ }
1300
+ refills = mr.dispenseRequest?.numberOfRepeatsAllowed?.toString() || "-";
1301
+ if (mr.dispenseRequest?.validityPeriod) {
1302
+ startDate = mr.dispenseRequest.validityPeriod.start || "-";
1303
+ endDate = mr.dispenseRequest.validityPeriod.end || "-";
1304
+ } else {
1305
+ startDate = mr.authoredOn || "-";
1306
+ }
1153
1307
  } 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 || "-";
1308
+ const ms = medication.resource;
1309
+ type = "Statement";
1310
+ status = ms.status ? String(ms.status) : "-";
1311
+ medicationName = templateUtilities.getMedicationName(
1312
+ ms.medicationReference || ms.medicationCodeableConcept
1313
+ );
1314
+ sig = templateUtilities.concat(ms.dosage, "text") || "-";
1315
+ if (ms.effectiveDateTime) {
1316
+ startDate = ms.effectiveDateTime;
1317
+ } else if (ms.effectivePeriod) {
1318
+ startDate = ms.effectivePeriod.start || "-";
1319
+ endDate = ms.effectivePeriod.end || "-";
1320
+ }
1209
1321
  }
1210
1322
  html += `
1211
1323
  <tr${narrativeLinkId ? ` id="${narrativeLinkId}"` : ""}>
1212
- <td>${medication}<ul></ul></td>
1324
+ <td>${type}</td>
1325
+ <td>${medicationName}<ul></ul></td>
1213
1326
  <td>${sig}</td>
1214
1327
  <td>${dispenseQuantity}</td>
1215
1328
  <td>${refills}</td>
@@ -1234,6 +1347,13 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
1234
1347
  * @returns HTML string for rendering
1235
1348
  */
1236
1349
  generateNarrative(resource, timezone) {
1350
+ if (resource.entry && Array.isArray(resource.entry)) {
1351
+ resource.entry.sort((a, b) => {
1352
+ const dateA = a.resource?.occurrenceDateTime;
1353
+ const dateB = b.resource?.occurrenceDateTime;
1354
+ return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
1355
+ });
1356
+ }
1237
1357
  return _ImmunizationsTemplate.generateStaticNarrative(resource, timezone);
1238
1358
  }
1239
1359
  /**
@@ -1245,7 +1365,6 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
1245
1365
  static generateStaticNarrative(resource, timezone) {
1246
1366
  const templateUtilities = new TemplateUtilities(resource);
1247
1367
  let html = `
1248
- <h5>Immunizations</h5>
1249
1368
  <table>
1250
1369
  <thead>
1251
1370
  <tr>
@@ -1303,81 +1422,35 @@ var ProblemListTemplate = class _ProblemListTemplate {
1303
1422
  static generateStaticNarrative(resource, timezone) {
1304
1423
  const templateUtilities = new TemplateUtilities(resource);
1305
1424
  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">
1425
+ const activeConditions = resource.entry?.map((entry) => entry.resource) || [];
1426
+ activeConditions.sort((a, b) => {
1427
+ const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
1428
+ const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
1429
+ return dateB - dateA;
1430
+ });
1431
+ html += `
1432
+ <table>
1356
1433
  <thead>
1357
1434
  <tr>
1358
1435
  <th>Problem</th>
1359
- <th>Priority</th>
1360
- <th>Noted Date</th>
1361
- <th>Diagnosed Date</th>
1362
- <th>Resolved Date</th>
1436
+ <th>Severity</th>
1437
+ <th>Onset Date</th>
1438
+ <th>Recorded Date</th>
1363
1439
  <th>Notes</th>
1364
1440
  </tr>
1365
1441
  </thead>
1366
1442
  <tbody>`;
1367
- for (const cond of resolvedConditions) {
1368
- html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
1443
+ for (const cond of activeConditions) {
1444
+ html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
1369
1445
  <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>
1446
+ <td class="Severity">${templateUtilities.codeableConcept(cond.severity)}</td>
1447
+ <td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
1448
+ <td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
1449
+ <td class="Notes">${templateUtilities.renderNotes(cond.note, timezone)}</td>
1375
1450
  </tr>`;
1376
- }
1377
- html += `</tbody>
1378
- </table>
1379
- </div>`;
1380
1451
  }
1452
+ html += `</tbody>
1453
+ </table>`;
1381
1454
  return html;
1382
1455
  }
1383
1456
  };
@@ -1401,8 +1474,13 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
1401
1474
  */
1402
1475
  static generateStaticNarrative(resource, timezone) {
1403
1476
  const templateUtilities = new TemplateUtilities(resource);
1477
+ const observations = resource.entry?.map((entry) => entry.resource) || [];
1478
+ observations.sort((a, b) => {
1479
+ const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
1480
+ const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
1481
+ return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
1482
+ });
1404
1483
  let html = `
1405
- <h5>Vital Signs</h5>
1406
1484
  <table>
1407
1485
  <thead>
1408
1486
  <tr>
@@ -1416,13 +1494,8 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
1416
1494
  </tr>
1417
1495
  </thead>
1418
1496
  <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 += `
1497
+ for (const obs of observations) {
1498
+ html += `
1426
1499
  <tr id="${templateUtilities.narrativeLinkId(obs)}">
1427
1500
  <td>${templateUtilities.codeableConcept(obs.code, "display")}</td>
1428
1501
  <td>${templateUtilities.extractObservationValue(obs)}</td>
@@ -1430,9 +1503,8 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
1430
1503
  <td>${templateUtilities.firstFromCodeableConceptList(obs.interpretation)}</td>
1431
1504
  <td>${templateUtilities.renderComponent(obs.component)}</td>
1432
1505
  <td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
1433
- <td>${templateUtilities.renderEffective(obs.effectiveDateTime, timezone)}</td>
1506
+ <td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
1434
1507
  </tr>`;
1435
- }
1436
1508
  }
1437
1509
  html += `
1438
1510
  </tbody>
@@ -1450,6 +1522,13 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
1450
1522
  * @returns HTML string for rendering
1451
1523
  */
1452
1524
  generateNarrative(resource, timezone) {
1525
+ if (resource.entry && Array.isArray(resource.entry)) {
1526
+ resource.entry.sort((a, b) => {
1527
+ const dateA = a.resource?.recordedOn;
1528
+ const dateB = b.resource?.recordedOn;
1529
+ return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
1530
+ });
1531
+ }
1453
1532
  return _MedicalDevicesTemplate.generateStaticNarrative(resource, timezone);
1454
1533
  }
1455
1534
  /**
@@ -1461,7 +1540,6 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
1461
1540
  static generateStaticNarrative(resource, timezone) {
1462
1541
  const templateUtilities = new TemplateUtilities(resource);
1463
1542
  let html = `
1464
- <h5>Medical Devices</h5>
1465
1543
  <table>
1466
1544
  <thead>
1467
1545
  <tr>
@@ -1515,10 +1593,20 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
1515
1593
  let html = "";
1516
1594
  const observations = this.getObservations(resource);
1517
1595
  if (observations.length > 0) {
1596
+ observations.sort((a, b) => {
1597
+ const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
1598
+ const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
1599
+ return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
1600
+ });
1518
1601
  html += this.renderObservations(templateUtilities, observations, timezone);
1519
1602
  }
1520
1603
  const diagnosticReports = this.getDiagnosticReports(resource);
1521
1604
  if (diagnosticReports.length > 0) {
1605
+ diagnosticReports.sort((a, b) => {
1606
+ const dateA = a.issued;
1607
+ const dateB = b.issued;
1608
+ return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
1609
+ });
1522
1610
  html += this.renderDiagnosticReports(templateUtilities, diagnosticReports, timezone);
1523
1611
  }
1524
1612
  return html;
@@ -1554,7 +1642,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
1554
1642
  */
1555
1643
  static renderObservations(templateUtilities, observations, timezone) {
1556
1644
  let html = `
1557
- <h5>Diagnostic Results: Observations</h5>
1645
+ <h5>Observations</h5>
1558
1646
  <table>
1559
1647
  <thead>
1560
1648
  <tr>
@@ -1577,7 +1665,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
1577
1665
  <td>${templateUtilities.firstFromCodeableConceptList(obs.interpretation)}</td>
1578
1666
  <td>${templateUtilities.concatReferenceRange(obs.referenceRange)}</td>
1579
1667
  <td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
1580
- <td>${templateUtilities.renderTime(obs.effectiveDateTime, timezone)}</td>
1668
+ <td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
1581
1669
  </tr>`;
1582
1670
  }
1583
1671
  html += `
@@ -1594,7 +1682,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
1594
1682
  */
1595
1683
  static renderDiagnosticReports(templateUtilities, reports, timezone) {
1596
1684
  let html = `
1597
- <h5>Diagnostic Results: Reports</h5>
1685
+ <h5>Diagnostic Reports</h5>
1598
1686
  <table>
1599
1687
  <thead>
1600
1688
  <tr>
@@ -1636,6 +1724,11 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
1636
1724
  * @returns HTML string for rendering
1637
1725
  */
1638
1726
  generateNarrative(resource, timezone) {
1727
+ resource.entry?.sort((a, b) => {
1728
+ const dateA = a.resource.performedDateTime || a.resource.performedPeriod?.start;
1729
+ const dateB = b.resource.performedDateTime || b.resource.performedPeriod?.start;
1730
+ return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
1731
+ });
1639
1732
  return _HistoryOfProceduresTemplate.generateStaticNarrative(resource, timezone);
1640
1733
  }
1641
1734
  /**
@@ -1647,7 +1740,6 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
1647
1740
  static generateStaticNarrative(resource, timezone) {
1648
1741
  const templateUtilities = new TemplateUtilities(resource);
1649
1742
  let html = `
1650
- <h5>History Of Procedures</h5>
1651
1743
  <table>
1652
1744
  <thead>
1653
1745
  <tr>
@@ -1660,14 +1752,11 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
1660
1752
  if (resource.entry && Array.isArray(resource.entry)) {
1661
1753
  for (const entry of resource.entry) {
1662
1754
  const proc = entry.resource;
1663
- if (proc.resourceType === "Composition") {
1664
- continue;
1665
- }
1666
1755
  html += `
1667
1756
  <tr id="${templateUtilities.narrativeLinkId(proc)}">
1668
1757
  <td>${templateUtilities.codeableConcept(proc.code, "display")}</td>
1669
1758
  <td>${templateUtilities.renderNotes(proc.note, timezone)}</td>
1670
- <td>${templateUtilities.renderTime(proc.performedDateTime, timezone)}</td>
1759
+ <td>${proc.performedDateTime ? templateUtilities.renderTime(proc.performedDateTime, timezone) : proc.performedPeriod ? templateUtilities.renderPeriod(proc.performedPeriod, timezone) : ""}</td>
1671
1760
  </tr>`;
1672
1761
  }
1673
1762
  }
@@ -1697,8 +1786,13 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
1697
1786
  */
1698
1787
  static generateStaticNarrative(resource, timezone) {
1699
1788
  const templateUtilities = new TemplateUtilities(resource);
1789
+ const observations = resource.entry?.map((entry) => entry.resource) || [];
1790
+ observations.sort((a, b) => {
1791
+ const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
1792
+ const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
1793
+ return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
1794
+ });
1700
1795
  let html = `
1701
- <h5>Social History</h5>
1702
1796
  <table>
1703
1797
  <thead>
1704
1798
  <tr>
@@ -1710,21 +1804,15 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
1710
1804
  </tr>
1711
1805
  </thead>
1712
1806
  <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 += `
1807
+ for (const obs of observations) {
1808
+ html += `
1720
1809
  <tr id="${templateUtilities.narrativeLinkId(obs)}">
1721
- <td>${templateUtilities.codeableConcept(obs.code, "display")}</td>
1810
+ <td>${templateUtilities.codeableConcept(obs.code)}</td>
1722
1811
  <td>${templateUtilities.extractObservationValue(obs)}</td>
1723
1812
  <td>${templateUtilities.extractObservationValueUnit(obs)}</td>
1724
1813
  <td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
1725
- <td>${templateUtilities.renderTime(obs.effectiveDateTime, timezone)}</td>
1814
+ <td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
1726
1815
  </tr>`;
1727
- }
1728
1816
  }
1729
1817
  html += `
1730
1818
  </tbody>
@@ -1743,36 +1831,38 @@ var PastHistoryOfIllnessTemplate = class {
1743
1831
  */
1744
1832
  generateNarrative(resource, timezone) {
1745
1833
  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
- }
1834
+ let html = ``;
1835
+ const resolvedConditions = resource.entry?.map((entry) => entry.resource) || [];
1836
+ resolvedConditions.sort((a, b) => {
1837
+ const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
1838
+ const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
1839
+ return dateB - dateA;
1840
+ });
1773
1841
  html += `
1774
- </tbody>
1775
- </table>`;
1842
+ <table>
1843
+ <thead>
1844
+ <tr>
1845
+ <th>Problem</th>
1846
+ <th>Severity</th>
1847
+ <th>Onset Date</th>
1848
+ <th>Recorded Date</th>
1849
+ <th>Resolved Date</th>
1850
+ <th>Notes</th>
1851
+ </tr>
1852
+ </thead>
1853
+ <tbody>`;
1854
+ for (const cond of resolvedConditions) {
1855
+ html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
1856
+ <td class="Name">${templateUtilities.codeableConcept(cond.code)}</td>
1857
+ <td class="Severity">${templateUtilities.codeableConcept(cond.severity)}</td>
1858
+ <td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
1859
+ <td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
1860
+ <td class="ResolvedDate">${templateUtilities.renderDate(cond.abatementDateTime)}</td>
1861
+ <td class="Notes">${templateUtilities.renderNotes(cond.note, timezone)}</td>
1862
+ </tr>`;
1863
+ }
1864
+ html += `</tbody>
1865
+ </table>`;
1776
1866
  return html;
1777
1867
  }
1778
1868
  };
@@ -1785,15 +1875,19 @@ var PlanOfCareTemplate = class {
1785
1875
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1786
1876
  * @returns HTML string for rendering
1787
1877
  */
1788
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1789
1878
  generateNarrative(resource, timezone) {
1790
1879
  const templateUtilities = new TemplateUtilities(resource);
1880
+ const carePlans = resource.entry?.map((entry) => entry.resource) || [];
1881
+ carePlans.sort((a, b) => {
1882
+ const endA = a.period?.end ? new Date(a.period?.end).getTime() : 0;
1883
+ const endB = b.period?.end ? new Date(b.period?.end).getTime() : 0;
1884
+ return endB - endA;
1885
+ });
1791
1886
  let html = `
1792
- <h5>Plan of Care</h5>
1793
1887
  <table>
1794
1888
  <thead>
1795
1889
  <tr>
1796
- <th>Activity</th>
1890
+ <th>Description</th>
1797
1891
  <th>Intent</th>
1798
1892
  <th>Comments</th>
1799
1893
  <th>Planned Start</th>
@@ -1801,21 +1895,15 @@ var PlanOfCareTemplate = class {
1801
1895
  </tr>
1802
1896
  </thead>
1803
1897
  <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 += `
1898
+ for (const cp of carePlans) {
1899
+ html += `
1811
1900
  <tr id="${templateUtilities.narrativeLinkId(cp)}">
1812
- <td>${cp.description || ""}</td>
1813
- <td>${cp.intent || cp.intent || ""}</td>
1901
+ <td>${cp.description || cp.title || ""}</td>
1902
+ <td>${cp.intent || ""}</td>
1814
1903
  <td>${templateUtilities.concat(cp.note, "text")}</td>
1815
- <td>${cp.period?.start || ""}</td>
1816
- <td>${cp.period?.end || ""}</td>
1904
+ <td>${cp.period?.start ? templateUtilities.renderTime(cp.period?.start, timezone) : ""}</td>
1905
+ <td>${cp.period?.end ? templateUtilities.renderTime(cp.period?.end, timezone) : ""}</td>
1817
1906
  </tr>`;
1818
- }
1819
1907
  }
1820
1908
  html += `
1821
1909
  </tbody>
@@ -1843,38 +1931,112 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
1843
1931
  */
1844
1932
  static generateStaticNarrative(resource, timezone) {
1845
1933
  const templateUtilities = new TemplateUtilities(resource);
1846
- let html = `
1847
- <h5>Functional Status</h5>
1934
+ let html = ``;
1935
+ const activeConditions = [];
1936
+ const clinicalImpressions = [];
1937
+ if (resource.entry && Array.isArray(resource.entry)) {
1938
+ for (const entry of resource.entry) {
1939
+ if (entry.resource?.resourceType === "Condition") {
1940
+ const cond = entry.resource;
1941
+ const isResolved = cond.clinicalStatus?.coding?.some(
1942
+ (c) => c.code === "resolved" || c.code === "inactive" || c.display?.toLowerCase().includes("resolved")
1943
+ );
1944
+ if (!isResolved) {
1945
+ activeConditions.push(cond);
1946
+ }
1947
+ } else if (entry.resource?.resourceType === "ClinicalImpression") {
1948
+ clinicalImpressions.push(entry.resource);
1949
+ }
1950
+ }
1951
+ }
1952
+ activeConditions.sort((a, b) => {
1953
+ const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
1954
+ const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
1955
+ return dateB - dateA;
1956
+ });
1957
+ clinicalImpressions.sort((a, b) => {
1958
+ 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;
1959
+ 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;
1960
+ return dateB - dateA;
1961
+ });
1962
+ if (activeConditions.length > 0) {
1963
+ html += `<h3>Conditions</h3>
1964
+ <table>
1965
+ <thead>
1966
+ <tr>
1967
+ <th>Problem</th>
1968
+ <th>Severity</th>
1969
+ <th>Onset Date</th>
1970
+ <th>Recorded Date</th>
1971
+ <th>Notes</th>
1972
+ </tr>
1973
+ </thead>
1974
+ <tbody>`;
1975
+ for (const cond of activeConditions) {
1976
+ html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
1977
+ <td class="Name">${templateUtilities.codeableConcept(cond.code)}</td>
1978
+ <td class="Severity">${templateUtilities.codeableConcept(cond.severity)}</td>
1979
+ <td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
1980
+ <td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
1981
+ <td class="Notes">${templateUtilities.renderNotes(cond.note, timezone, { styled: true, warning: true })}</td>
1982
+ </tr>`;
1983
+ }
1984
+ html += `</tbody>
1985
+ </table>`;
1986
+ }
1987
+ if (clinicalImpressions.length > 0) {
1988
+ html += `<h3>Clinical Impressions</h3>
1848
1989
  <table>
1849
1990
  <thead>
1850
1991
  <tr>
1851
- <th>Assessment</th>
1852
- <th>Status</th>
1853
- <th>Finding</th>
1854
- <th>Comments</th>
1855
1992
  <th>Date</th>
1993
+ <th>Status</th>
1994
+ <th>Description</th>
1995
+ <th>Summary</th>
1996
+ <th>Findings</th>
1997
+ <th>Notes</th>
1856
1998
  </tr>
1857
1999
  </thead>
1858
2000
  <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;
2001
+ for (const impression of clinicalImpressions) {
2002
+ let formattedDate = "";
2003
+ if (impression.effectiveDateTime) {
2004
+ formattedDate = templateUtilities.renderTime(
2005
+ impression.effectiveDateTime,
2006
+ timezone
2007
+ );
2008
+ } else if (impression.effectivePeriod) {
2009
+ formattedDate = templateUtilities.renderPeriod(
2010
+ impression.effectivePeriod,
2011
+ timezone
2012
+ );
2013
+ } else if (impression.date) {
2014
+ formattedDate = templateUtilities.renderDate(impression.date);
1864
2015
  }
2016
+ let findingsHtml = "";
2017
+ if (impression.finding && impression.finding.length > 0) {
2018
+ findingsHtml = "<ul>";
2019
+ for (const finding of impression.finding) {
2020
+ const findingText = finding.itemCodeableConcept ? templateUtilities.codeableConcept(finding.itemCodeableConcept) : finding.itemReference ? templateUtilities.renderReference(finding.itemReference) : "";
2021
+ const cause = finding.basis || "";
2022
+ findingsHtml += `<li>${findingText}${cause ? ` - ${cause}` : ""}</li>`;
2023
+ }
2024
+ findingsHtml += "</ul>";
2025
+ }
2026
+ const notes = templateUtilities.renderNotes(impression.note, timezone);
1865
2027
  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>
2028
+ <tr id="${templateUtilities.narrativeLinkId(impression)}">
2029
+ <td>${formattedDate}</td>
2030
+ <td>${impression.status || ""}</td>
2031
+ <td>${impression.description || ""}</td>
2032
+ <td>${impression.summary || ""}</td>
2033
+ <td>${findingsHtml}</td>
2034
+ <td>${notes}</td>
1872
2035
  </tr>`;
1873
2036
  }
2037
+ html += `</tbody>
2038
+ </table>`;
1874
2039
  }
1875
- html += `
1876
- </tbody>
1877
- </table>`;
1878
2040
  return html;
1879
2041
  }
1880
2042
  };
@@ -1898,32 +2060,30 @@ var PregnancyTemplate = class _PregnancyTemplate {
1898
2060
  */
1899
2061
  static generateStaticNarrative(resource, timezone) {
1900
2062
  const templateUtilities = new TemplateUtilities(resource);
2063
+ const observations = resource.entry?.map((entry) => entry.resource) || [];
2064
+ observations.sort((a, b) => {
2065
+ const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
2066
+ const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
2067
+ return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
2068
+ });
1901
2069
  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 += `
2070
+ <table>
2071
+ <thead>
2072
+ <tr>
2073
+ <th>Result</th>
2074
+ <th>Comments</th>
2075
+ <th>Date</th>
2076
+ </tr>
2077
+ </thead>
2078
+ <tbody>`;
2079
+ for (const resource2 of observations) {
2080
+ const obs = resource2;
2081
+ html += `
1920
2082
  <tr id="${templateUtilities.narrativeLinkId(obs)}">
1921
- <td>${templateUtilities.codeableConcept(obs.code, "display")}</td>
1922
- <td>${templateUtilities.extractObservationValue(obs)}</td>
2083
+ <td>${templateUtilities.extractPregnancyStatus(obs)}</td>
1923
2084
  <td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
1924
- <td>${templateUtilities.renderEffective(obs.effectiveDateTime, timezone)}</td>
2085
+ <td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
1925
2086
  </tr>`;
1926
- }
1927
2087
  }
1928
2088
  html += `
1929
2089
  </tbody>
@@ -1941,6 +2101,13 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
1941
2101
  * @returns HTML string for rendering
1942
2102
  */
1943
2103
  generateNarrative(resource, timezone) {
2104
+ if (resource.entry && Array.isArray(resource.entry)) {
2105
+ resource.entry.sort((a, b) => {
2106
+ const dateA = new Date(a.resource.dateTime || 0);
2107
+ const dateB = new Date(b.resource.dateTime || 0);
2108
+ return dateB.getTime() - dateA.getTime();
2109
+ });
2110
+ }
1944
2111
  return _AdvanceDirectivesTemplate.generateStaticNarrative(resource, timezone);
1945
2112
  }
1946
2113
  /**
@@ -1953,7 +2120,6 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
1953
2120
  static generateStaticNarrative(resource, timezone) {
1954
2121
  const templateUtilities = new TemplateUtilities(resource);
1955
2122
  let html = `
1956
- <h5>Advance Directives</h5>
1957
2123
  <table>
1958
2124
  <thead>
1959
2125
  <tr>
@@ -1967,9 +2133,6 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
1967
2133
  if (resource.entry && Array.isArray(resource.entry)) {
1968
2134
  for (const entry of resource.entry) {
1969
2135
  const consent = entry.resource;
1970
- if (consent.resourceType === "Composition") {
1971
- continue;
1972
- }
1973
2136
  html += `
1974
2137
  <tr id="${templateUtilities.narrativeLinkId(consent)}">
1975
2138
  <td>${templateUtilities.codeableConcept(consent.scope, "display")}</td>
@@ -1986,171 +2149,6 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
1986
2149
  }
1987
2150
  };
1988
2151
 
1989
- // src/narratives/templates/typescript/FamilyHistoryTemplate.ts
1990
- var FamilyHistoryTemplate = class {
1991
- /**
1992
- * Generate HTML narrative for Family History
1993
- * @param resource - FHIR Bundle containing FamilyMemberHistory resources
1994
- * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1995
- * @returns HTML string for rendering
1996
- */
1997
- generateNarrative(resource, timezone) {
1998
- const templateUtilities = new TemplateUtilities(resource);
1999
- let html = `
2000
- <h5>Family History</h5>
2001
- <table>
2002
- <thead>
2003
- <tr>
2004
- <th>Relationship</th>
2005
- <th>Condition</th>
2006
- <th>Status</th>
2007
- <th>Onset</th>
2008
- <th>Notes</th>
2009
- </tr>
2010
- </thead>
2011
- <tbody>`;
2012
- if (resource.entry && Array.isArray(resource.entry)) {
2013
- let hasFamilyHistory = false;
2014
- for (const entry of resource.entry) {
2015
- const familyHistory = entry.resource;
2016
- if (!familyHistory || familyHistory.resourceType !== "FamilyMemberHistory") {
2017
- continue;
2018
- }
2019
- hasFamilyHistory = true;
2020
- const fmh = familyHistory;
2021
- const relationship = templateUtilities.codeableConcept(fmh.relationship, "display");
2022
- if (fmh.condition && Array.isArray(fmh.condition)) {
2023
- for (const condition of fmh.condition) {
2024
- const conditionCode = templateUtilities.codeableConcept(condition.code, "display");
2025
- const status = fmh.status || "";
2026
- let onset = "";
2027
- if (condition.onsetAge) {
2028
- onset = templateUtilities.renderOnset(condition.onsetAge, timezone);
2029
- }
2030
- const notes = condition.note ? templateUtilities.renderNotes(condition.note, timezone) : templateUtilities.renderNotes(fmh.note, timezone);
2031
- html += `
2032
- <tr id="${templateUtilities.narrativeLinkId(fmh)}">
2033
- <td>${relationship}</td>
2034
- <td>${conditionCode}</td>
2035
- <td>${status}</td>
2036
- <td>${onset}</td>
2037
- <td>${notes}</td>
2038
- </tr>`;
2039
- }
2040
- } else {
2041
- html += `
2042
- <tr id="${templateUtilities.narrativeLinkId(fmh)}">
2043
- <td>${relationship}</td>
2044
- <td>Not specified</td>
2045
- <td>${fmh.status || ""}</td>
2046
- <td></td>
2047
- <td>${templateUtilities.renderNotes(fmh.note, timezone)}</td>
2048
- </tr>`;
2049
- }
2050
- }
2051
- if (!hasFamilyHistory) {
2052
- html += `
2053
- <tr>
2054
- <td colspan="5">No family history recorded</td>
2055
- </tr>`;
2056
- }
2057
- } else {
2058
- html += `
2059
- <tr>
2060
- <td colspan="5">No family history recorded</td>
2061
- </tr>`;
2062
- }
2063
- html += `
2064
- </tbody>
2065
- </table>`;
2066
- return html;
2067
- }
2068
- };
2069
-
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
2152
  // src/narratives/templates/typescript/TypeScriptTemplateMapper.ts
2155
2153
  var TypeScriptTemplateMapper = class {
2156
2154
  /**
@@ -2173,20 +2171,18 @@ var TypeScriptTemplateMapper = class {
2173
2171
  TypeScriptTemplateMapper.sectionToTemplate = {
2174
2172
  ["Patient" /* PATIENT */]: new PatientTemplate(),
2175
2173
  ["AllergyIntoleranceSection" /* ALLERGIES */]: new AllergyIntoleranceTemplate(),
2176
- ["MedicationSection" /* MEDICATIONS */]: new MedicationSummaryTemplate(),
2174
+ ["MedicationSummarySection" /* MEDICATIONS */]: new MedicationSummaryTemplate(),
2177
2175
  ["ImmunizationSection" /* IMMUNIZATIONS */]: new ImmunizationsTemplate(),
2178
2176
  ["ProblemSection" /* PROBLEMS */]: new ProblemListTemplate(),
2179
2177
  ["VitalSignsSection" /* VITAL_SIGNS */]: new VitalSignsTemplate(),
2180
2178
  ["MedicalDeviceSection" /* MEDICAL_DEVICES */]: new MedicalDevicesTemplate(),
2181
- ["DiagnosticReportSection" /* DIAGNOSTIC_REPORTS */]: new DiagnosticResultsTemplate(),
2182
- ["ProcedureSection" /* PROCEDURES */]: new HistoryOfProceduresTemplate(),
2183
- ["FamilyHistorySection" /* FAMILY_HISTORY */]: new FamilyHistoryTemplate(),
2179
+ ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: new DiagnosticResultsTemplate(),
2180
+ ["HistoryOfProceduresSection" /* PROCEDURES */]: new HistoryOfProceduresTemplate(),
2184
2181
  ["SocialHistorySection" /* SOCIAL_HISTORY */]: new SocialHistoryTemplate(),
2185
- ["PregnancyHistorySection" /* PREGNANCY_HISTORY */]: new PregnancyTemplate(),
2182
+ ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: new PregnancyTemplate(),
2186
2183
  ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: new FunctionalStatusTemplate(),
2187
- ["MedicalHistorySection" /* MEDICAL_HISTORY */]: new PastHistoryOfIllnessTemplate(),
2188
- ["CarePlanSection" /* CARE_PLAN */]: new PlanOfCareTemplate(),
2189
- ["ClinicalImpressionSection" /* CLINICAL_IMPRESSION */]: new ClinicalImpressionTemplate(),
2184
+ ["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: new PastHistoryOfIllnessTemplate(),
2185
+ ["PlanOfCareSection" /* CARE_PLAN */]: new PlanOfCareTemplate(),
2190
2186
  ["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: new AdvanceDirectivesTemplate()
2191
2187
  };
2192
2188
 
@@ -2223,10 +2219,9 @@ var NarrativeGenerator = class {
2223
2219
  * @param section - IPS section type
2224
2220
  * @param resources - Array of domain resources
2225
2221
  * @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
2222
  * @returns Generated HTML content or undefined if no resources
2228
2223
  */
2229
- static async generateNarrativeContentAsync(section, resources, timezone, wrapInXhtml = true) {
2224
+ static async generateNarrativeContentAsync(section, resources, timezone) {
2230
2225
  if (!resources || resources.length === 0) {
2231
2226
  return void 0;
2232
2227
  }
@@ -2242,11 +2237,7 @@ var NarrativeGenerator = class {
2242
2237
  if (!content) {
2243
2238
  return void 0;
2244
2239
  }
2245
- if (wrapInXhtml) {
2246
- return await this.wrapInXhtmlAsync(content);
2247
- } else {
2248
- return await this.minifyHtmlAsync(content);
2249
- }
2240
+ return content;
2250
2241
  } catch (error) {
2251
2242
  console.error(`Error generating narrative for section ${section}:`, error);
2252
2243
  return `<div class="error">Error generating narrative: ${error instanceof Error ? error.message : String(error)}</div>`;
@@ -2293,35 +2284,35 @@ var NarrativeGenerator = class {
2293
2284
  * @param resources - Array of domain resources
2294
2285
  * @param timezone - Optional timezone to use for date formatting
2295
2286
  * @param minify - Whether to minify the HTML content (default: true)
2296
- * @param wrapInXhtml - Whether to wrap the content in XHTML div
2297
2287
  * @returns Promise that resolves to a FHIR Narrative object or undefined if no resources
2298
2288
  */
2299
- static async generateNarrativeAsync(section, resources, timezone, minify = true, wrapInXhtml) {
2300
- const content = await this.generateNarrativeContentAsync(section, resources, timezone, wrapInXhtml);
2289
+ static async generateNarrativeAsync(section, resources, timezone, minify = true) {
2290
+ const content = await this.generateNarrativeContentAsync(section, resources, timezone);
2301
2291
  if (!content) {
2302
2292
  return void 0;
2303
2293
  }
2304
2294
  return await this.createNarrativeAsync(content, minify);
2305
2295
  }
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
- }
2296
+ };
2297
+
2298
+ // src/structures/ips_mandatory_sections.ts
2299
+ var IPSMandatorySections = ((IPSMandatorySections2) => {
2300
+ IPSMandatorySections2[IPSMandatorySections2["PATIENT"] = "Patient" /* PATIENT */] = "PATIENT";
2301
+ IPSMandatorySections2[IPSMandatorySections2["PROBLEMS"] = "ProblemSection" /* PROBLEMS */] = "PROBLEMS";
2302
+ IPSMandatorySections2[IPSMandatorySections2["ALLERGIES"] = "AllergyIntoleranceSection" /* ALLERGIES */] = "ALLERGIES";
2303
+ IPSMandatorySections2[IPSMandatorySections2["MEDICATIONS"] = "MedicationSummarySection" /* MEDICATIONS */] = "MEDICATIONS";
2304
+ return IPSMandatorySections2;
2305
+ })(IPSMandatorySections || {});
2306
+ var IPSMissingMandatorySectionContent = {
2307
+ ["ProblemSection" /* PROBLEMS */]: "There is no information available about the subject's health problems or disabilities.",
2308
+ ["AllergyIntoleranceSection" /* ALLERGIES */]: "There is no information available regarding the subject's allergy conditions.",
2309
+ ["MedicationSummarySection" /* MEDICATIONS */]: "There is no information available about the subject's medication use or administration."
2318
2310
  };
2319
2311
 
2320
2312
  // src/generators/fhir_summary_generator.ts
2321
2313
  var ComprehensiveIPSCompositionBuilder = class {
2322
2314
  constructor() {
2323
2315
  this.sections = [];
2324
- this.mandatorySectionsAdded = /* @__PURE__ */ new Set();
2325
2316
  this.resources = /* @__PURE__ */ new Set();
2326
2317
  }
2327
2318
  /**
@@ -2339,35 +2330,35 @@ var ComprehensiveIPSCompositionBuilder = class {
2339
2330
  /**
2340
2331
  * Adds a section to the composition with async HTML minification
2341
2332
  * @param sectionType - IPS section type
2342
- * @param resources - Array of domain resources
2333
+ * @param validResources - Array of domain resources
2343
2334
  * @param timezone - Optional timezone to use for date formatting
2344
- * @param options - Optional configuration options
2345
2335
  */
2346
- async addSectionAsync(sectionType, resources, timezone, options) {
2347
- const validResources = resources;
2336
+ async addSectionAsync(sectionType, validResources, timezone) {
2348
2337
  for (const resource of validResources) {
2349
2338
  this.resources.add(resource);
2350
2339
  }
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
2340
  if (sectionType !== "Patient" /* PATIENT */) {
2358
- const narrative = await NarrativeGenerator.generateNarrativeAsync(
2359
- sectionType,
2360
- validResources,
2361
- timezone,
2362
- true,
2363
- false
2364
- );
2341
+ let narrative = void 0;
2342
+ if (validResources.length > 0) {
2343
+ narrative = await NarrativeGenerator.generateNarrativeAsync(
2344
+ sectionType,
2345
+ validResources,
2346
+ timezone,
2347
+ true
2348
+ );
2349
+ } else if (sectionType in IPSMandatorySections) {
2350
+ narrative = await NarrativeGenerator.createNarrativeAsync(
2351
+ IPSMissingMandatorySectionContent[sectionType]
2352
+ );
2353
+ } else {
2354
+ return this;
2355
+ }
2365
2356
  const sectionEntry = {
2366
2357
  title: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType,
2367
2358
  code: {
2368
2359
  coding: [{
2369
2360
  system: "http://loinc.org",
2370
- code: options?.customLoincCode || IPS_SECTION_LOINC_CODES[sectionType],
2361
+ code: IPS_SECTION_LOINC_CODES[sectionType],
2371
2362
  display: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType
2372
2363
  }],
2373
2364
  text: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType
@@ -2378,9 +2369,6 @@ var ComprehensiveIPSCompositionBuilder = class {
2378
2369
  display: resource.resourceType
2379
2370
  }))
2380
2371
  };
2381
- if (!options?.isOptional) {
2382
- this.mandatorySectionsAdded.add(sectionType);
2383
- }
2384
2372
  this.sections.push(sectionEntry);
2385
2373
  }
2386
2374
  return this;
@@ -2399,43 +2387,20 @@ var ComprehensiveIPSCompositionBuilder = class {
2399
2387
  throw new Error("Patient resource not found in the bundle");
2400
2388
  }
2401
2389
  this.patient = patientEntry.resource;
2390
+ const resources = bundle.entry.map((e) => e.resource);
2402
2391
  for (const sectionType of Object.values(IPSSections)) {
2403
2392
  const resourceTypesForSection = IPSSectionResourceHelper.getResourceTypesForSection(sectionType);
2404
2393
  const customFilter = IPSSectionResourceHelper.getResourceFilterForSection(sectionType);
2405
- let resources = bundle.entry.map((e) => e.resource).filter((r) => typeof r?.resourceType === "string" && resourceTypesForSection.includes(r.resourceType));
2394
+ let sectionResources = resources.filter(
2395
+ (r) => r && typeof r.resourceType === "string" && resourceTypesForSection.includes(r.resourceType)
2396
+ );
2406
2397
  if (customFilter) {
2407
- resources = resources.filter(customFilter);
2408
- }
2409
- if (resources.length > 0) {
2410
- await this.addSectionAsync(sectionType, resources, timezone, {
2411
- isOptional: true
2412
- });
2398
+ sectionResources = sectionResources.filter((resource) => resource && customFilter(resource));
2413
2399
  }
2400
+ await this.addSectionAsync(sectionType, sectionResources, timezone);
2414
2401
  }
2415
2402
  return this;
2416
2403
  }
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
2404
  /**
2440
2405
  * Builds a complete FHIR Bundle containing the Composition and all resources.
2441
2406
  * @param authorOrganizationId - ID of the authoring organization (e.g., hospital or clinic)
@@ -2472,7 +2437,12 @@ var ComprehensiveIPSCompositionBuilder = class {
2472
2437
  date: (/* @__PURE__ */ new Date()).toISOString(),
2473
2438
  title: "International Patient Summary",
2474
2439
  section: this.sections,
2475
- text: await this.createCompositionNarrativeAsync(timezone)
2440
+ text: await NarrativeGenerator.generateNarrativeAsync(
2441
+ "Patient" /* PATIENT */,
2442
+ [this.patient],
2443
+ timezone,
2444
+ true
2445
+ )
2476
2446
  };
2477
2447
  const bundle = {
2478
2448
  resourceType: "Bundle",
@@ -2513,41 +2483,11 @@ var ComprehensiveIPSCompositionBuilder = class {
2513
2483
  return bundle;
2514
2484
  }
2515
2485
  /**
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
2486
+ * Returns the Composition sections without creating a full bundle.
2487
+ * @returns Array of TCompositionSection
2519
2488
  */
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
- };
2489
+ getSections() {
2490
+ return this.sections;
2551
2491
  }
2552
2492
  };
2553
2493