Add past days toggle and update terminology for blisters

- Added translations for showing/hiding past days and past days count in German and English.
- Renamed "slices" to "blisters" in both translation files.
- Updated CSS styles to reflect the change from slices to blisters, including layout and hover effects.
- Introduced new styles for past days toggle button and past day blocks.
This commit is contained in:
Daniel Volz
2025-12-27 15:01:54 +01:00
parent d69c38e141
commit 9ccb5b1f0f
9 changed files with 727 additions and 277 deletions
@@ -8,7 +8,7 @@ import { getAllUserSettings, sendShoutrrrNotification, type UserSettings } from
import { getTranslations, t, getDateLocale, type Language } from "../i18n/translations.js";
import { getReminderState, updateReminderSentTime } from "./reminder-scheduler.js";
type Slice = { usage: number; every: number; start: string };
type Blister = { usage: number; every: number; start: string };
type IntakeReminderState = {
sentReminders: string[]; // Array of "medName:timestamp" to track sent reminders
@@ -42,17 +42,17 @@ function saveIntakeReminderState(state: IntakeReminderState): void {
writeFileSync(intakeReminderStateFile, JSON.stringify(state, null, 2));
}
function parseSlices(row: { usageJson: string; everyJson: string; startJson: string }): Slice[] {
function parseBlisters(row: { usageJson: string; everyJson: string; startJson: string }): Blister[] {
try {
const usage = JSON.parse(row.usageJson) as number[];
const every = JSON.parse(row.everyJson) as number[];
const start = JSON.parse(row.startJson) as string[];
const len = Math.min(usage.length, every.length, start.length);
const slices: Slice[] = [];
const blisters: Blister[] = [];
for (let i = 0; i < len; i++) {
slices.push({ usage: usage[i], every: every[i], start: start[i] });
blisters.push({ usage: usage[i], every: every[i], start: start[i] });
}
return slices;
return blisters;
} catch {
return [];
}
@@ -67,7 +67,7 @@ type UpcomingIntake = {
pillWeightMg: number | null;
};
function getUpcomingIntakes(medName: string, slices: Slice[], minutesBefore: number, takenBy: string | null, pillWeightMg: number | null, locale: string): UpcomingIntake[] {
function getUpcomingIntakes(medName: string, blisters: Blister[], minutesBefore: number, takenBy: string | null, pillWeightMg: number | null, locale: string): UpcomingIntake[] {
const now = Date.now();
// Window to detect if "now" is the right time to send reminder
// We check if the notify time (intake - 15min) falls within current minute ±1
@@ -76,9 +76,9 @@ function getUpcomingIntakes(medName: string, slices: Slice[], minutesBefore: num
const upcoming: UpcomingIntake[] = [];
for (const slice of slices) {
const startTime = new Date(slice.start).getTime();
const intervalMs = slice.every * 24 * 60 * 60 * 1000;
for (const blister of blisters) {
const startTime = new Date(blister.start).getTime();
const intervalMs = blister.every * 24 * 60 * 60 * 1000;
if (intervalMs <= 0) continue;
@@ -112,7 +112,7 @@ function getUpcomingIntakes(medName: string, slices: Slice[], minutesBefore: num
const intakeDate = new Date(nextTime);
upcoming.push({
medName,
usage: slice.usage,
usage: blister.usage,
intakeTime: intakeDate,
intakeTimeStr: intakeDate.toLocaleTimeString(locale, {
hour: "2-digit",
@@ -303,8 +303,8 @@ async function checkAndSendIntakeRemindersForUser(
// Find all upcoming intakes across all medications for this user
for (const med of medsWithReminders) {
const slices = parseSlices(med);
const upcoming = getUpcomingIntakes(med.name, slices, REMINDER_MINUTES_BEFORE, med.takenBy, med.pillWeightMg, locale);
const blisters = parseBlisters(med);
const upcoming = getUpcomingIntakes(med.name, blisters, REMINDER_MINUTES_BEFORE, med.takenBy, med.pillWeightMg, locale);
allUpcoming.push(...upcoming);
}
+11 -11
View File
@@ -7,7 +7,7 @@ import { resolve } from "path";
import { loadUserSettings, getAllUserSettings, sendShoutrrrNotification, type UserSettings } from "../routes/settings.js";
import { getTranslations, t, getDateLocale, type Language } from "../i18n/translations.js";
type Slice = { usage: number; every: number; start: string };
type Blister = { usage: number; every: number; start: string };
type ReminderState = {
lastAutoEmailSent: string | null; // ISO date string
@@ -172,28 +172,28 @@ export function updateReminderSentTime(type: "stock" | "intake" = "stock", chann
});
}
function parseSlices(row: { usageJson: string; everyJson: string; startJson: string }): Slice[] {
function parseBlisters(row: { usageJson: string; everyJson: string; startJson: string }): Blister[] {
try {
const usage = JSON.parse(row.usageJson) as number[];
const every = JSON.parse(row.everyJson) as number[];
const start = JSON.parse(row.startJson) as string[];
const len = Math.min(usage.length, every.length, start.length);
const slices: Slice[] = [];
const blisters: Blister[] = [];
for (let i = 0; i < len; i++) {
slices.push({ usage: usage[i], every: every[i], start: start[i] });
blisters.push({ usage: usage[i], every: every[i], start: start[i] });
}
return slices;
return blisters;
} catch {
return [];
}
}
function calculateDailyUsage(slices: Slice[]): number {
return slices.reduce((sum, s) => sum + s.usage / s.every, 0);
function calculateDailyUsage(blisters: Blister[]): number {
return blisters.reduce((sum, s) => sum + s.usage / s.every, 0);
}
function calculateDepletionInfo(med: { count: number; slices: Slice[] }, language: Language): { daysLeft: number | null; depletionDate: string | null } {
const dailyUsage = calculateDailyUsage(med.slices);
function calculateDepletionInfo(med: { count: number; blisters: Blister[] }, language: Language): { daysLeft: number | null; depletionDate: string | null } {
const dailyUsage = calculateDailyUsage(med.blisters);
if (dailyUsage <= 0) return { daysLeft: null, depletionDate: null };
const daysLeft = Math.floor(med.count / dailyUsage);
@@ -220,8 +220,8 @@ async function getMedicationsNeedingReminder(userId: number, reminderDaysBefore:
const lowStock: LowStockItem[] = [];
for (const row of rows) {
const slices = parseSlices(row);
const { daysLeft, depletionDate } = calculateDepletionInfo({ count: row.count, slices }, language);
const blisters = parseBlisters(row);
const { daysLeft, depletionDate } = calculateDepletionInfo({ count: row.count, blisters }, language);
// Check if medication runs out within reminderDaysBefore days
if (daysLeft !== null && daysLeft <= reminderDaysBefore) {