165 lines
5.3 KiB
TypeScript
165 lines
5.3 KiB
TypeScript
import type { Coverage, Medication, PackageType } from "../types";
|
|
import { getMedTotal as getMedTotalFromTypes, isLiquidContainerPackageType, isTubePackageType } from "../types";
|
|
import { splitCurrentBlisterStock } from "../utils/stock";
|
|
|
|
export function userStorageKey(userId: number | undefined, key: string): string {
|
|
return userId ? `user_${userId}_${key}` : key;
|
|
}
|
|
|
|
export function getBlisterStock(
|
|
totalPills: number,
|
|
pillsPerBlister: number,
|
|
looseTablets: number,
|
|
_originalTotal: number
|
|
) {
|
|
return splitCurrentBlisterStock(totalPills, pillsPerBlister, looseTablets);
|
|
}
|
|
|
|
export function formatFullBlisters(count: number, t: (key: string) => string): string {
|
|
return `${count} ${count === 1 ? t("common.blister") : t("common.blisters")}`;
|
|
}
|
|
|
|
export function formatOpenBlisterAndLoose(
|
|
openBlisterPills: number,
|
|
loosePills: number,
|
|
pillsPerBlister: number,
|
|
t: (key: string) => string
|
|
): string {
|
|
if (openBlisterPills > 0 && loosePills > 0) {
|
|
return `${openBlisterPills} ${t("common.of")} ${pillsPerBlister} ${t("common.pills")} + ${loosePills} ${t("modal.loosePills")}`;
|
|
}
|
|
if (openBlisterPills > 0) {
|
|
return `${openBlisterPills} ${t("common.of")} ${pillsPerBlister} ${t("common.pills")}`;
|
|
}
|
|
if (loosePills > 0) {
|
|
return `${loosePills} ${t("modal.loosePills")}`;
|
|
}
|
|
return "-";
|
|
}
|
|
|
|
export function getMedTotal(med: {
|
|
packCount: number;
|
|
blistersPerPack: number;
|
|
pillsPerBlister: number;
|
|
looseTablets: number;
|
|
stockAdjustment?: number | null;
|
|
packageType?: PackageType;
|
|
}): number {
|
|
return getMedTotalFromTypes({
|
|
...med,
|
|
stockAdjustment: med.stockAdjustment ?? undefined,
|
|
});
|
|
}
|
|
|
|
export function getReminderStatusData(
|
|
reminderDaysBefore: number,
|
|
lowStockDays: number,
|
|
_allLowCoverage: Coverage[],
|
|
allCoverage: Coverage[],
|
|
meds: Medication[],
|
|
lastAutoEmailSent: string | null,
|
|
_lastNotificationType: string | null,
|
|
_lastNotificationChannel: string | null,
|
|
lastReminderMedName: string | null,
|
|
lastReminderTakenBy: string | null,
|
|
lastStockReminderSent: string | null,
|
|
_lastStockReminderChannel: string | null,
|
|
lastStockReminderMedNames: string | null,
|
|
t: (key: string, options?: Record<string, unknown>) => string,
|
|
locale: string
|
|
): {
|
|
status: { text: string; className: string };
|
|
lowStockMeds: { name: string; daysLeft: number; isCritical: boolean }[];
|
|
lastStockSent: { date: string; medNames: string | null } | null;
|
|
lastIntakeSent: { date: string; medName: string | null; takenBy: string | null } | null;
|
|
} {
|
|
const lowStockMap = new Map<string, { name: string; daysLeft: number; isCritical: boolean }>();
|
|
const medByName = new Map(meds.map((med) => [med.name || med.genericName || "", med] as const));
|
|
|
|
for (const c of allCoverage) {
|
|
const med = medByName.get(c.name);
|
|
if (isTubePackageType(med?.packageType)) continue;
|
|
|
|
if (c.medsLeft <= 0) {
|
|
lowStockMap.set(c.name, { name: c.name, daysLeft: 0, isCritical: true });
|
|
continue;
|
|
}
|
|
|
|
if (c.daysLeft === null) continue;
|
|
|
|
const roundedDaysLeft = Math.round(c.daysLeft);
|
|
const isLiquid = isLiquidContainerPackageType(med?.packageType);
|
|
const liquidLowDays = Math.max(1, Math.floor(reminderDaysBefore));
|
|
const liquidCriticalDays = Math.max(1, Math.ceil(liquidLowDays / 2));
|
|
const isCritical = isLiquid ? c.daysLeft <= liquidCriticalDays : c.daysLeft <= reminderDaysBefore;
|
|
const isLow = isLiquid ? c.daysLeft <= liquidLowDays : c.daysLeft < lowStockDays;
|
|
if (!isCritical && !isLow) continue;
|
|
|
|
const existing = lowStockMap.get(c.name);
|
|
if (!existing || roundedDaysLeft < existing.daysLeft || (isCritical && !existing.isCritical)) {
|
|
lowStockMap.set(c.name, { name: c.name, daysLeft: roundedDaysLeft, isCritical });
|
|
}
|
|
}
|
|
|
|
const lowStockMeds = Array.from(lowStockMap.values()).sort((a, b) => a.daysLeft - b.daysLeft);
|
|
const emptyCount = lowStockMeds.filter((m) => m.daysLeft <= 0).length;
|
|
const criticalCount = lowStockMeds.filter((m) => m.isCritical && m.daysLeft > 0).length;
|
|
const lowCount = lowStockMeds.filter((m) => !m.isCritical).length;
|
|
|
|
let status: { text: string; className: string };
|
|
if (emptyCount > 0) {
|
|
status = {
|
|
text: t("dashboard.reminders.emptyStock", { count: emptyCount }),
|
|
className: "danger",
|
|
};
|
|
} else if (criticalCount > 0) {
|
|
status = {
|
|
text: t("dashboard.reminders.criticalMeds", { count: criticalCount }),
|
|
className: "danger",
|
|
};
|
|
} else if (lowCount > 0) {
|
|
status = {
|
|
text: t("dashboard.reminders.lowMeds", { count: lowCount }),
|
|
className: "warning",
|
|
};
|
|
} else {
|
|
status = {
|
|
text: t("dashboard.reminders.allOk"),
|
|
className: "success",
|
|
};
|
|
}
|
|
|
|
let lastStockSent: { date: string; medNames: string | null } | null = null;
|
|
if (lastStockReminderSent) {
|
|
const sentDate = new Date(lastStockReminderSent);
|
|
const formattedDate = sentDate.toLocaleDateString(locale, {
|
|
day: "2-digit",
|
|
month: "short",
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
});
|
|
lastStockSent = {
|
|
date: formattedDate,
|
|
medNames: lastStockReminderMedNames,
|
|
};
|
|
}
|
|
|
|
let lastIntakeSent: { date: string; medName: string | null; takenBy: string | null } | null = null;
|
|
if (lastAutoEmailSent) {
|
|
const sentDate = new Date(lastAutoEmailSent);
|
|
const formattedDate = sentDate.toLocaleDateString(locale, {
|
|
day: "2-digit",
|
|
month: "short",
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
});
|
|
lastIntakeSent = {
|
|
date: formattedDate,
|
|
medName: lastReminderMedName,
|
|
takenBy: lastReminderTakenBy,
|
|
};
|
|
}
|
|
|
|
return { status, lowStockMeds, lastStockSent, lastIntakeSent };
|
|
}
|