Skip to content

Commit aad4b79

Browse files
authored
Merge branch 'ohcnetwork:develop' into develop
2 parents 8697cee + d6525ff commit aad4b79

31 files changed

Lines changed: 3421 additions & 4851 deletions

.example.env

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ REACT_ENABLE_AUTO_INVOICE_AFTER_DISPENSE=false
136136
# Default state for tax inclusive pricing in inventory, When true, base price is calculated from MRP by removing tax
137137
REACT_INVENTORY_DEFAULT_TAX_INCLUSIVE=false
138138

139+
# Set to "true" to open the schedule window automatically after patient registration
140+
REACT_OPEN_SCHEDULE_AFTER_PATIENT_REGISTRATION=true
141+
139142
# Number of months offset for expiry restriction
140143
# 0 = restrict products expiring by end of current month
141144
# 1 = restrict products expiring by end of next month

care.config.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,14 @@ const careConfig = {
274274
: null,
275275
},
276276

277+
/**
278+
* Open schedule window automatically after patient registration if set to "true"
279+
*/
280+
openScheduleAfterPatientRegistration: booleanFromString(
281+
env.REACT_OPEN_SCHEDULE_AFTER_PATIENT_REGISTRATION,
282+
false,
283+
),
284+
277285
/**
278286
* Decimal calculation configuration
279287
*/

public/locale/en.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,7 @@
909909
"bed_created_notification_one": "{{count}} Bed created successfully",
910910
"bed_created_notification_other": "{{count}} Beds created successfully",
911911
"bed_currently_reserved": "Bed is currently reserved for another patient",
912+
"bed_encounter_details": "Bed Encounter Details",
912913
"bed_history": "Bed History",
913914
"bed_hold": "Bed Hold",
914915
"bed_layout": "Bed Layout",
@@ -3592,6 +3593,8 @@
35923593
"no_product_definition_added": "No product definition added",
35933594
"no_product_knowledge_found": "No product knowledges found!",
35943595
"no_product_knowledge_found_for": "No product knowledge found for {{query}}",
3596+
"no_product_knowledge_linked": "No product knowledge linked, please select a product knowledge",
3597+
"no_product_linked": "No product linked",
35953598
"no_product_selected": "No product selected",
35963599
"no_products_added": "No products added",
35973600
"no_products_found": "No products found",
@@ -4837,6 +4840,7 @@
48374840
"schedule_weekdays_min_error": "Select at least one weekday",
48384841
"scheduled": "Scheduled",
48394842
"scheduled_for": "Schedule for:",
4843+
"scheduling": "Scheduling",
48404844
"scribe__reviewing_field": "Reviewing field {{currentField}} / {{totalFields}}",
48414845
"scribe_error": "Could not autofill fields",
48424846
"scroll_to_question": "Scroll to question",
@@ -5146,6 +5150,7 @@
51465150
"select_prn_reason": "Select reason for PRN",
51475151
"select_product": "Select Product",
51485152
"select_product_at_row": "Please select a product at row {{row}}",
5153+
"select_product_first": "Select a product first",
51495154
"select_product_knowledge": "Select product knowledge",
51505155
"select_product_type": "Select the product type",
51515156
"select_purchase_order": "Select Purchase Order",

scripts/validate-env.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ const envSchema = z
125125
booleanAsStringSchema.optional(),
126126
REACT_INVENTORY_DEFAULT_TAX_INCLUSIVE: booleanAsStringSchema.optional(),
127127
REACT_INVENTORY_EXPIRY_MONTH_OFFSET: numberAsString.optional(),
128+
REACT_OPEN_SCHEDULE_AFTER_PATIENT_REGISTRATION:
129+
booleanAsStringSchema.optional(),
128130
REACT_OBSERVATION_PLOTS_CONFIG_URL: z.string().url().optional(),
129131
REACT_DEFAULT_COUNTRY: z.string().optional(),
130132
REACT_DEFAULT_COUNTRY_NAME: z.string().optional(),

