fix(wizard): cache geo singleton, feedback carga, debounce draft

geo_paraguay_full.json se cargaba dos veces sin cache compartido.
Módulo lib/geo-cache.ts singleton: un fetch para toda la sesión.
Feedback visual mientras carga (disabled + placeholder) y si falla
(mensaje + texto libre). debounce 500ms en saveDraft para no escribir
en localStorage en cada keystroke.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
markosbenitez
2026-06-28 22:04:12 -03:00
parent 5472043a33
commit 704d2407e8
4 changed files with 42 additions and 13 deletions

View File

@@ -1,7 +1,6 @@
'use client'
import { useState, useEffect, useRef, type CSSProperties, type ReactNode } from 'react'
type GeoData = Record<string, Record<string, string[]>>
import { getGeoData, type GeoData } from '@/lib/geo-cache'
interface CiudadSelectFieldProps {
name: string
@@ -23,6 +22,7 @@ export function CiudadSelectField({
error,
}: CiudadSelectFieldProps) {
const [geoData, setGeoData] = useState<GeoData | null>(null)
const [geoStatus, setGeoStatus] = useState<'loading' | 'ready' | 'error'>('loading')
const [open, setOpen] = useState(false)
const [activeIdx, setActiveIdx] = useState(-1)
const [dropdownStyle, setDropdownStyle] = useState<CSSProperties>({})
@@ -35,10 +35,9 @@ export function CiudadSelectField({
const prevDeptoRef = useRef(departamento)
useEffect(() => {
fetch('/data/geo_paraguay_full.json')
.then((r) => r.json())
.then(setGeoData)
.catch(() => {})
getGeoData()
.then((data) => { setGeoData(data); setGeoStatus('ready') })
.catch(() => setGeoStatus('error'))
}, [])
// Clear city when departamento changes so no orphan value remains
@@ -130,6 +129,12 @@ export function CiudadSelectField({
autoComplete="off"
className="text-short-input"
ref={inputRef}
disabled={geoStatus === 'loading'}
placeholder={
geoStatus === 'loading' ? 'Cargando opciones...' :
geoStatus === 'error' ? 'No se pudieron cargar las opciones. Podés escribir el nombre manualmente.' :
undefined
}
onChange={(e) => handleInput(e.target.value)}
onKeyDown={handleKeyDown}
onFocus={() => { if (suggestions.length > 0) { computeDropdownStyle(); setOpen(true) } }}