refactor(compare-toggle): migrate to queryKeys.dashboardWidgets factory

The compare-toggle component still declared three bare-literal
`["user", "dashboardWidgets"]` queryKeys for read, onMutate
cancel/set, onError rollback and onSettled invalidation. Migrate every
call site to the central `queryKeys.dashboardWidgets()` factory so a
future rename or shape evolution only touches one file. Extend the
W-RSC factory-bypass walker to cover `src/components/comparison/`
so any future regression in this directory gets caught by CI.

Closes UX H1 from the v1.4.40 QA round.
This commit is contained in:
Marc-André Bombeck committed 2026-05-21 12:42:17 +02:00
1 parent 853792ca06
commit 4d4a034efc
2 files changed
+12 -9

No files matched your search

+11 -9
View File
@@ -3,6 +3,7 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useTranslations } from "@/lib/i18n/context";
import { cn } from "@/lib/utils";
import { queryKeys } from "@/lib/query-keys";
import {
COMPARISON_BASELINES,
resolveDashboardLayout,
@@ -26,7 +27,7 @@ export function CompareToggle({ className }: { className?: string }) {
const queryClient = useQueryClient();
const { data: layoutData } = useQuery({
queryKey: ["user", "dashboardWidgets"],
queryKey: queryKeys.dashboardWidgets(),
queryFn: async () => {
const res = await fetch("/api/dashboard/widgets");
if (!res.ok) throw new Error("failed");
@@ -52,15 +53,14 @@ export function CompareToggle({ className }: { className?: string }) {
},
onMutate: async (next) => {
await queryClient.cancelQueries({
queryKey: ["user", "dashboardWidgets"],
queryKey: queryKeys.dashboardWidgets(),
});
const prev = queryClient.getQueryData<DashboardLayout>([
"user",
"dashboardWidgets",
]);
const prev = queryClient.getQueryData<DashboardLayout>(
queryKeys.dashboardWidgets(),
);
if (prev) {
queryClient.setQueryData<DashboardLayout>(
["user", "dashboardWidgets"],
queryKeys.dashboardWidgets(),
{ ...prev, comparisonBaseline: next },
);
}
@@ -68,11 +68,13 @@ export function CompareToggle({ className }: { className?: string }) {
},
onError: (_err, _next, ctx) => {
if (ctx?.prev) {
queryClient.setQueryData(["user", "dashboardWidgets"], ctx.prev);
queryClient.setQueryData(queryKeys.dashboardWidgets(), ctx.prev);
}
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ["user", "dashboardWidgets"] });
queryClient.invalidateQueries({
queryKey: queryKeys.dashboardWidgets(),
});
},
});
+1
View File
@@ -172,6 +172,7 @@ describe("queryKey factory enforcement", () => {
// guard's scope until a follow-up wave routes those keys.
const guardedRoots = [
join(repoRoot, "src", "components", "charts"),
join(repoRoot, "src", "components", "comparison"),
join(repoRoot, "src", "app", "page.tsx"),
join(repoRoot, "src", "hooks", "use-auth.ts"),
];