Files
prosapp-migration/admin/src/app/cities/page.tsx
T

115 lines
4.1 KiB
TypeScript

'use client';
import { useEffect, useState, useCallback } from 'react';
import { api } from '@/lib/api';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { toast } from 'sonner';
import { Plus } from 'lucide-react';
interface City { id: string; name: string; }
interface Region { id: string; name: string; cities: City[]; }
interface Country { id: string; name: string; regions: Region[]; }
export default function CitiesPage() {
const [countries, setCountries] = useState<Country[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [newCity, setNewCity] = useState('');
const [selectedRegionId, setSelectedRegionId] = useState('');
const load = useCallback(() => {
setLoading(true);
setError(null);
api.get<Country[]>('/locations/countries')
.then(setCountries)
.catch(() => setError('Error al cargar ciudades'))
.finally(() => setLoading(false));
}, []);
useEffect(() => { load(); }, [load]);
const addCity = async () => {
if (!selectedRegionId || !newCity.trim()) {
toast.error('Selecciona una región y escribe un nombre');
return;
}
try {
await api.post('/locations/cities', { region_id: selectedRegionId, name: newCity.trim() });
toast.success('Ciudad agregada');
setNewCity('');
load();
} catch (e: any) {
toast.error(e?.message || 'Error al agregar ciudad');
}
};
if (loading) {
return <div className="flex items-center justify-center py-16 text-muted-foreground">Cargando...</div>;
}
if (error) {
return (
<div className="flex flex-col items-center justify-center py-16 text-destructive">
<p>{error}</p>
<Button variant="outline" size="sm" onClick={load} className="mt-2">Reintentar</Button>
</div>
);
}
return (
<div className="space-y-4">
<h1 className="text-2xl font-bold">Ciudades</h1>
<Card>
<CardHeader><CardTitle>Agregar ciudad</CardTitle></CardHeader>
<CardContent className="flex gap-2">
<select
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
value={selectedRegionId}
onChange={(e) => setSelectedRegionId(e.target.value)}
>
<option value="">Seleccionar región...</option>
{countries.map((c) => (
<optgroup key={c.id} label={c.name}>
{c.regions.map((r) => (
<option key={r.id} value={r.id}>{r.name}</option>
))}
</optgroup>
))}
</select>
<Input
value={newCity}
onChange={(e) => setNewCity(e.target.value)}
placeholder="Nombre de la ciudad"
/>
<Button onClick={addCity}><Plus className="mr-1 h-4 w-4" />Agregar</Button>
</CardContent>
</Card>
{countries.map((c) => (
<Card key={c.id}>
<CardContent className="pt-4">
<h2 className="text-lg font-semibold mb-2">{c.name}</h2>
{c.regions?.map((r) => (
<details key={r.id} className="ml-4 mb-2">
<summary className="cursor-pointer text-sm font-medium text-muted-foreground hover:text-foreground">
{r.name} ({r.cities?.length || 0} ciudades)
</summary>
<div className="ml-4 mt-1 flex flex-wrap gap-1">
{r.cities?.map((city) => (
<span key={city.id} className="inline-block rounded bg-muted px-2 py-0.5 text-xs">
{city.name}
</span>
))}
</div>
</details>
))}
</CardContent>
</Card>
))}
</div>
);
}