* feat: add package amount persistence and backend route support
* test: align backend test schemas with medication metadata fields
* fix(backend): restore intake usage normalizer for planner endpoint
* fix(backend): keep export typing compatible before liquid-unit stack step
* feat: simplify tube stock editing in desktop and mobile forms
Remove ESC-keydown tests from ProfileModal.test.tsx since the
useEscapeKey hook was removed from individual modals. Escape key
handling is now centralized in App.tsx's global handler, making
per-component ESC tests invalid (the component no longer responds
to ESC in isolation).
AboutModal, ProfileModal, and ShareDialog each had their own
useEscapeKey hook AND were handled by the global ESC handler in
App.tsx. When ESC was pressed, both fired synchronously, calling
history.back() twice — navigating past the current page instead
of just closing the modal.
Removed the per-modal useEscapeKey calls since the global handler
in App.tsx already manages ESC priority for all modals.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Initial plan
* feat: allow generic name only for medications (frontend changes)
- Add getMedDisplayName() helper for consistent name display
- Update validation to require either commercial or generic name
- Update all display locations to use display name fallback
- Add i18n keys for nameOrGenericRequired in en.json and de.json
- Remove required attribute from commercial name field
- Update FIELD_LIMITS.name.min from 1 to 0
Co-authored-by: DanielVolz <3275994+DanielVolz@users.noreply.github.com>
* feat: allow generic name only for medications (backend changes)
- Update Zod schema to allow empty name with cross-field refinement
- Update reminder scheduler to use name || genericName for display
- Update planner routes to match medications by display name
- Update existing tests to match new validation behavior
Co-authored-by: DanielVolz <3275994+DanielVolz@users.noreply.github.com>
* fix: update placeholder text and fix FIELD_LIMITS test
- Remove "(optional)" from generic name placeholder in en/de
- Update types.test.ts to expect FIELD_LIMITS.name.min = 0
Co-authored-by: DanielVolz <3275994+DanielVolz@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: DanielVolz <3275994+DanielVolz@users.noreply.github.com>
- Replace 2 MB favicon.svg (base64-PNG-in-SVG) with optimized 43 KB app-logo.png (256x256)
- Update AppHeader and AboutModal references to use new logo
- Remove SVG favicon link from index.html (PNG/ICO favicons remain)
- Fix deprecated apple-mobile-web-app-capable → mobile-web-app-capable meta tag
- Add clipboard copy fallback for non-secure contexts (LAN IP over HTTP)
Closes#303
- Add sharp for server-side image processing (WebP conversion + thumbnails)
- New shared backend utility for image upload, optimization, and cleanup
- Return structured error codes from upload endpoints (IMAGE_TOO_LARGE, INVALID_TYPE, etc.)
- Frontend error code mapping with i18n support (EN + DE)
- MedicationAvatar tries thumbnail first, falls back to full image
- Error display in MedicationsPage, MobileEditModal, and Auth avatar upload
Closes#302
Replace CSS-only modal-open class toggle with a shared useScrollLock
hook that uses position:fixed + scroll position save/restore. This
reliably prevents background scrolling on all browsers including
iOS Safari.
The hook supports nesting (lock counter) so stacked modals (e.g.
MedDetail → RefillModal) work correctly.
Also adds missing modal states to the scroll lock: showRefillModal,
showEditStockModal, showImageLightbox, scheduleLightboxImage.
Replaces the inline 40-line scroll lock in MobileEditModal with the
shared hook.
Guard against tooltip pseudo-elements being clipped by ancestor
overflow:hidden or hidden behind modal overlays. Covers edit,
stock correction, export, and close button tooltips.
Reorder stepper DOM elements (input first) and apply refill-number-stepper
class to both steppers for consistent CSS order-based layout.
Fix missing bottom padding on .med-detail-body.
Replace plain numeric inputs with a reusable +/− stepper component in
both desktop (MedicationsPage) and mobile (MobileEditModal) edit forms.
Applied to Stock, Schedule, and Prescription tab fields. Reorder tabs
so Schedule appears before Prescription. Add responsive grid overrides
for narrow sidebar and compact schedule rows.
Fix label-hover ghost activation by placing <input> first in DOM
(CSS order restores visual [−] [value] [+] layout).
Closes#273
* feat: make medication names clickable in Dashboard dose schedule
Add click handlers to med-name-stack divs in all three dose schedule
sections (past, current/overdue, future) on DashboardPage, opening the
MedDetail modal on click.
Add early-return guards to all four modal openers in AppContext
(openMedDetail, openImageLightbox, openScheduleLightbox, openUserFilter)
to prevent duplicate pushState entries on double-click, which caused
unexpected navigation to the Medications page.
Closes#266
* fix: stabilize dashboard modal and image click handling
* fix: close medication detail on first backdrop click
* fix: auto-mark intakes at due time and show robot marker
* test: add taken_source to integration schema
* test: align e2e route schema with taken_source
Widen detail modal on desktop (711px, up from 500px) with max-width
override to beat modals-base.css specificity. Limit fullscreen mode
to actual phones (<=500px) instead of all screens <=900px. Move intake
schedule section before prescription details. Show per-intake takenBy
person and bell icon with proper warning color. Right-align time in
schedule rows. Move notes icon after label text. Replace emoji bell
icons with Lucide Bell component in SchedulePage and MobileEditModal.
Add common.on/common.off i18n keys.
Closes#254
* feat: close modals with browser back button on mobile
Create reusable useModalHistory hook that pushes history state when a
modal opens and listens for popstate to close it. Apply to ReportModal,
ClearMissedConfirm, ExportModal, ImportConfirm, and all modals using
ConfirmModal/ShareDialog/Auth/ExportModal base components. Escape key
handling was already in place for desktop.
Closes#253
* fix: update tests for renamed button labels and missing useModalHistory mock
* feat: improve mobile edit modal swipe gestures and tab navigation
Replace React passive touch handlers with native non-passive
addEventListener via useEffect for reliable horizontal swipe blocking.
Reduce axis-lock threshold from 18-26px to 6px for more responsive
gesture detection. Remove isInteractive() guard so swipe works on
input fields. Add tab strip auto-scroll via scrollIntoView when
active tab changes. Fix vertical scrolling by changing readonly
fieldset from display:block to display:flex.
Closes#252
* fix: guard scrollIntoView for jsdom test compatibility
* fix: notification channel toggles snap back after being enabled
The checked props for email/push notification toggles had redundant
conditions (smtpHost/shoutrrrUrl checks) that forced them to false,
causing immediate visual snap-back. Additionally, performSave()
overwrote emailEnabled/shoutrrrEnabled in local state with effective
values, disabling toggles when no SMTP host or Shoutrrr URL was set.
Remove redundant checked prop conditions (disabled attr already handles
interaction gating) and stop overwriting enabled flags in local state
after save.
Closes#250
* fix: remove leaked useModalHistory import from SettingsPage
* fix: update useSettings tests to match new toggle behavior