Skip to content

chore(deps): bump react-router-dom from 7.17.0 to 7.18.1 in /frontend - #7

Closed
dependabot[bot] wants to merge 83 commits into
mainfrom
dependabot/npm_and_yarn/frontend/react-router-dom-7.18.1
Closed

chore(deps): bump react-router-dom from 7.17.0 to 7.18.1 in /frontend#7
dependabot[bot] wants to merge 83 commits into
mainfrom
dependabot/npm_and_yarn/frontend/react-router-dom-7.18.1

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Jul 3, 2026

Copy link
Copy Markdown
Contributor

Bumps react-router-dom from 7.17.0 to 7.18.1.

Changelog

Sourced from react-router-dom's changelog.

v7.18.1

Patch Changes

v7.18.0

Patch Changes

Commits

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

Trifunovich and others added 30 commits June 13, 2026 15:52
Bootstrapped from fullstack-template (ASP.NET Core + React/Vite + Postgres rails).
Backend 28 tests + frontend 6 tests green. Next: harden auth (TODO(SECURITY)),
then build the first nutrition-tracking feature.
…ploy ports

- README: present Fuel as the self-hosted AI calorie tracker (product +
  early-status), drop the template/rename framing
- notifications.md: status header now reflects the feature is implemented
- testing.md: drop 'template starter tests' phrasing
- deploy env examples: move off the template default ports (which collide
  with ThoseDaysApp) to Fuel's own block — app 9223/9224, Postgres 5435/5436,
  Seq 9233/9234 (staging/prod)
…ericize deploy docs

- docs/food-catalogue-and-logging.md: Phase 0 spec (catalogue, ingredients
  with cycle detection, food entries with snapshots, meal-sectioned day view)
- docs/profile-and-weight.md: Phase 1 spec (profile, Mifflin-St Jeor + BMI,
  frame-size chooser, weight register, meal-pause)
- docs/ai-estimation.md: Phase 2/3 spec (AI estimation from text & photo)
- docs/ai-providers.md: deploy-time, swappable provider abstraction (DeepSeek first)
- infrastructure.md / deploy-runbook.md: rewrite to as-built Fuel values
  (own ports, /opt/fuel, dedicated runner folder, runs-on self-hosted)
- README: link the feature specs
- add CLAUDE.md
- Add Food, FoodIngredient, FoodEntry models + MealType enum
- Add DailyCalorieGoal to User
- Food catalogue CRUD with cycle detection (BFS) + inline ingredient creation
- Food entry CRUD with snapshotted nutrition + date-range filtering
- Day view with meal sections, calorie progress bar, date navigation
- Entry form with food search, inline definition, computed nutrition
- Catalogue page with add/edit/delete, ingredient management
- 57 backend tests + 18 frontend tests, all passing
…te, copy

- Style .app-nav/.nav-link into real tabs (were unstyled/near-invisible, so
  Catalogue/Settings looked unreachable — the 'only one page' bug)
- Enforce password policy (8+, letter, number, special) in AuthService and
  surface specific register/reset errors; AuthContext now reads the API's
  {error} instead of a generic message; live ✓/○ checklist on the register form
- Rebrand palette to indigo #4F46E5 + lime #84CC16 (light+dark), replacing the
  shared template coral/orange and its hardcoded focus/hover shadows
- Copy: view-aware login subtitle, self-hosted privacy line, 'Default unit'
  instead of 'Default UoM', cleaner diary empty state
- Tests: AuthTests (policy + error messages), LoginPage password checklist
Saving on every keystroke set saving=true, which hit the input's
disabled={saving} and dropped focus/keystrokes after a couple of digits.
Keep a local draft, persist on blur/Enter, and don't disable while typing.
Add SettingsPage test locking the multi-digit + single-save behaviour.
Backend:
- Add profile fields to User (height, sex, constitution, yearOfBirth, activityLevel)
- Add WeightEntry model + controller with delta-per-row computation
- ProfileService: Mifflin–St Jeor BMR, TDEE, BMI, ideal weight range by frame
- ProfileController: profile CRUD, metabolism endpoint, meal-pause check
- Constitution helper via wrist ratio with named Small/Medium/Large chooser
- 49 new backend tests (ProfileService, ProfileController, WeightController)

