Replace free-text encounter method input with dropdown selector
Use the predefined METHOD_ORDER/METHOD_CONFIG from EncounterMethodBadge to populate a select dropdown with all known encounter methods plus an "Other" option for custom values. Shows a colored badge preview on selection. Correctly handles editing encounters with non-standard methods. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,11 @@
|
|||||||
---
|
---
|
||||||
# nuzlocke-tracker-5o1v
|
# nuzlocke-tracker-5o1v
|
||||||
title: Improve encounter method input in route encounter form
|
title: Improve encounter method input in route encounter form
|
||||||
status: todo
|
status: completed
|
||||||
type: feature
|
type: feature
|
||||||
|
priority: normal
|
||||||
created_at: 2026-02-08T19:06:10Z
|
created_at: 2026-02-08T19:06:10Z
|
||||||
updated_at: 2026-02-08T19:06:10Z
|
updated_at: 2026-02-08T19:17:14Z
|
||||||
parent: nuzlocke-tracker-iu5b
|
parent: nuzlocke-tracker-iu5b
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
# nuzlocke-tracker-8w9s
|
||||||
|
title: Gauntlet rule option for genlockes
|
||||||
|
status: draft
|
||||||
|
type: feature
|
||||||
|
created_at: 2026-02-08T19:15:43Z
|
||||||
|
updated_at: 2026-02-08T19:15:43Z
|
||||||
|
parent: nuzlocke-tracker-25mh
|
||||||
|
---
|
||||||
|
|
||||||
|
Add an optional **Gauntlet** rule for genlocke runs. When enabled, Pokemon that enter the Hall of Fame at the end of a leg are NOT transferred to the next game — instead, they (and their evolutionary families) are added to the dupe list for subsequent legs.
|
||||||
|
|
||||||
|
## Behavior
|
||||||
|
- Toggled as an optional rule when creating/editing a genlocke
|
||||||
|
- When a leg is completed, surviving Hall of Fame Pokemon are marked as "dupes" for the next leg rather than being transferred as eggs/starters
|
||||||
|
- This means the player cannot catch anything in those evolutionary lines in future legs (standard dupe clause enforcement)
|
||||||
|
- The cumulative dupe list grows with each completed leg
|
||||||
|
- Effectively forces the player to use entirely new Pokemon each generation
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- This is a variant of the standard genlocke format that increases difficulty
|
||||||
|
- The dupe list should track evolutionary families, not just the specific Pokemon (e.g., if Charizard is in the HoF, Charmander and Charmeleon are also duped)
|
||||||
|
- Should integrate with the existing dupe clause system already in the tracker
|
||||||
|
- Consider showing the cumulative gauntlet dupe list somewhere in the genlocke dashboard
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
const METHOD_CONFIG: Record<string, { label: string; color: string }> = {
|
export const METHOD_CONFIG: Record<string, { label: string; color: string }> = {
|
||||||
starter: {
|
starter: {
|
||||||
label: 'Starter',
|
label: 'Starter',
|
||||||
color:
|
color:
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { type FormEvent, useState } from 'react'
|
import { type FormEvent, useState } from 'react'
|
||||||
import { FormModal } from './FormModal'
|
import { FormModal } from './FormModal'
|
||||||
import { PokemonSelector } from './PokemonSelector'
|
import { PokemonSelector } from './PokemonSelector'
|
||||||
|
import { METHOD_ORDER, METHOD_CONFIG, getMethodLabel } from '../EncounterMethodBadge'
|
||||||
import type { RouteEncounterDetail, CreateRouteEncounterInput, UpdateRouteEncounterInput } from '../../types'
|
import type { RouteEncounterDetail, CreateRouteEncounterInput, UpdateRouteEncounterInput } from '../../types'
|
||||||
|
|
||||||
interface RouteEncounterFormModalProps {
|
interface RouteEncounterFormModalProps {
|
||||||
@@ -21,7 +22,13 @@ export function RouteEncounterFormModal({
|
|||||||
isDeleting,
|
isDeleting,
|
||||||
}: RouteEncounterFormModalProps) {
|
}: RouteEncounterFormModalProps) {
|
||||||
const [pokemonId, setPokemonId] = useState(encounter?.pokemonId ?? 0)
|
const [pokemonId, setPokemonId] = useState(encounter?.pokemonId ?? 0)
|
||||||
const [encounterMethod, setEncounterMethod] = useState(encounter?.encounterMethod ?? '')
|
|
||||||
|
const initialMethod = encounter?.encounterMethod ?? ''
|
||||||
|
const isKnownMethod = METHOD_ORDER.includes(initialMethod)
|
||||||
|
const [selectedMethod, setSelectedMethod] = useState(isKnownMethod ? initialMethod : initialMethod ? 'other' : '')
|
||||||
|
const [customMethod, setCustomMethod] = useState(isKnownMethod ? '' : initialMethod)
|
||||||
|
const encounterMethod = selectedMethod === 'other' ? customMethod : selectedMethod
|
||||||
|
|
||||||
const [encounterRate, setEncounterRate] = useState(String(encounter?.encounterRate ?? ''))
|
const [encounterRate, setEncounterRate] = useState(String(encounter?.encounterRate ?? ''))
|
||||||
const [minLevel, setMinLevel] = useState(String(encounter?.minLevel ?? ''))
|
const [minLevel, setMinLevel] = useState(String(encounter?.minLevel ?? ''))
|
||||||
const [maxLevel, setMaxLevel] = useState(String(encounter?.maxLevel ?? ''))
|
const [maxLevel, setMaxLevel] = useState(String(encounter?.maxLevel ?? ''))
|
||||||
@@ -64,14 +71,40 @@ export function RouteEncounterFormModal({
|
|||||||
)}
|
)}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium mb-1">Encounter Method</label>
|
<label className="block text-sm font-medium mb-1">Encounter Method</label>
|
||||||
<input
|
<select
|
||||||
type="text"
|
|
||||||
required
|
required
|
||||||
value={encounterMethod}
|
value={selectedMethod}
|
||||||
onChange={(e) => setEncounterMethod(e.target.value)}
|
onChange={(e) => {
|
||||||
placeholder="e.g. Walking, Surfing, Fishing"
|
setSelectedMethod(e.target.value)
|
||||||
|
if (e.target.value !== 'other') setCustomMethod('')
|
||||||
|
}}
|
||||||
className="w-full px-3 py-2 border rounded-md dark:bg-gray-700 dark:border-gray-600"
|
className="w-full px-3 py-2 border rounded-md dark:bg-gray-700 dark:border-gray-600"
|
||||||
/>
|
>
|
||||||
|
<option value="">Select method...</option>
|
||||||
|
{METHOD_ORDER.map((m) => (
|
||||||
|
<option key={m} value={m}>
|
||||||
|
{getMethodLabel(m)}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
<option value="other">Other...</option>
|
||||||
|
</select>
|
||||||
|
{selectedMethod && selectedMethod !== 'other' && (
|
||||||
|
<span
|
||||||
|
className={`inline-block mt-1 text-[9px] px-1.5 py-0.5 font-medium rounded-full whitespace-nowrap ${METHOD_CONFIG[selectedMethod]?.color ?? ''}`}
|
||||||
|
>
|
||||||
|
{getMethodLabel(selectedMethod)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{selectedMethod === 'other' && (
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
value={customMethod}
|
||||||
|
onChange={(e) => setCustomMethod(e.target.value)}
|
||||||
|
placeholder="Custom method name"
|
||||||
|
className="w-full mt-2 px-3 py-2 border rounded-md dark:bg-gray-700 dark:border-gray-600"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium mb-1">Encounter Rate (%)</label>
|
<label className="block text-sm font-medium mb-1">Encounter Rate (%)</label>
|
||||||
|
|||||||
Reference in New Issue
Block a user