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
@@ -14,9 +14,16 @@ const defaultForm: FormState = {
looseTablets: "0",
totalPills: "",
pillWeightMg: "",
doseUnit: "mg",
medicationStartDate: "",
expiryDate: "",
notes: "",
intakeRemindersEnabled: false,
prescriptionEnabled: false,
prescriptionAuthorizedRefills: "",
prescriptionRemainingRefills: "",
prescriptionLowRefillThreshold: "1",
prescriptionExpiryDate: "",
blisters: [
{
usage: "1",
@@ -47,6 +54,8 @@ const defaultProps = {
formSaved: false,
formChanged: false,
hasValidationErrors: false,
dateConsistencyError: null,
readOnlyMode: false,
takenByInput: "",
onTakenByInputChange: vi.fn(),
existingPeople: [],
@@ -108,7 +117,7 @@ describe("MobileEditModal", () => {
it("renders close button", () => {
render(<MobileEditModal {...defaultProps} />);
const closeBtn = document.querySelector(".modal-close");
const closeBtn = document.querySelector(".btn-nav");
expect(closeBtn).toBeInTheDocument();
});
@@ -116,7 +125,7 @@ describe("MobileEditModal", () => {
const onClose = vi.fn();
render(<MobileEditModal {...defaultProps} onClose={onClose} />);
const closeBtn = document.querySelector(".modal-close");
const closeBtn = document.querySelector(".btn-nav");
if (closeBtn) {
fireEvent.click(closeBtn);
}
@@ -191,7 +200,7 @@ describe("MobileEditModal", () => {
render(<MobileEditModal {...defaultProps} hasValidationErrors={true} />);
const saveBtn = document.querySelector('button[type="submit"]') as HTMLButtonElement;
expect(saveBtn).toBeDisabled();
expect(saveBtn).toHaveClass("has-validation-error");
});
it("renders add intake button", () => {