Frontend:
- OnboardingPage: first-login profile + starting weight collection
- WeightPage: weight register with delta arrows + delete confirmation
- Settings expanded: profile, display (show macros), meal pause, metabolism
- App.tsx: onboarding gating (nullable height check), /weight route + nav
- EntryFormPage: meal-pause warning banner on intake time/meal change
- 3 new CSS files, 2 modified test files

Docs: update ai-estimation, ai-providers; add barcode-lookup spec

Tests: 106 backend + 21 frontend, all passing
- OnboardingPage signals completion via onComplete so AppContent lifts the
  needsOnboarding gate immediately — previously the gate's profile check only
  ran on [user], so new users were stuck on onboarding until a manual refresh
- GetMetabolism returns 400 when Sex is null instead of silently assuming Male
  (~166 kcal BMR difference)
- Tests: OnboardingPage onComplete, GetMetabolism sex-not-set guard
…nums

- OnboardingPage reads current prefs before the goal PUT so it no longer
  clobbers notifyReleases (the prefs PUT is a full overwrite)
- UpdateProfile returns 400 on an unparseable Sex/Constitution instead of
  silently no-opping with a 200
- Test: UpdateProfile invalid sex → BadRequest
- HomePage: always render meal sections with +Add buttons, even when empty
- OnboardingPage: add Skip for now button that persists skip to localStorage
- App.tsx: check localStorage skip flag so onboarding doesn't re-appear on refresh
- storage.ts: add getOnboardingCompleted/saveOnboardingCompleted helpers
- Home: replace full-width "+ Add" rows with a round + button to the
  right of each meal header (next to the cal total), per request.
- Entry form: default the intake timestamp to the *day being viewed*
  (queryDate) at the current time, instead of always "now" — so logging
  for another day lands on that day. Time stays editable to record when
  you actually ate. This was why entries seemed to vanish.
- Defaults: DailyCalorieGoal defaults to 2100, MealPauseHours to 0 for
  new users; onboarding pre-fills the goal with 2100.
- Meal pause: 0 or negative turns the feature off (backend already
  skipped the check; clarified the Settings help text).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The day view filtered entries by UTC calendar day, so meals logged near
midnight landed on the wrong day relative to the user's local time.

- Home: query entries by a [from, to) pair of UTC instants computed from
  the viewer's *local* day (browser handles offset + DST). The date passed
  to the entry form is now the local date too.
- Backend GetEntries: accept from/to UTC instants; normalize both these and
  the legacy `date` path to DateTimeKind.Utc. The old path passed a
  Kind=Unspecified DateTime, which Npgsql rejects against a timestamptz
  column — so day-view loading was effectively broken on real Postgres.
- Add GET entries/{id} and use it for the edit load, so editing works for
  any day's entry (the old load fetched only "today" and searched the list).

Verified against real Postgres: from/to range includes/excludes correctly,
legacy date path returns 200, single-entry GET returns 200/404.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Deferred until after the AI features (which add the first external
dependency, DeepSeek). Captures the rationale — EF InMemory accepted the
Kind=Unspecified/timestamptz value real Npgsql rejects — and the shape:
separate project, real Postgres via Testcontainers/compose, external HTTP
via a stub, first regression test = entries from/to timestamptz range.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Global/shared catalogue is intentional — a homelab household co-builds and
co-edits one food list. This step makes it navigable:
- Ponder: int priority on Food, default 0, sort asc (lower = stronger).
- Sort modes: Priority / Alphabetical / Most-used / Recent.
- Most-used and Recent derive from existing FoodEntry data; the only schema
  change is the Ponder column. Two open questions parked (negative ponder;
  per-user vs household scope).

Sequenced after AI text + photo, immediately before the integration-test tier
(testing.md sequencing updated).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The `INutritionEstimator` seam + DeepSeek provider, the estimate endpoint,
and a batch-save endpoint for the multi-item review screen.

- INutritionEstimator (text now, image stubbed for Phase 3) returning a list
  of editable line-items; chosen at deploy time from flat AI_* keys.
- DeepSeekEstimator: OpenAI-compatible chat/completions in JSON mode.
  Hand-rolled resilience (no new dep): per-call timeout linked to the caller's
  CancellationToken so a user Cancel — or the timeout — tears down the in-flight
  request; one retry on transient errors; user-cancel never retried; bad/empty
  JSON is a failure. DisabledNutritionEstimator when AI_ENABLED=false.
