Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions public/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -3478,6 +3478,7 @@
"medicine_administration": "Medicine Administration",
"medicine_administration_history": "Medicine Administration History",
"medicine_dispensed": "Medicine Dispensed",
"medicine_instructions": "Medicine Instructions",
"medicine_prescription": "Medicine Prescription",
"medicines": "Medicines",
"medicines_administered": "Medicine(s) administered",
Expand Down Expand Up @@ -4273,6 +4274,7 @@
"patient_identifiers": "Patient Identifiers",
"patient_information": "Patient Information",
"patient_instruction": "Patient Instruction",
"patient_ip_location": "Patient IP Location",
"patient_is_deceased": "Patient is deceased",
"patient_location": "Patient Location",
"patient_login": "Log in as Patient",
Expand Down Expand Up @@ -6658,6 +6660,7 @@
"workflow_progress": "Workflow Progress",
"working_status": "Working Status",
"x": "X",
"x_of_y": "{{current}} of {{total}}",
"year": "Year",
"year_of_birth": "Year of Birth",
"year_of_birth_format": "Year of birth must be in YYYY format",
Expand Down
12 changes: 11 additions & 1 deletion src/CAREUI/misc/PrintPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type Props = {
facility?: FacilityRead;
templateSlug: string;
hideFacilityHeader?: boolean;
footer?: ReactNode;
};

