Recipe search card on the AI assistant screen
CI / ci (push) Successful in 2m1s
CI / ci (pull_request) Successful in 1m55s

Add a Recipe search card to /settings/ai: a SearXNG | Brave segmented
control, the SearXNG URL pre-filled from the saved settings, or the Brave
API key shown masked (last four characters) with Replace. The provider,
URL and new key are a draft saved together with PATCH
/api/v1/settings/recipe-search; Test calls the recipe-search test
endpoint and shows "Found N recipes for 'chicken tikka masala'" or the
error.

The leave-without-saving guard moves to page level so it covers both the
AI assistant form and the card; the shell test now renders in a data
router as main.tsx does.

Maestro task: 0698ae2c-f65e-408e-a9c6-198edc2c5696

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-06 10:27:16 -04:00
co-authored by Claude Opus 5.5
parent e9a35a7f57
commit cb42ffa280
8 changed files with 646 additions and 33 deletions
+2 -1
View File
@@ -5,7 +5,7 @@ import { createNetworkStatus } from './pwa/networkStatus.ts'
import { mockViewport } from './test/matchMedia.ts'
import { errorResponse, fakeLocation, jsonResponse, ME, request, stubFetch, stubFetchByPath } from './test/fetchStub.ts'
import App, { routes } from './App.tsx'
import { AI_SETTINGS, SETTINGS_API } from './test/ApiProviders.tsx'
import { AI_SETTINGS, RECIPE_SEARCH_SETTINGS, SETTINGS_API } from './test/ApiProviders.tsx'
import { InDataRouter } from './test/dataRouter.tsx'
afterEach(() => {
@@ -80,6 +80,7 @@ describe('App routes', () => {
'/api/v1/me': () => jsonResponse(200, ME),
'/api/v1/settings/ai': () => jsonResponse(200, AI_SETTINGS),
'/api/v1/settings/ai/models': () => jsonResponse(200, { models: [] }),
'/api/v1/settings/recipe-search': () => jsonResponse(200, RECIPE_SEARCH_SETTINGS),
})
renderAt('/settings/ai')
const heading = await screen.findByRole('heading', { level: 1, name: 'AI assistant' })