- POST /api/user/{id}/estimate/text — side-effect-free: matches items to
  existing catalogue foods by name (refine re-issues it; no writes). Degrades
  to a manual-fallback response on disabled/failure/timeout; propagates user
  cancel. GET /api/ai/status for the SPA to disable affordances when off.
- POST /api/user/{id}/entries/batch — each reviewed row → its own FoodEntry;
  a row with no FoodId defines a new catalogue food first (per-unit derived),
  Source=AiText, AiConfidence recorded.
- Wired in Program.cs; AI_* threaded through deploy compose + env examples
  (AI_API_KEY stays a secret, like SMTP_PASS).
- Tests (+14): fake estimator covers item→row mapping, case-insensitive
  catalogue match, AI-off/failure fallbacks, user-cancel propagation, refine
  passing accumulated notes; batch save covers new-vs-existing food, multi-row,
  validation. 122 pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Swap the hand-rolled timeout/retry loop in DeepSeekEstimator for the MS
resilience pipeline on the typed client: a total timeout of AI_TIMEOUT_SECONDS
wrapping one retry on transient HTTP (5xx/408/network). The caller's
CancellationToken is linked in, so a user Cancel still tears down the request
and stays distinguishable from a timeout (OperationCanceledException vs
TimeoutRejectedException). Estimator is now just send → parse → log.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New AiEntryPage (/entry/ai) implementing the agreed flow:
- Describe what you ate → Estimate. Single in-flight request guarded by a
  `pending` flag + one AbortController; while estimating only Cancel is live
  (Estimate/Refine/Save disabled). Cancel aborts the fetch (→ the backend tears
  down the DeepSeek call); AbortError resets silently. Aborts on unmount too.
- Multi-row review: every field editable, rows deletable, "new" badge for foods
  not yet in the catalogue, per-row + overall confidence, low-confidence rows
  flagged. Shared meal + (date-aware) intake time for the batch.
- Refine loop: client owns the notes thread; each clarification re-issues the
  estimate with accumulated notes.
- Save → POST entries/batch (matched rows reference the food; new rows create it).
- Graceful fallback: AI-off notice / "couldn't estimate — enter manually", with
  a manual link throughout. AI affordances gated on GET /api/ai/status.
- EntryFormPage gains a "Describe it with AI" entry point, shown only when AI is on.

Tests: AiEntryPage (estimate→rows, delete, edit-overrides-on-save, refine passes
notes, unavailable fallback, AI-off notice). EntryFormPage mocks updated for the
new ai-status call. 28 frontend pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…alization

