refactor(query-keys): route long-tail surfaces through the factory

The settings / medications / admin / hooks tree still declared
bare-literal `queryKey: [ … ]` arrays at ~70 call sites. Centralise
them through `queryKeys.<entry>()` so mutation-invalidation prefixes
stay symmetrical and a future rename only touches one location.

New factory entries cover per-medication compliance / titration /
cadence / glp1-details / drug-level-chart / intake-list, the
Withings status read, the paginated admin audit-log filter, and the
workouts-recent list opts. Existing entries cover everything else
(passkeys, tokens, settings, telegram, integrations, research-mode,
admin status / users / backups / feedback / host-metrics / app-logs /
ai-quality / assistant-flags, coach-prefs, feature-flags, etc.).

Lockstep updates:

- `eslint-plugins/healthlog/queryKey-factory.js` extends
  `GUARDED_DIRECTORIES` to cover `src/components/{settings,
  medications,admin}` + `src/hooks`, plus the three guarded files
  under `src/app/medications/**` and `src/app/targets/page.tsx`.
- `src/lib/__tests__/query-keys.test.ts` mirrors the guard's
  `guardedRoots` and pins the shape of every new factory entry.

Tests stay green: 4 736 unit; the factory walker reports zero
bare-literal `queryKey:` in any newly-guarded surface.
This commit is contained in:
Marc-André Bombeck committed 2026-05-21 19:04:22 +02:00
1 parent 6850818271
commit e2018b2d15
43 files changed
+332 -118

No files matched your search

+2 -1
View File
@@ -2,6 +2,7 @@
import { useQuery } from "@tanstack/react-query";
import { queryKeys } from "@/lib/query-keys";
import {
DEFAULT_COACH_PREFS,
type CoachPrefs,
@@ -24,7 +25,7 @@ import {
*/
export function useCoachPrefs(opts?: { enabled?: boolean }) {
return useQuery<CoachPrefs>({
queryKey: ["coach-prefs"],
queryKey: queryKeys.coachPrefs(),
queryFn: async () => {
const res = await fetch("/api/auth/me/coach-prefs");
if (!res.ok) return DEFAULT_COACH_PREFS;
+3 -1
View File
@@ -3,6 +3,8 @@
import { useContext } from "react";
import { QueryClientContext, useQuery } from "@tanstack/react-query";
import { queryKeys } from "@/lib/query-keys";
/**
* v1.4.31 — Client-side accessor for the operator's assistant
* feature-flag matrix. Mirrors `GET /api/feature-flags`'s response
@@ -86,7 +88,7 @@ export function useFeatureFlags(): AssistantFlagSet {
function useFeatureFlagsQuery(): AssistantFlagSet {
const query = useQuery({
queryKey: ["feature-flags"],
queryKey: queryKeys.featureFlags(),
queryFn: fetchFeatureFlags,
staleTime: 60_000,
gcTime: 300_000,
+2 -1
View File
@@ -13,7 +13,8 @@ import type { SubPageAnalyticsData } from "@/types/analytics";
* `schlaf`) each used to declare:
*
* - a private `AnalyticsData` interface,
* - a `useQuery({ queryKey: ["analytics"], staleTime: 60_000 })` block,
* - a `useQuery` block keyed on `["analytics"]` with a 60-second
* `staleTime`,
* - an `if (isAuthenticated && analytics && !hasMetricData(...)) { … }`
* short-circuit that rendered a sub-page-specific empty state.
*
+6 -4
View File
@@ -92,10 +92,12 @@ export function useWorkouts(opts: UseWorkoutsOptions = {}): UseWorkoutsResult {
const { isAuthenticated } = useAuth();
const query = useQuery({
queryKey: [
...queryKeys.workoutsRecent(),
{ limit: opts.limit, offset: opts.offset, since: opts.since, sportType: opts.sportType },
] as const,
queryKey: queryKeys.workoutsRecentList({
limit: opts.limit,
offset: opts.offset,
since: opts.since,
sportType: opts.sportType,
}),
queryFn: () => fetchWorkouts(opts),
enabled: isAuthenticated,
staleTime: 60 * 1000,