export default function PrintPreview(props: Props) {
Expand Down Expand Up @@ -103,6 +104,7 @@ export default function PrintPreview(props: Props) {
facility={props.facility}
templateSlug={props.templateSlug}
hideFacilityHeader={props.hideFacilityHeader}
footer={props.footer}
>
{props.children}
</FacilityPrintLayout>
Expand Down Expand Up @@ -312,14 +314,21 @@ function FacilityPrintLayout({
facility,
children,
hideFacilityHeader,
footer,
}: {
templateSlug?: string;
facility?: FacilityRead;
children: ReactNode;
hideFacilityHeader?: boolean;
footer?: ReactNode;
}) {
if (!facility) {
return <>{children}</>;
return (
<>
{children}
{footer && <div>{footer}</div>}
</>
);
}

const printTemplate = resolvePrintTemplate(facility, templateSlug);
Expand Down Expand Up @@ -383,6 +392,7 @@ function FacilityPrintLayout({
/>
Comment thread
abhimanyurajeesh marked this conversation as resolved.
</div>
)}
{footer && <div>{footer}</div>}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
</div>
);
}
107 changes: 82 additions & 25 deletions src/components/Common/PrintTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ interface GenericTableProps {
headers: HeaderRow[];
rows: TableRowType[] | undefined;
className?: string;
classNameCell?: string;
cellClassName?: string;
headerClassName?: string;
tableClassName?: string;
cellConfig?: Record<string, CellConfig>;
renderCell?: (
key: string,
Expand All @@ -42,13 +44,33 @@ export default function PrintTable({
headers,
rows,
className,
classNameCell,
cellClassName,
headerClassName,
tableClassName,
cellConfig,
renderCell,
rowClassName,
}: GenericTableProps) {
const { t } = useTranslation();

// Pre-compute which cells are covered by a rowspan from a previous row.
// Rows can encode `_span_${key}: "N"` to span N rows for that column.
const skipCells: Record<string, Set<number>> = {};
headers.forEach(({ key }) => {
skipCells[key] = new Set();
});
rows?.forEach((row, rowIndex) => {
headers.forEach(({ key }) => {
const spanVal = row[`_span_${key}`];
if (spanVal) {
const span = parseInt(spanVal);
for (let i = 1; i < span; i++) {
skipCells[key].add(rowIndex + i);
}
}
});
});
Comment thread
abhimanyurajeesh marked this conversation as resolved.

const getCellContent = (
key: string,
value: string | undefined,
Expand All @@ -66,7 +88,12 @@ export default function PrintTable({
};

return (
<div className="overflow-hidden rounded border border-gray-200">
<div
className={cn(
"overflow-hidden rounded border border-gray-200",
tableClassName,
)}
>
<Table className="w-full">
<TableHeader>
<TableRow className="bg-transparent hover:bg-transparent divide-x divide-gray-200 border-b-gray-200">
Expand All @@ -76,6 +103,7 @@ export default function PrintTable({
index == 0 && "first:rounded-l-md",
"h-auto py-1 pl-2 pr-2 text-black text-center ",
width && `w-${width}`,
headerClassName,
)}
key={key}
>
Expand All @@ -86,29 +114,58 @@ export default function PrintTable({
</TableHeader>
<TableBody>
{!!rows &&
rows.map((row, index) => (
<TableRow
key={index}
className={cn(
"bg-transparent hover:bg-transparent divide-x divide-gray-200",
className,
rowClassName?.(row, index),
)}
>
{headers.map(({ key }) => (
<TableCell
className={cn(
"wrap-break-words whitespace-normal text-center",
classNameCell,
cellConfig?.[key]?.className,
)}
key={key}
rows.map((row, index) => {
if (row["_fullspan"]) {
const skippedCount = headers.filter(({ key }) =>
skipCells[key]?.has(index),
).length;
return (
<TableRow
key={index}
className="bg-transparent hover:bg-transparent"
>
{getCellContent(key, row[key], index)}
</TableCell>
))}
</TableRow>
))}
<TableCell
colSpan={headers.length - skippedCount}
className={cn(
"wrap-break-word whitespace-normal",
cellClassName,
)}
>
{row["_fullspan"]}
</TableCell>
</TableRow>
);
}
return (
<TableRow
key={index}
className={cn(
"bg-transparent hover:bg-transparent divide-x divide-gray-200",
className,
rowClassName?.(row, index),
)}
>
{headers.map(({ key }) => {
if (skipCells[key]?.has(index)) return null;
const spanVal = row[`_span_${key}`];
const rowSpan = spanVal ? parseInt(spanVal) : undefined;
return (
<TableCell
rowSpan={rowSpan}
className={cn(
"wrap-break-word whitespace-normal text-center",
cellClassName,
cellConfig?.[key]?.className,
)}
key={key}
>
{getCellContent(key, row[key], index)}
</TableCell>
);
})}
</TableRow>
);
})}
</TableBody>
</Table>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Medicine/FormattedDosage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function FormattedDosage({
return (
<span
className={cn(
"rounded bg-yellow-100 px-1.5 py-0.5 font-semibold text-yellow-900",
"rounded bg-yellow-100 px-1.5 -mx-1.5 py-0.5 font-semibold text-yellow-900",
className,
)}
>
Expand Down
26 changes: 20 additions & 6 deletions src/components/Medicine/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,30 @@ import {
MedicationRequestDosageInstruction,
} from "@/types/emr/medicationRequest/medicationRequest";
import { round } from "@/Utils/decimal";
import Decimal from "decimal.js";
Comment thread
abhimanyurajeesh marked this conversation as resolved.

Comment thread
abhimanyurajeesh marked this conversation as resolved.
/**
* Round to accounting precision for dosage/medication display.
* If the fractional part is all zeros (e.g. "5.00"), the decimal portion is omitted.
*/
export function roundDosage(value: string | number | Decimal): string {
const fixed = round(value);
const [intPart, fracPart] = fixed.split(".");
if (fracPart && /^0+$/.test(fracPart)) {
return intPart;
}
return fixed;
}

// Helper function to format dosage in Rx style
export function formatDosage(instruction?: MedicationRequestDosageInstruction) {
if (!instruction?.dose_and_rate) return "";

const { dose_range, dose_quantity } = instruction.dose_and_rate;
if (dose_range) {
return `${round(dose_range.low.value)} ${dose_range.low.unit.display} -> ${round(dose_range.high.value)} ${dose_range.high.unit.display}`;
return `${roundDosage(dose_range.low.value)} ${dose_range.low.unit.display} -> ${roundDosage(dose_range.high.value)} ${dose_range.high.unit.display}`;
} else if (dose_quantity) {
return `${round(dose_quantity.value)} ${dose_quantity.unit.display}`;
return `${roundDosage(dose_quantity.value)} ${dose_quantity.unit.display}`;
}
return "";
}
Expand All @@ -34,7 +48,7 @@ export function isNonUnitDose(
const { dose_range, dose_quantity } = doseAndRate;
if (dose_range) return true;
if (dose_quantity?.value == null) return false;
return round(dose_quantity.value) !== round(1);
return roundDosage(dose_quantity.value) !== roundDosage(1);
}

// Helper function to format dosage instructions in Rx style
Expand Down Expand Up @@ -62,7 +76,7 @@ export function formatSig(instruction?: MedicationRequestDosageInstruction) {

export function formatDoseRange(range?: DoseRange): string {
if (!range?.high?.value) return "";
return `${round(range.low.value)} → ${round(range.high?.value)} ${range.high?.unit?.display}`;
return `${roundDosage(range.low.value)} → ${roundDosage(range.high?.value)} ${range.high?.unit?.display}`;
}

/**
Expand Down Expand Up @@ -140,7 +154,7 @@ export function formatTotalUnits(
const dose = prnInstruction.dose_and_rate?.dose_quantity?.value;
const doseUnit =
prnInstruction.dose_and_rate?.dose_quantity?.unit?.display || unitText;
return dose ? `${round(dose)} ${doseUnit} (PRN)` : "PRN";
return dose ? `${roundDosage(dose)} ${doseUnit} (PRN)` : "PRN";
}

// Sum total dose across all instructions
Expand Down Expand Up @@ -168,5 +182,5 @@ export function formatTotalUnits(

if (!hasAnyDose) return "";

return `${round(String(totalValue))} ${doseUnit}${hasTapered ? " (tapered)" : ""}`;
return `${roundDosage(String(totalValue))} ${doseUnit}${hasTapered ? " (tapered)" : ""}`;
}
Loading
Loading