- Default AI_MODEL → deepseek-v4-pro (DeepSeek's latest, 1.6T params). The
  user's API key is already pro-tier; v4-pro gives significantly better
  multilingual name translation (SR pizza+juice went from 0→2/2 matched).
- Stronger system prompt: "CRITICAL RULE: MUST use English (lowercase) for
  every food name regardless of input language. Non-English names will be
  rejected." This instruction, combined with v4-pro, pushed the non-English
  match rate from ~12% to ~30% in e2e testing.
- Name normalization in EstimateController.ResolveAsync: strip parenthetical
  qualifiers — "(groß)", "(Wiener Art)", "(fried)" etc. — before matching
  against the catalogue. Catches the common pattern where the model adds
  clarifying qualifiers to food names.
- Spreads v4-pro through all defaults: AiOptions, Program.cs fallback, both
  deploy env examples, and the staging runtime env.

E2E tested against four languages (SR/RU/DE) with a real DeepSeek key.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nmatched edit

- Switch the default model to deepseek-v4-flash: $0.14/$0.28 per MTok (7-18x
  cheaper than Claude Haiku), ~6s latency, and benchmarked as matching or
  beating v4-pro on Latin-script multilingual food-name accuracy. Reasoning
  models add cost/latency without gains for this structured-JSON task.
- Info tooltip (ℹ️) next to the "What did you eat?" textarea label: explains
  that English works best, Latin-alphabet languages are usually fine, and
  Cyrillic/transcribed Cyrillic may miss items. Hover to read.
- Unmatched rows (isNew) now show with an orange left border, highlighted
  name input, and an "edit the name" hint — the user can correct it right
  there, no extra click needed.
- Name normalization strips parenthetical qualifiers ("Schnitzel (Wiener
  Art)" → "schnitzel") before catalogue matching.
- Updated ai-providers.md (model selection rationale + benchmark results)
  and ai-estimation.md (new Language support section documenting the four
  tiers: English/Latin/Cyrillic/transcribed).
- Fixed cross-test mock contamination in AiEntryPage/EntryFormPage tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
DeepSeek v4-flash is text-only (image_url rejected at the API level), so
Phase 3 photo estimation needs a vision-capable provider.

- ClaudeEstimator: calls the Anthropic Messages API (v1/messages), implements
  the full INutritionEstimator interface. Uses the same system prompt pattern
  and JSON-parsing logic as DeepSeekEstimator. Separate typed HttpClient with
  its own resilience pipeline (timeout + one retry).
- CompositeNutritionEstimator: thin composite delegating text→DeepSeek,
  image→Claude. If only one provider is configured, it falls back to that
  provider for both methods (allows future DeepSeek vision models with zero
  code changes).
- AI_CLAUDE_* flat env keys (AI_CLAUDE_ENABLED / _API_KEY / _MODEL) follow
  the same pattern as SMTP_*/AI_*. Threaded through docker-compose.yml and
  both deploy env examples.
- GET /api/ai/status now reports supportsImages=true when the composite has
  an image-capable provider.
- Staging runtime env has the Claude key + enabled flag.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…es API

Photo capture (finishes Phase 3 photo):
- POST estimate/image: multipart upload → EstimateFromImageAsync → same resolve/
  row-mapping as text; Source=AiPhoto; 15MB cap; image never persisted.
- AiEntryPage: Describe/Photo tab (shown only when supportsImages), live-camera
  capture (getUserMedia → <video> → <canvas> snapshot → blob) + file-upload
  fallback; image kept in memory, re-sent each refine, dropped on save/leave;
  save now sends the real source (AiPhoto/AiText).

Provider unification:
- Both DeepSeek (text) and Claude (photo) speak the Anthropic Messages API, so one
  AnthropicEstimator drives both — they differ only by *connection* (endpoint + key
  + model, AnthropicConnection). DeepSeek uses its Anthropic-compatible /anthropic
  endpoint; the parser now skips the reasoner's leading "thinking" content block.
- DeepSeekEstimator (OpenAI /chat/completions) → renamed OpenAiEstimator, parked for
  a future OpenAI-format provider. ClaudeEstimator → AnthropicEstimator.
- Program.cs: two named HttpClients (ai-text, ai-image) + composite. New
  AI_CLAUDE_BASE_URL; AI_BASE_URL now points at DeepSeek's /anthropic endpoint;
  blank base URLs coalesce to defaults.
- Why photo ≠ DeepSeek: the v4 models are text-only — the /anthropic endpoint
  accepts an image (HTTP 200) but substitutes "[Unsupported Image]" before the model
  sees it (verified against v4-flash/-pro), so photos route to Claude.

Verified locally on real data: text (deepseek-v4-flash via /anthropic) and photo
(claude-haiku-4-5). Tests: backend 134 (new AnthropicEstimatorTests cover the
thinking-block/fence parse + image block; EstimateController image cases), frontend
30. Docs + deploy env/compose updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… hot reload

Replace the fixed two-provider wiring with an N-provider registry tried in priority
order per modality, with automatic fallback. Built for home-lab self-hosting: run a
local model first, fall back to a public provider — for both text and photo.

Model:
- A provider is pure data: { name, convention (openai|anthropic), capabilities
  ([text]/[vision]), baseUrl, model, order, enabled, keyRef }.
- EstimatorChain dispatches: for the requested modality it sorts enabled, capable
  providers by `order` and tries them in turn — first success wins; any failure
  (timeout/network/5xx/bad JSON/empty result, incl. DeepSeek's "[Unsupported Image]")
  falls through to the next. User-cancel stops the chain. No provider for a modality →
  feature blocked ("not configured"); all fail → manual fallback.
- Two wire conventions, one impl each: AnthropicEstimator (Messages API; skips the
  reasoner "thinking" block) and OpenAiEstimator (chat/completions + image_url vision;
  Bearer omitted when key-less — covers self-hosted Ollama/vLLM).

Config split (list vs. secrets):
- Provider LIST = a hot-reloadable JSON file (AI_CONFIG_FILE, bind-mounted, reloadOnChange
  via IOptionsMonitor). Reorder / enable-disable / swap model / change URL / add a
  provider reusing an existing key → live, no redeploy. Verified: vision flipped off in
  <1s with no restart.
- Secret key VALUES = flat AI_KEY_<NAME> env vars; a provider names one via keyRef. Only a
  brand-new secret needs a redeploy. (Documented as the one deliberate exception to the
  flat-env-var rule in CLAUDE.md.)

API/UI:
- /api/ai/status now reports { enabled, supportsText, supportsImages }; the SPA gates the
  Describe input and Photo tab independently (toggle only when both exist).

Removed: AiOptions, DisabledNutritionEstimator, CompositeNutritionEstimator, and the old
single-provider AI_* env vars (full cut). Deploy: compose mounts the providers JSON +
passes AI_KEY_*; new deploy/ai-providers.example.json; env examples rewritten.

Verified locally on real data: text fell through a deliberately-broken order-1 provider to
DeepSeek; photo via Claude; hot-reload live. Tests: backend 144 (new EstimatorChainTests
for ordered fallback / disable / empty / all-fail; OpenAiEstimatorTests for vision + Bearer),
frontend 30, prod build clean. Docs updated (ai-providers.md rewrite, CLAUDE.md exception).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tgres; suppress zero usage count

- Replace ToListItem helper with inline new FoodListItemResponse in all four
  GetFoods sort branches so EF Core translates Ingredients.Count to SQL
  instead of client-evaluating it as 0.
- In most-used mode, map zero usage count to null so the frontend doesn't
  render "· 0×" on every never-logged food.
- Compute Count and MaxIntake upfront in the GroupJoin for most-used to
  avoid double Count() subquery.

Co-Authored-By: Claude <noreply@anthropic.com>
…eMock

- Add Api.IntegrationTests project (net10, xUnit, Testcontainers.PostgreSql, WireMock.Net)
- PostgresFixture: container fixture applying migrations, yielding fresh AppDbContext per test
- WireMockFixture: server fixture on random port for estimator HTTP tests
- EntryIntegrationTests: timestamptz range round-trip + multi-entry ordering on real Postgres
- FoodIntegrationTests: composite ingredient count/isComposite on real Postgres
- EstimatorIntegrationTests: 14 tests covering OpenAI/Anthropic HTTP round-trips,
  auth headers, thinking-block skip, image payloads, server errors, malformed JSON,
  empty items, connection refusal, name filtering
- Update docs/testing.md from planned to built
- Add InternalsVisibleTo for Api.IntegrationTests
- Add project to Fuel.slnx

Co-Authored-By: Claude <noreply@anthropic.com>
…vider chain

Test-correctness fixes:
- EntryIntegrationTests: make the timestamptz round-trip timezone-independent
  (Utc-kind inputs + in-window entry strictly inside the range) — it was green
  only because the dev box runs UTC-4; on a UTC runner the exact-boundary entry
  fell outside the strict < endUtc range.
- EstimatorIntegrationTests: reset the shared WireMock server in the test-class
  constructor so overlapping same-path stubs (200/500/422 on /chat/completions)
  no longer accumulate and resolve order-dependently.
- PostgresFixture.ResetAsync(): truncate all data tables so tests can assert on
  whole-table ordering/counts without cross-test pollution; called per test.

New coverage (the SQL/external logic this tier exists for):
- FoodIntegrationTests: priority / most-used / recent sort modes (GroupJoin +
  aggregate + null-coalescing OrderBy that InMemory translates differently),
  plus FoodService.WouldCreateCycle over a real ingredient graph.
- EstimatorChainIntegrationTests: ordered multi-provider registry over real HTTP
  — fall-through, Order precedence, all-fail -> AiUnavailableException,
  capability filtering (text-only rejects vision), disabled-provider skipping.

28 integration + 144 unit tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ship

Replace the demo Base64(userId:ticks) token with a signed JWT (HMAC-SHA256;
sub=userId, email, exp via JwtTokenService). Wire AddJwtBearer + a fallback
authorization policy so every endpoint requires a valid token unless it opts
out with [AllowAnonymous] (auth, version, unsubscribe, SPA fallback).

Add a global ResourceOwnershipFilter that 403s when the route/query userId
doesn't match the token's sub, closing the old hole where any {userId} in the
URL was trusted. Signing key comes from flat JWT_SIGNING_KEY (ephemeral random
key + warning when unset, so local dev needs no setup); JWT_EXPIRY_DAYS default 30.

Frontend: all API calls go through apiFetch (src/lib/api.ts), which attaches the
bearer token and clears the session + redirects to login on 401.

Tests: JwtTokenServiceTests (sign/validate, wrong-key/expired) and
ResourceOwnershipFilterTests (own/cross-user/anon/no-id). Also register
afterEach(cleanup) in the frontend setup (auto-cleanup never ran without
globals) and switch EntryFormPage's beforeEach to resetAllMocks so a prior
test's debounced once-mock can't shift the next test's sequence.

Docs + deploy: update CLAUDE.md/README, add JWT_SIGNING_KEY/JWT_EXPIRY_DAYS to
docker-compose and both .env examples. Real keys live only in host secret files.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the barcode-scan add-method specified in docs/barcode-lookup.md — point a camera
(or type digits) at a grocery EAN/UPC, resolve it to an official food definition from
the free Open Food Facts API, cache it in our catalogue, and prefill the entry screen.

Backend:
- Food.Barcode (nullable, unique) as the cache key
- IBarcodeFoodLookup + OpenFoodFactsLookup — its own seam, separate from AI estimation
- GET /api/barcode/status + /api/barcode/lookup/{code} — cache-first, fallback on miss
- Flat env config: BARCODE_ENABLED, BARCODE_BASE_URL, BARCODE_TIMEOUT_SECONDS

Frontend:
- @zxing/browser (dynamically imported) — one-shot camera decode + manual digit fallback
- Inline "Scan barcode" toggle on EntryFormPage, prefills via the existing selectedFood flow
- Miss → fallback message with describe/photo/manual buttons

Tests: BarcodeControllerTests (fake lookup, cache, miss, race), OpenFoodFactsLookupTests
(stubbed OFF JSON → BarcodeMatch), EntryFormPage mocks updated.

Deploy: docker-compose + .env examples; BARCODE_ENABLED=true in both host secret files.

Verified end-to-end: Nutella EAN 3017620422003 → found, 5.39 cal/g, cache hit on repeat.

Co-Authored-By: Claude <noreply@anthropic.com>
…, and /#register routing

- ProfileController: validate and normalize ActivityLevel (sedentary/light/moderate/active/very_active)
  and MealPauseScope (all/non-snack) server-side; previously any string was stored silently,
  causing TDEE calculation to fall through to the sedentary default
- Align Microsoft.EntityFrameworkCore direct references (10.0.9→10.0.4) with what
  Npgsql.EntityFrameworkCore.PostgreSQL 10.0.2 brings transitively, eliminating MSB3277
  assembly-version conflict warnings in both test projects
- LoginPage: read window.location.hash on mount and update it on view switches so /#register
  and /#forgot work as direct links, not just UI state toggles
- Add .claude/test-assets.md with test credentials, AI estimate inputs, and API field name reference

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…to, fix image 400s

Frontend:
- Login & onboarding cards were hardcoded white while text vars flip light in
  dark mode → unreadable; make both theme-aware (surface bg + themed inputs).
- AI review grid used repeat(6,1fr) and blew past its container; switch to
  minmax(0,1fr) + min-width:0 so the row stays in bounds.
- Fold barcode/EAN into the AI entry Photo mode (camera / upload / scan) with a
  styled scan button; a resolved product joins the review list as a matched row.
  Remove the barcode UI from the manual entry form.
- Normalize photos client-side (decode → canvas → JPEG, cap longest edge 1568px)
  before sending to the vision provider — fixes HEIC + >5MB uploads 400ing.
- Detect non-secure-context camera and message clearly (staging is plain HTTP).
- Day header: size the Today button to match the nav arrows.
- Entry-form food search lists the whole catalogue on focus, narrows on type.
- Catalogue: label the priority control + wrap the card so it stays reachable on
  narrow screens; composite foods get a hover popover and a click-to-expand caret
  listing ingredients with weights.

Backend:
- AnthropicEstimator: log the provider's response body on non-2xx instead of
  letting EnsureSuccessStatusCode drop it, so image 400s are diagnosable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…, icon delete buttons

- Catalogue: shrink priority, reorder after composite badge, round + add button, narrower search
- Home: swap calorie colors (meal total primary, entry calories secondary)
- Weight: round red delete icon matching meal entries
- Settings: grid children min-width:0 so 1fr-1fr cols shrink, body frame in own grid row
- Nav: wrap on narrow screens
- Upload labels: explicit centering to override base-class padding
- Skill: project-startup always --host + nohup for LAN access

Co-Authored-By: Claude <noreply@anthropic.com>
Trifunovich and others added 21 commits June 22, 2026 11:51
…oak-ready, backward-compat)

CrimsonRaven is moving to Keycloak (GUID subjects), so the old Guid.TryParse(sub) discriminator inverts. Provision only when iss == OIDC_AUTHORITY + a re-entrancy marker; userinfo -> Keycloak realm path. Works unchanged against the current Zitadel, so it ships safely ahead of the cutover.
CrimsonRaven is Keycloak, which hosts the themed login + native email
verification, resend and forgot-password. So remove what we built around
Zitadel's gaps:

- delete EmailVerificationHoldMiddleware (+ test) and its Program.cs wiring
- delete AuthResendController + the app-mailer/CR_MAILER_PAT/SendEmailCode path
- OidcUserProvisioner: drop the unverified-email hold + verified-email gate;
  link to an existing row by email unconditionally (KC gates verification
  before issuing a token). Keep the stable-identity link-by-email + sub rewrite.
- ConfigController: drop the IdP logo-scrape (KC themes own branding); add
  authMode for the legacy break-glass, matching ThoseDays
- frontend: drop useOidcLogo + the held/resend UI from LoginPage/AuthContext/
  AuthCallbackPage; keep the CR auto-redirect + the legacy break-glass form
- tests updated (backend 188+28 green, frontend 47 green)
- Bump VERSION 1.7 -> 1.8; rewrite RELEASE_NOTES.md to announce the upgraded login and ask
  users to register again with the SAME email (stable-identity relinks their data on first login).
- Refresh docs/auth-crimsonraven.md for the Keycloak cutover.
Fuel showed "CrimsonRaven offline" and fell back to the legacy
email/password form on mobile when a single /api/config fetch failed:
loadRuntimeConfig memoised that failure for the whole page session, so a
CR-only account (no local password) was stranded until a full reload.

- loadRuntimeConfig: retry with backoff, cache only a successful result,
  never memoise a failure (a transient, un-cached oidcEnabled:false), and
  add refreshRuntimeConfig() to bypass the cache.
- AuthContext: re-check CR on `online` / visibility regain so a stranded
  user recovers without reloading.
- Add oidc.test.ts; full suite green (51 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The SW used a constant cache name (indigo-swallow-v1), so its activate
handler never purged anything across deploys and hashed build assets
accumulated indefinitely (~150 cached entries for fuel alone). Stamp the
cache name with APP_VERSION at build time — and in dev via middleware —
through a small stamp-service-worker Vite plugin, so each release uses a
fresh cache name and the previous cache is evicted on activate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Header: move primary nav into a dropdown under the user's name; add a
  far-left Diary (home) icon. "Home" is renamed "Diary" and Logout moves
  into the menu. Theme/font controls stay in the header.
- New Stats page (/stats): Week/Month/Year bar chart of registered calories
  (per day / per week / per month) with period navigation, plus daily
  avg/max/min rings vs. the daily goal and a per-bucket dashed goal line
  scaled to each bucket's day count.
- Persist the chosen stats range in storage.
- Don't register the PWA service worker in dev: its cache-first asset
  strategy served stale Vite modules across restarts (blank page on boot).
  Production builds are unaffected.
- Bump VERSION to 1.9 and refresh user-facing release notes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Manual Add Entry: move the food sort-order picker into Settings, reword the
"add to catalogue" link, and move the AI entry button to a bottom-right
"Use AI Instead" CTA.

AI entry: split the input into three tabs (Text / Photo / Barcode); add a
shared checkmark confirm button (camera capture, barcode look-up, Estimate);
disable Estimate on empty text; single right-aligned "Upload" control per tab
that opens the live camera on a secure origin (device-permission prompt on
desktop, rear camera on phone) and falls back to the file input otherwise;
resize the in-app camera preview to ~70%.

Settings: reorder sections (Daily Goal, Profile, Metabolism, Display,
Preferences, Meal Pause, Notifications) and add a "Sort foods by" preference.
Default the release-email opt-in to on (seeder included).

Spec: docs/input-field-redesign.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
User-facing release notes for the meal-logging & AI-entry input redesign;
bump VERSION 1.9 → 1.10.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Settings → Preferences: font-size + spacing steppers in two columns;
  spacing scales key paddings and section gaps via a --density CSS var
- AI photo estimate: optional description sent with the first prompt
- appearance.ts centralizes text-size/spacing; drop the header font control
…l build

- APP_VERSION's CI run-number segment auto-increments every deploy, so keying off
  the full version emailed users on every redeploy
- match ThoseDays: only a hand VERSION bump announces; same-line rebuilds stay
  silent (stored value normalized so legacy full versions count as announced)
- Each snack row now carries its own logged-at time, just left of the calories
- Snack section header shows the last snack's time, matching the other meals
- reset-password now requires a single-use, 1h, emailed token (stored hashed);
  request step is generic to avoid email enumeration
- OIDC-only accounts get no local reset, so it can't bypass CrimsonRaven
- PBKDF2 -> 600k iters/32B, self-describing hash w/ legacy-hash fallback;
  constant-time compare
- OIDC_AUDIENCE now mandatory when OIDC enabled (blocks cross-app token replay)
- add PasswordResetTokens table + migration; new AuthController + service tests
…LICENSE

- Dockerfile: drop to UID 1654, pre-create writable logs/ and backups/
- deploy.yml: top-level permissions: contents: read
- add LICENSE (matches CONTRIBUTING/PR template)
… mobile

Single button was wired to live getUserMedia (camera only, no shutter for
barcode). Route both to a native file input with no `capture`, so phones show
the OS Camera/Photo Library chooser. Remove the dead live-camera code.
- rethrow OperationCanceledException when ct is the caller's token, so a closed
  tab / navigate-away no longer poisons the shared cache for 60s
- shorter TTL for negative results (10s) so a transient blip clears fast
- image: ghcr.io/trifunovich/fuel -> ghcr.io/ofbirds/fuel (workflow, env
  examples, runbook, infra doc)
- repo links -> github.com/OfBirds/Fuel (issue template, runner config, OFF
  user-agent)
- private vuln reporting via GitHub security advisories
- CODEOWNERS default reviewer for branch protection
- weekly dependabot for nuget, npm, github-actions
- replace real domains/IPs/client-ids with example placeholders across env
  examples, docs, the OIDC test issuer, ai-providers, and the startup skill
- auth-crimsonraven.md: rewrite Zitadel-era content (Keycloak now), fix the
  /api/config shape (drop logo fields, add authMode), correct userinfo path,
  note OIDC_AUDIENCE is now mandatory
Regression test for the false-offline bug — a client abort must not poison the
shared cache, while an HttpClient timeout still records offline.
Bumps [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) from 7.17.0 to 7.18.1.
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/react-router-dom@7.18.1/packages/react-router-dom/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@7.18.1/packages/react-router-dom)

---
updated-dependencies:
- dependency-name: react-router-dom
  dependency-version: 7.18.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code labels Jul 3, 2026
@Trifunovich Trifunovich closed this Jul 3, 2026
@dependabot @github

dependabot Bot commented on behalf of github Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

OK, I won't notify you again about this release, but will get in touch when a new version is available. If you'd rather skip all updates until the next major or minor version, let me know by commenting @dependabot ignore this major version or @dependabot ignore this minor version. You can also ignore all major, minor, or patch releases for a dependency by adding an ignore condition with the desired update_types to your config file.

If you change your mind, just re-open this PR and I'll resolve any conflicts on it.

@Trifunovich
Trifunovich deleted the dependabot/npm_and_yarn/frontend/react-router-dom-7.18.1 branch July 3, 2026 20:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant