From 39d18c241e512249de7c38f6fd748fe3ea28d1df Mon Sep 17 00:00:00 2001 From: Julian Tabel Date: Wed, 11 Feb 2026 21:48:29 +0100 Subject: [PATCH] Integrate name suggestions into encounter registration UI Add clickable suggestion chips below the nickname input in the encounter modal. Chips are fetched from GET /runs/{id}/name-suggestions via React Query, shown only when a naming scheme is set. Clicking a chip fills in the nickname; a regenerate button fetches a fresh random batch. Completes the Name Generation epic. Co-Authored-By: Claude Opus 4.6 --- ...ame-suggestions-into-encounter-registra.md | 16 +++---- .../nuzlocke-tracker-igl3--name-generation.md | 8 ++-- frontend/src/api/runs.ts | 4 ++ frontend/src/components/EncounterModal.tsx | 42 +++++++++++++++++++ frontend/src/hooks/useRuns.ts | 10 ++++- frontend/src/pages/RunEncounters.tsx | 2 + 6 files changed, 69 insertions(+), 13 deletions(-) diff --git a/.beans/nuzlocke-tracker-bi4e--integrate-name-suggestions-into-encounter-registra.md b/.beans/nuzlocke-tracker-bi4e--integrate-name-suggestions-into-encounter-registra.md index 899ded3..9b9e6ce 100644 --- a/.beans/nuzlocke-tracker-bi4e--integrate-name-suggestions-into-encounter-registra.md +++ b/.beans/nuzlocke-tracker-bi4e--integrate-name-suggestions-into-encounter-registra.md @@ -1,11 +1,11 @@ --- # nuzlocke-tracker-bi4e title: Integrate name suggestions into encounter registration UI -status: todo +status: completed type: task priority: normal created_at: 2026-02-11T15:56:44Z -updated_at: 2026-02-11T20:23:40Z +updated_at: 2026-02-11T20:48:02Z parent: nuzlocke-tracker-igl3 --- @@ -27,9 +27,9 @@ Show name suggestions in the encounter registration flow so users can pick a nic ## Checklist -- [ ] Add a name suggestions component (chips/buttons with regenerate) -- [ ] Integrate the component into the encounter registration modal/form -- [ ] Wire up the backend API endpoint to the component via React Query -- [ ] Ensure clicking a suggestion populates the nickname field -- [ ] Ensure regenerate fetches a new batch from the API -- [ ] Hide suggestions gracefully if no naming scheme is set on the run \ No newline at end of file +- [x] Add a name suggestions component (chips/buttons with regenerate) +- [x] Integrate the component into the encounter registration modal/form +- [x] Wire up the backend API endpoint to the component via React Query +- [x] Ensure clicking a suggestion populates the nickname field +- [x] Ensure regenerate fetches a new batch from the API +- [x] Hide suggestions gracefully if no naming scheme is set on the run \ No newline at end of file diff --git a/.beans/nuzlocke-tracker-igl3--name-generation.md b/.beans/nuzlocke-tracker-igl3--name-generation.md index a6c3cff..b29cc27 100644 --- a/.beans/nuzlocke-tracker-igl3--name-generation.md +++ b/.beans/nuzlocke-tracker-igl3--name-generation.md @@ -1,11 +1,11 @@ --- # nuzlocke-tracker-igl3 title: Name Generation -status: todo +status: completed type: epic priority: normal created_at: 2026-02-05T13:45:15Z -updated_at: 2026-02-11T20:44:23Z +updated_at: 2026-02-11T20:48:02Z --- Implement a dictionary-based nickname generation system for Nuzlocke runs. Instead of using an LLM API to generate names on the fly, provide a static dictionary of words categorised by theme. A word can belong to multiple categories, making it usable across different naming schemes. @@ -29,6 +29,6 @@ Implement a dictionary-based nickname generation system for Nuzlocke runs. Inste - [x] Word dictionary data file exists with multiple categories, each containing 150-200 words - [x] Name suggestion engine picks random names from the selected category, avoiding duplicates already used in the run -- [ ] Encounter registration UI shows 5-10 clickable name suggestions -- [ ] User can regenerate suggestions if none fit +- [x] Encounter registration UI shows 5-10 clickable name suggestions +- [x] User can regenerate suggestions if none fit - [x] User can select a naming scheme per run \ No newline at end of file diff --git a/frontend/src/api/runs.ts b/frontend/src/api/runs.ts index 2a7e4d4..1757bcb 100644 --- a/frontend/src/api/runs.ts +++ b/frontend/src/api/runs.ts @@ -32,3 +32,7 @@ export function deleteRun(id: number): Promise { export function getNamingCategories(): Promise { return api.get('/runs/naming-categories') } + +export function getNameSuggestions(runId: number, count = 10): Promise { + return api.get(`/runs/${runId}/name-suggestions?count=${count}`) +} diff --git a/frontend/src/components/EncounterModal.tsx b/frontend/src/components/EncounterModal.tsx index cd8d3a8..6c38032 100644 --- a/frontend/src/components/EncounterModal.tsx +++ b/frontend/src/components/EncounterModal.tsx @@ -1,5 +1,6 @@ import { useState, useEffect, useMemo } from 'react' import { useRoutePokemon } from '../hooks/useGames' +import { useNameSuggestions } from '../hooks/useRuns' import { EncounterMethodBadge, getMethodLabel, @@ -15,6 +16,8 @@ import type { interface EncounterModalProps { route: Route gameId: number + runId: number + namingScheme?: string | null existing?: EncounterDetail dupedPokemonIds?: Set retiredPokemonIds?: Set @@ -92,6 +95,8 @@ function pickRandomPokemon( export function EncounterModal({ route, gameId, + runId, + namingScheme, existing, dupedPokemonIds, retiredPokemonIds, @@ -120,6 +125,10 @@ export function EncounterModal({ const isEditing = !!existing + const showSuggestions = !!namingScheme && status === 'caught' && !isEditing + const { data: suggestions, refetch: regenerate, isFetching: loadingSuggestions } = + useNameSuggestions(showSuggestions ? runId : null) + // Pre-select pokemon when editing useEffect(() => { if (existing && routePokemon) { @@ -380,6 +389,39 @@ export function EncounterModal({ placeholder="Give it a name..." className="w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500" /> + {showSuggestions && suggestions && suggestions.length > 0 && ( +
+
+ + Suggestions ({namingScheme}) + + +
+
+ {suggestions.map((name) => ( + + ))} +
+
+ )} )} diff --git a/frontend/src/hooks/useRuns.ts b/frontend/src/hooks/useRuns.ts index 7843523..8e65126 100644 --- a/frontend/src/hooks/useRuns.ts +++ b/frontend/src/hooks/useRuns.ts @@ -1,6 +1,6 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { toast } from 'sonner' -import { getRuns, getRun, createRun, updateRun, deleteRun, getNamingCategories } from '../api/runs' +import { getRuns, getRun, createRun, updateRun, deleteRun, getNamingCategories, getNameSuggestions } from '../api/runs' import type { CreateRunInput, UpdateRunInput } from '../types/game' export function useRuns() { @@ -59,3 +59,11 @@ export function useNamingCategories() { staleTime: Infinity, }) } + +export function useNameSuggestions(runId: number | null) { + return useQuery({ + queryKey: ['name-suggestions', runId], + queryFn: () => getNameSuggestions(runId!), + enabled: runId !== null, + }) +} diff --git a/frontend/src/pages/RunEncounters.tsx b/frontend/src/pages/RunEncounters.tsx index ec50e65..7b07b43 100644 --- a/frontend/src/pages/RunEncounters.tsx +++ b/frontend/src/pages/RunEncounters.tsx @@ -1431,6 +1431,8 @@ export function RunEncounters() {