Manual mode: Use takenAt timestamp instead of dose date-only comparison
to correctly distinguish doses taken before vs after stock correction
on the same day. Add polling race condition guard (mutationInFlightRef)
so Take/Undo immediately reflects in dashboard stock.
Automatic mode: Grid-align effectiveStart to the medication schedule
and use hybrid consumed calculation (time-based + early-taken doses)
for accurate stock counting.
- Tag builds now also set 'latest' Docker tag (fixes race condition where
main-push build could overwrite latest with older version)
- Remove duplicate release.yml (create-release job in docker-build.yml
already handles GitHub releases)
- Remove redundant version-bump.yml (release.sh already bumps versions
in the release PR)
- Change update-test-badges.yml trigger to workflow_run after successful
docker-build (prevents parallel execution and ensures correct ordering)
- Update agent instructions and CI documentation to reflect changes
- Fix bottle type: submitStockCorrection used blister formula for baseTotal
but getMedTotal uses only looseTablets for bottles. Now uses getPackageSize()
which handles both types correctly.
- Fix manual mode: same-day taken doses were counted as consumed after a stock
correction (>= comparison with date-only timestamps). Changed to > so doses
on the correction day are excluded.
- Add agent instruction: only release-manager may create PRs/push/merge.
Tests are already guaranteed by branch protection (test.yml must pass
before PR can be merged to main). Running them again in docker-build.yml
was redundant and slowed down image builds.
This reduces test runs from 3x to 2x per code change:
- test.yml on PR (required by branch protection)
- update-test-badges.yml on main push (needed for badge counts)
Docker image builds now start immediately after merge.
Revert Dockerfile to use /tmp redirect for envsubst output, so the image
works regardless of docker-compose.yml tmpfs configuration. Removes the
uid=101,gid=101 requirement from compose that was a breaking change.
Remove Dockerfile /tmp workaround hacks (NGINX_ENVSUBST_OUTPUT_DIR and sed).
Use tmpfs with uid=101,gid=101 in docker-compose.yml instead, so the
nginx user can write to /etc/nginx/conf.d directly under read_only: true.
- Replace separate Frontend/Backend versions with single app version
- Version is now a clickable link to the GitHub release page
- Replace stopwatch SVG with actual app logo (favicon.svg)
- Fix update check UX: previous result stays visible during re-check
- Add 1s minimum delay for update check spinner visibility
- Reserve space for update result to prevent modal jumping
- Remove unused i18n keys (frontend/backend)
- Update release-manager docs with version link info
Redirect NGINX_ENVSUBST_OUTPUT_DIR to /tmp and update nginx.conf include
path so envsubst works with read_only: true in docker-compose.
Add tmpfs mount for /etc/nginx/conf.d for additional write layer.
* fix: badge workflow commits directly instead of creating PRs
Replace peter-evans/create-pull-request with direct git push.
Removes need for pull-requests:write permission and the repo setting
'Allow GitHub Actions to create pull requests'.
Uses [skip ci] in commit message to avoid triggering itself.
* chore: trigger CI
Document that both backend/package.json and frontend/package.json
must be updated before tagging a release, since the About modal
reads versions from these files.
Fixes#96
- nginx.conf converted to template processed by envsubst at container start
- BACKEND_URL env var (default: backend:3000) replaces hardcoded container name
- Docker DNS resolver used for dynamic upstream resolution
- Dockerfile copies nginx.conf as template to /etc/nginx/templates/
This prevents frontend breakage when users customize container names
in their docker-compose.yml.
- getDataDir() now detects monorepo by checking for ../docker-compose.yml
- DATA_DIR env var removed from .env and .env.example (no longer needed for local dev)
- Docker compose files explicitly set DATA_DIR=/app/data for containers
- Updated tests for monorepo detection logic
Add DATA_DIR env var support to configure the data directory path.
All hardcoded resolve(cwd, 'data') paths now use a central getDataDir()
function from db-utils.ts that checks DATA_DIR first, falling back to
resolve(cwd, 'data').
This prevents local dev (cd backend && npm run dev) from creating a
separate backend/data/ directory instead of using the root data/ folder.
Changes:
- Add getDataDir() to db-utils.ts as single source of truth
- Update all 8 source files that reference the data directory
- Add dotenv fallback to ../.env for local dev from backend/
- Add DATA_DIR documentation to .env.example
- Add 7 new tests for getDataDir and getDbPaths with DATA_DIR
- 493 tests pass, TypeScript clean
Two bugs in the backend medications route:
1. Planner /medications/usage had the same +1 phantom consumption bug
that was fixed in the frontend (PR #109). After a stock correction,
effectiveStart was set to max(blisterStart, correctionCutoff) instead
of correctionCutoff + period, causing 1 dose to be immediately
counted as consumed.
2. PUT /medications/:id did not reset stockAdjustment when stock fields
(packCount, blistersPerPack, pillsPerBlister, looseTablets) changed.
If a user edited stock values to correct their inventory, the old
stockAdjustment offset was preserved, resulting in wrong totals.
Added 4 tests covering both scenarios.
Extract DB utility functions (buildDbUrl, getDbPaths, ensureDataDirectory,
runAlterMigrations, etc.) from client.ts into db-utils.ts.
client.ts contained top-level initialization code (ensureDataDirectory,
createClient) that ran on every import. database.test.ts imported utility
functions from client.ts, which triggered the initialization as a side
effect — creating backend/data/ with a .write-test file and
medassist-ng.db every time tests ran.
Now database.test.ts imports from db-utils.ts (side-effect-free), and
client.ts re-exports everything for backward compatibility.
test.yml: Use dorny/paths-filter to detect changed paths. Backend
tests only run when backend/**, biome.json, or the workflow itself
changes. Frontend build only runs when frontend/**, biome.json, or
the workflow changes. Jobs skipped via job-level 'if:' are treated
as passed by GitHub required checks.
codeql.yml: Only run on push/PR when JS/TS source files, package
files, or CodeQL config changes. Weekly schedule and manual dispatch
remain unfiltered.
Create dedicated GitHub Copilot agent for release management with
4 tasks: branch/PR workflow, version determination, release execution,
and release notes writing.
Move release-specific instructions (workflow, release notes format,
breaking changes) from copilot-instructions.md to the agent file
to keep concerns separated.
After correcting medication stock, the coverage calculation immediately
counted 1 dose as consumed (due to +1 in occurrences formula), which
neutralized small corrections like +1 pill.
Fix: start consumption counting from stockCorrectionCutoff + period
(the next scheduled dose) instead of from the correction time itself.
Added 3 frontend tests for stock correction scenarios and 6 backend
e2e tests for the PATCH /medications/:id/stock-adjustment endpoint.
The groupedSchedule useMemo used slice(0, 2000) to limit events. With daily
medications having start dates far in the past, thousands of past events would
fill all 2000 slots, pushing today and future events completely out of the
display. This caused the past schedule to only show weekly medications (fewer
events) while daily medications appeared missing.
Replace the fixed count limit with a time-based window: only past events
within the scheduleDays window (30/90/180 days) are included. All today and
future events are always included regardless.
Coverage calculations are not affected as they use schedule.events directly.
The takenBy field is a string[]. Empty arrays [] are truthy in JavaScript,
causing d.takenBy ? [...] patterns to generate dose IDs with trailing
hyphens (e.g., '5-0-173...-') instead of base IDs ('5-0-173...').
This mismatch between ID generation and computeMissedPastDoseIds (which
correctly uses .length > 0) caused doses to always appear as missed.
Changes:
- Add expandDoseIds() helper function using correct .length > 0 check
- Replace 8 buggy inline patterns in DashboardPage.tsx
- Refactor SchedulePage.tsx to use shared expandDoseIds()
- Add backend startup repair to strip trailing hyphens from existing IDs
- Add 12 new tests (6 frontend + 6 backend)
Add repairOrphanedDoseIds() function that runs during app startup
(after ALTER migrations) to fix dose tracking entries that became
invalid when medication schedules were changed before PR #103.
The function:
- Generates valid schedule dates for each medication's current intakes
- Detects dose_tracking entries whose dateOnlyMs doesn't match any
valid schedule date
- Remaps orphaned doses to the nearest valid schedule date within
half the intake interval
- Preserves person suffixes in dose IDs
- Is idempotent (safe to run on every startup)
This complements PR #103 which only migrates dose IDs on future edits.
The startup repair fixes existing broken data in production databases.
Includes 8 tests covering: valid doses unchanged, 1-day shift repair,
person suffix preservation, out-of-range detection, idempotency,
multi-medication handling, and legacy format fallback.
When a medication's start date or interval changes, the generated dose
IDs shift (dateOnlyMs values change). Previously, doses marked as taken
under the old schedule were orphaned — they no longer matched the new
schedule's dose IDs, causing them to appear as missed.
Now the PUT /medications/:id endpoint:
1. Parses old intakes from the existing medication row
2. Detects which intake indices had schedule changes
3. Maps old dateOnlyMs values to the nearest new dateOnlyMs
4. Updates dose_tracking entries with the migrated IDs
5. Preserves person suffixes (e.g. -Alice) during migration
Also fixes the start-date cleanup to use date-only comparison,
preventing doses on the start date from being incorrectly deleted
when the start time is after midnight.
Adds 4 integration tests covering weekly day shift, person suffix
preservation, time-only changes, and interval changes.
Allows manual triggering of the badge update workflow, useful when
the ANSI fix or other workflow-only changes need to take effect
without waiting for source code changes.
Vitest 4 outputs ANSI color codes in test results, which caused the
grep regex to fail when extracting test counts. The badge workflow
silently skipped the update, leaving stale counts in the README.
Add a sed pass to strip ANSI escape sequences before parsing.
- Add 'Include consumption from today until start date' checkbox to planner
- When checked, usage calculation starts from today instead of max(today, startDate)
- Persist checkbox state in localStorage per user
- Add i18n translations (EN + DE)
- Update planner tests to use dynamic future dates
- Remove broken isDoseFromPreviousSchedule that falsely dismissed all past doses
after any medication edit (compared dateOnlyMs < updatedAt incorrectly)
- Fix takenBy normalization in AppContext: event.takenBy (string|null) was passed
through as-is via || operator instead of being properly converted to string[]
- Fix DashboardPage: 5 locations treated dose.takenBy as single string instead of
iterating the array, causing per-person dose tracking to silently fail
- Extract isDoseDismissed and computeMissedPastDoseIds as pure testable functions
from AppContext.tsx into utils/schedule.ts
- Update SharedSchedule.tsx to use shared isDoseDismissed from utils
- Add 22 regression tests covering isDoseDismissed, computeMissedPastDoseIds,
and full dose-tracking-survives-medication-edit workflows
- Add 'fix bugs, don't test around them' rule to copilot instructions
* Initial plan
* Add Playwright E2E testing infrastructure
- Add @playwright/test dependency
- Create playwright.config.ts with best practices configuration
- Create e2e test structure with fixtures and auth setup
- Add E2E tests for auth, dashboard, medications, and settings pages
- Add npm scripts for running E2E tests
- Update .gitignore for Playwright artifacts
- Add E2E test job to CI workflow
- Update vite.config.ts to support BACKEND_URL env variable
- Update biome.json to include e2e files in linting
Co-authored-by: DanielVolz <3275994+DanielVolz@users.noreply.github.com>
* Remove waitForTimeout anti-pattern from E2E tests
Replace hard-coded timeouts with proper Playwright waiting strategies:
- Use waitForLoadState('networkidle') for page load
- Use element.waitFor() for dynamic elements
- Use expect assertions for state verification
Co-authored-by: DanielVolz <3275994+DanielVolz@users.noreply.github.com>
* Remove E2E tests from CI workflow
E2E tests will only be run locally as requested.
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>
Co-authored-by: Daniel Volz <mail@danielvolz.org>
- Add Array.isArray() checks before calling .map() on dose.takenBy
- Fixes TypeError: dose.takenBy.map is not a function
- Affects AppContext missedPastDoseIds calculation
- Affects SchedulePage dose ID generation (3 locations)
This hotfix prevents the app from crashing when dose.takenBy
is null, undefined, or any non-array value.
## Package Type Feature
- Add 'blister' and 'bottle' package types for medications
- Bottle type uses totalPills for capacity and looseTablets for current stock
- Blister type continues to use packCount/blistersPerPack/pillsPerBlister
- Add doseUnit field for flexible dosing (mg, ml, IU, etc.)
- Full UI support in medication form and detail modal
## Per-Intake TakenBy
- Move takenBy from medication level to individual intakes
- Each intake schedule can now be assigned to a different person
- Update scheduler-utils to handle per-intake takenBy
- Update SharedSchedule to filter by per-intake takenBy
- Backward compatible with existing medication data
## UI Improvements
- Add PasswordInput component with show/hide toggle
- Centralize stockThresholds in AppContext for consistent status display
- Fix SharedSchedule sync issues with per-intake takenBy
- Improve mobile editing experience
## Technical
- Add migrations 0004 and 0005 for schema changes
- Update all relevant tests (1064 tests passing)
- Maintain backward compatibility with ALTER migrations
- Add updatedAt field to share API response
- Add isDoseFromPreviousSchedule check in SharedSchedule
- Don't count doses scheduled before medication update as missed
- Syncs SharedSchedule behavior with main app's AppContext logic