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
Closed
chore(deps): bump react-router-dom from 7.17.0 to 7.18.1 in /frontend#7dependabot[bot] wants to merge 83 commits into
dependabot[bot] wants to merge 83 commits into
Conversation
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>
…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>
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 If you change your mind, just re-open this PR and I'll resolve any conflicts on it. |
Trifunovich
deleted the
dependabot/npm_and_yarn/frontend/react-router-dom-7.18.1
branch
July 3, 2026 20:22
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bumps react-router-dom from 7.17.0 to 7.18.1.
Changelog
Sourced from react-router-dom's changelog.
Commits
afdf85dRelease v7.18.1 (#15253)2ecaa1dFix react-router-dom main entry metadata (#15238)6fb1e79Release v7.18.0 (#15187)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 rebasewill rebase this PR@dependabot recreatewill recreate this PR, overwriting any edits that have been made to it@dependabot show <dependency name> ignore conditionswill show all of the ignore conditions of the specified dependency@dependabot ignore this major versionwill 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 versionwill 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 dependencywill close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)