feat: obsolete medication archiving, start date, and UI improvements (#215)

* feat: obsolete medication archiving, start date, and UI improvements

- Add soft-archive (obsolete) for medications with dedicated section and toggle
- Add medication start date field with date picker and validation
- Add obsolete/reactivate API endpoints with proper auth
- Filter obsolete meds from schedule, coverage, planner, and notifications
- Improve UserFilterModal with intake schedules, stock badges, and click-to-open
- Improve dashboard taken-by badges with per-intake bell icons
- Add Escape key support to ConfirmModal and MobileEditModal
- Fix Lightbox close button positioning near image
- Add read-only mode support for MobileEditModal
- DB migrations: 0008 (is_obsolete, obsolete_at), 0009 (medication_start_date)
- All user-facing text uses i18n keys (en + de)

* test: fix tests for obsolete medications and UI changes

- Backend: add is_obsolete, obsolete_at, medication_start_date columns to test schemas
- Backend: add test medication inserts in planner tests for active-med filtering
- Frontend: update useMedications URL to include includeObsolete param
- Frontend: fix MobileEditModal selectors and validation assertions
- Frontend: add onClearUser prop to UserFilterModal test renders
- Frontend: fix MedicationsPage and DashboardPage test assertions
This commit is contained in:
Daniel Volz
2026-02-15 23:23:38 +01:00
committed by GitHub
parent c47a35d642
commit 4b697374f6
38 changed files with 2042 additions and 907 deletions
+11 -3
View File
@@ -1,6 +1,6 @@
import { existsSync, readFileSync, writeFileSync } from "node:fs";
import { resolve } from "node:path";
import { eq } from "drizzle-orm";
import { and, eq } from "drizzle-orm";
import nodemailer from "nodemailer";
import { db } from "../db/client.js";
import { getDataDir } from "../db/db-utils.js";
@@ -144,7 +144,11 @@ async function getMedicationsNeedingReminder(
lowStockDays: number,
language: Language
): Promise<LowStockItem[]> {
const rows = await db.select().from(medications).where(eq(medications.userId, userId)).orderBy(medications.id);
const rows = await db
.select()
.from(medications)
.where(and(eq(medications.userId, userId), eq(medications.isObsolete, false)))
.orderBy(medications.id);
const lowStock: LowStockItem[] = [];
@@ -176,7 +180,11 @@ async function getMedicationsNeedingReminder(
}
async function getMedicationsNeedingPrescriptionReminder(userId: number): Promise<PrescriptionReminderItem[]> {
const rows = await db.select().from(medications).where(eq(medications.userId, userId)).orderBy(medications.id);
const rows = await db
.select()
.from(medications)
.where(and(eq(medications.userId, userId), eq(medications.isObsolete, false)))
.orderBy(medications.id);
return rows
.filter(