src/PluginEngine.tsx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@ import {
1010
__federation_method_setRemote as setFederationRemote,
1111
__federation_method_unwrapDefault as unwrapModule,
1212
} from "__federation__";
13-
import React, { Suspense } from "react";
13+
import React, { Suspense, useEffect, useMemo } from "react";
1414

1515
import ErrorBoundary from "@/components/Common/ErrorBoundary";
1616
import Loading from "@/components/Common/Loading";
1717
import { PluginErrorBoundary } from "@/components/Common/PluginErrorBoundary";
18-
import { PlugConfig } from "@/types/plugConfig";
18+
import { PlugConfig, PlugConfigMeta } from "@/types/plugConfig";
1919
import plugConfigApi from "@/types/plugConfig/plugConfigApi";
2020
import query from "@/Utils/request/query";
21+
import { deepFreeze } from "@/Utils/utils";
2122
import { t } from "i18next";
2223
import { Loader2Icon } from "lucide-react";
2324
import { z } from "zod";
@@ -82,6 +83,22 @@ export default function PluginEngine({
8283
}),
8384
});
8485

86+
const pluginMeta = useMemo(() => {
87+
return pluginsQuery.reduce(
88+
(acc, plugin) => {
89+
if (!plugin.isLoading && plugin.meta) {
90+
acc[plugin.slug] = deepFreeze({ ...plugin.meta });
91+
}
92+
return acc;
93+
},
94+
{} as Record<string, PlugConfigMeta>,
95+
);
96+
}, [pluginsQuery]);
97+
98+
useEffect(() => {
99+
window.__CARE_PLUGIN_RUNTIME__ = deepFreeze({ meta: pluginMeta });
100+
}, [pluginMeta]);
101+
85102
return (
86103
<Suspense fallback={<Loading />}>
87104
<ErrorBoundary

src/Utils/utils.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,3 +430,12 @@ export function formatTruncatedList<T>(
430430

431431
return `${displayedItems.map(getDisplayValue).join(", ")} ... +${remainingCount} ${t("more") || moreText}`;
432432
}
433+
434+
export function deepFreeze<T>(obj: T): T {
435+
if (!obj || typeof obj !== "object") return obj;
436+
437+
Object.freeze(obj);
438+
Object.values(obj).forEach(deepFreeze);
439+
440+
return obj;
441+
}

src/components/Encounter/EncounterInfoCard.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,18 @@ export interface EncounterInfoCardProps {
3030
encounter: EncounterListRead | EncounterRead;
3131
facilityId: string;
3232
hideBorder?: boolean;
33+
disableHover?: boolean;
3334
}
3435

3536
export default function EncounterInfoCard(props: EncounterInfoCardProps) {
3637
const { t } = useTranslation();
3738

38-
const { encounter, facilityId, hideBorder = false } = props;
39+
const {
40+
encounter,
41+
facilityId,
42+
hideBorder = false,
43+
disableHover = false,
44+
} = props;
3945

4046
// Get encounter tags and handle overflow
4147
const encounterTags = encounter.tags || [];
@@ -47,8 +53,9 @@ export default function EncounterInfoCard(props: EncounterInfoCardProps) {
4753
data-status={encounter.status}
4854
key={props.encounter.id}
4955
className={cn(
50-
"hover:shadow-lg transition-shadow group md:flex md:flex-col h-full overflow-hidden",
56+
"md:flex md:flex-col h-full overflow-hidden",
5157
hideBorder && "border-none shadow-none",
58+
!disableHover && "hover:shadow-lg transition-shadow group",
5259
)}
5360
>
5461
<CardHeader className="bg-gray-100 px-4 pt-2 pb-1">

src/components/Medication/SubstitutionSheet.tsx

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,16 @@ import { ProductKnowledgeBase } from "@/types/inventory/productKnowledge/product
4646
interface SubstitutionSheetProps {
4747
open: boolean;
4848
onOpenChange: (open: boolean) => void;
49-
originalProductKnowledge: ProductKnowledgeBase | undefined;
49+
originalProductKnowledge?: ProductKnowledgeBase;
50+
/** Used when no original product knowledge exists (e.g., medication without linked product) */
51+
originalMedicationName?: string;
5052
currentSubstitution?: {
5153
substitutedProductKnowledge?: ProductKnowledgeBase;
5254
type?: SubstitutionType;
5355
reason?: SubstitutionReason;
5456
};
57+
/** Pre-selected substitute product (optional) */
58+
preSelectedProduct?: ProductKnowledgeBase;
5559
onSave: (
5660
substitutionDetails?: {
5761
substitutedProductKnowledge: ProductKnowledgeBase;
@@ -76,37 +80,41 @@ export function SubstitutionSheet({
7680
open,
7781
onOpenChange,
7882
originalProductKnowledge,
83+
originalMedicationName,
7984
currentSubstitution,
85+
preSelectedProduct,
8086
onSave,
8187
facilityId: _facilityId,
8288
}: SubstitutionSheetProps) {
8389
const { t } = useTranslation();
8490
const [selectedSubstitute, setSelectedSubstitute] = useState<
8591
ProductKnowledgeBase | undefined
86-
>(currentSubstitution?.substitutedProductKnowledge);
92+
>(currentSubstitution?.substitutedProductKnowledge || preSelectedProduct);
8793

8894
const form = useForm<SubstitutionFormValues>({
8995
resolver: zodResolver(substitutionSchema),
9096
defaultValues: {
9197
substitutedProductKnowledge:
92-
currentSubstitution?.substitutedProductKnowledge || undefined,
98+
currentSubstitution?.substitutedProductKnowledge ||
99+
preSelectedProduct ||
100+
undefined,
93101
type: currentSubstitution?.type || SubstitutionType.E,
94102
reason: currentSubstitution?.reason || SubstitutionReason.OS,
95103
},
96104
});
97105

98106
useEffect(() => {
99107
if (open) {
108+
const initialProduct =
109+
currentSubstitution?.substitutedProductKnowledge || preSelectedProduct;
100110
form.reset({
101-
substitutedProductKnowledge:
102-
currentSubstitution?.substitutedProductKnowledge || undefined,
111+
substitutedProductKnowledge: initialProduct || undefined,
103112
type: currentSubstitution?.type || SubstitutionType.E,
104113
reason: currentSubstitution?.reason || SubstitutionReason.OS,
105114
});
106-
setSelectedSubstitute(currentSubstitution?.substitutedProductKnowledge);
107-
// No need to set search term anymore
115+
setSelectedSubstitute(initialProduct);
108116
}
109-
}, [open, currentSubstitution, form]);
117+
}, [open, currentSubstitution, preSelectedProduct, form]);
110118

111119
useEffect(() => {
112120
form.setValue("substitutedProductKnowledge", selectedSubstitute, {
@@ -130,7 +138,8 @@ export function SubstitutionSheet({
130138
setSelectedSubstitute(product);
131139
};
132140

133-
if (!originalProductKnowledge) return null;
141+
const displayName =
142+
originalProductKnowledge?.name || originalMedicationName || "";
134143

135144
const handleClearSubstitution = () => {
136145
onSave(null); // Pass null to indicate clearing
@@ -148,9 +157,7 @@ export function SubstitutionSheet({
148157
<div className="space-y-2">
149158
<div className="flex items-center gap-2">
150159
<span>{t("substituting_for")}:</span>
151-
<Badge variant="secondary">
152-
{originalProductKnowledge.name}
153-
</Badge>
160+
<Badge variant="secondary">{displayName}</Badge>
154161
</div>
155162
<p className="text-sm text-muted-foreground">
156163
{t("select_alternative_medication_and_provide_details")}

src/components/Patient/DischargeConfirmationDialog.tsx

Lines changed: 0 additions & 85 deletions
This file was deleted.

0 commit comments

Comments
 (0)