diff --git a/frontend/src/pages/SetupPage.tsx b/frontend/src/pages/SetupPage.tsx index fc3b5a6e..74e926ba 100644 --- a/frontend/src/pages/SetupPage.tsx +++ b/frontend/src/pages/SetupPage.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import { Navigate, useNavigate } from 'react-router-dom'; -import { useQuery } from '@tanstack/react-query'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; import { Key, Mail, Lock, Eye, EyeOff, AlertCircle, ArrowLeft, ArrowRight, Copy, Check, ExternalLink, Bug, Lightbulb, Star, Coffee } from 'lucide-react'; import type { LucideIcon } from 'lucide-react'; import { toast } from 'react-toastify'; @@ -65,6 +65,7 @@ export const SetupPage: React.FC = () => { const { t } = useTranslation(); const navigate = useNavigate(); const { login } = useAdminAuth(); + const queryClient = useQueryClient(); const { data: status, isLoading: statusLoading, isError: statusError } = useQuery({ queryKey: ['setup-status'], @@ -292,7 +293,7 @@ export const SetupPage: React.FC = () => { const enableUsageReporting = async () => { setIsEnablingUsageReporting(true); try { - await productUsageService.enable(); + queryClient.setQueryData(['productUsage'], await productUsageService.enable()); toast.success(t('setup.usageReporting.enabled')); } catch { toast.warn(t('setup.usageReporting.enableFailed')); @@ -307,7 +308,7 @@ export const SetupPage: React.FC = () => { // the same question seconds later on their first dashboard visit. const skipUsageReporting = async () => { try { - await productUsageService.promptSeen(); + queryClient.setQueryData(['productUsage'], await productUsageService.promptSeen()); } catch { /* best-effort — worst case the dashboard asks once more */ } setStep('community'); }; diff --git a/frontend/src/pages/__tests__/SetupPage.usageReporting.test.tsx b/frontend/src/pages/__tests__/SetupPage.usageReporting.test.tsx index 0b23a258..e3c21d41 100644 --- a/frontend/src/pages/__tests__/SetupPage.usageReporting.test.tsx +++ b/frontend/src/pages/__tests__/SetupPage.usageReporting.test.tsx @@ -39,12 +39,15 @@ beforeEach(() => { vi.clearAllMocks(); vi.mocked(usage.status).mockResolvedValue({ status: 'disabled', collector_url: 'https://custom-collector.example.test' } as never); vi.mocked(usage.enable).mockResolvedValue({ status: 'active' } as never); + vi.mocked(usage.promptSeen).mockResolvedValue({ status: 'disabled', prompt_shown: true } as never); HTMLDialogElement.prototype.showModal = function () { this.setAttribute('open', ''); }; }); afterEach(cleanup); +let client: QueryClient; async function reachInvitation() { - render( + client = new QueryClient({ defaultOptions: { queries: { retry: false } } }); + render( ); fireEvent.change(await screen.findByLabelText('setup.tokenLabel'), { target: { value: 'test-token' } }); @@ -82,6 +85,7 @@ it('uses the full settings disclosure and configured collector before accepting fireEvent.click(dialog.getByRole('button', { name: 'productUsage.enable' })); await screen.findByText('setup.community.mission'); expect(usage.enable).toHaveBeenCalledTimes(1); + expect(client.getQueryData(['productUsage'])).toMatchObject({ status: 'active' }); }); it('skipping the invitation never enables reporting', async () => { @@ -89,6 +93,8 @@ it('skipping the invitation never enables reporting', async () => { fireEvent.click(screen.getByRole('button', { name: 'setup.usageReporting.skip' })); await screen.findByText('setup.community.mission'); expect(usage.enable).not.toHaveBeenCalled(); + expect(usage.promptSeen).toHaveBeenCalledTimes(1); + expect(client.getQueryData(['productUsage'])).toMatchObject({ status: 'disabled', prompt_shown: true }); }); it.each(['failed', 'invalid'])('keeps setup usable when collector configuration is %s', async (failure) => {