Files
HoruxDespachos/apps/web/app/(dashboard)/configuracion/seguridad/page.tsx
2026-04-27 22:09:36 -06:00

167 lines
6.0 KiB
TypeScript

'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { Header } from '@/components/layouts/header';
import { Card, CardContent, CardDescription, CardHeader, CardTitle, Button, Input, Label } from '@horux/shared-ui';
import { changePassword, logoutAll } from '@/lib/api/auth';
import { useAuthStore } from '@/stores/auth-store';
import { KeyRound, LogOut, Loader2, AlertCircle, CheckCircle2 } from 'lucide-react';
export default function SeguridadPage() {
const router = useRouter();
const { logout } = useAuthStore();
const [currentPassword, setCurrentPassword] = useState('');
const [newPassword, setNewPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [changing, setChanging] = useState(false);
const [changeError, setChangeError] = useState<string | null>(null);
const [changeOk, setChangeOk] = useState<string | null>(null);
const [loggingOut, setLoggingOut] = useState(false);
const handleChangePassword = async (e: React.FormEvent) => {
e.preventDefault();
setChangeError(null);
setChangeOk(null);
if (newPassword.length < 8) {
setChangeError('La nueva contraseña debe tener al menos 8 caracteres');
return;
}
if (newPassword !== confirmPassword) {
setChangeError('Las contraseñas no coinciden');
return;
}
if (currentPassword === newPassword) {
setChangeError('La nueva contraseña debe ser distinta a la actual');
return;
}
setChanging(true);
try {
const res = await changePassword(currentPassword, newPassword);
setChangeOk(res.message);
setCurrentPassword('');
setNewPassword('');
setConfirmPassword('');
setTimeout(() => {
logout();
router.push('/login');
}, 2500);
} catch (err: any) {
setChangeError(err?.response?.data?.message || 'Error al cambiar contraseña');
} finally {
setChanging(false);
}
};
const handleLogoutAll = async () => {
if (!confirm('Esto cerrará todas tus sesiones activas, incluyendo esta. Tendrás que iniciar sesión de nuevo. ¿Continuar?')) return;
setLoggingOut(true);
try {
await logoutAll();
logout();
router.push('/login');
} catch (err: any) {
alert(err?.response?.data?.message || 'Error al cerrar sesiones');
setLoggingOut(false);
}
};
return (
<>
<Header title="Seguridad" />
<main className="p-6 space-y-6 max-w-3xl">
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-base">
<KeyRound className="h-4 w-4" />
Cambiar contraseña
</CardTitle>
<CardDescription>
Al cambiar tu contraseña, todas tus sesiones (incluyendo esta) serán cerradas por seguridad.
</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleChangePassword} className="space-y-4">
<div className="space-y-1">
<Label htmlFor="current">Contraseña actual</Label>
<Input
id="current"
type="password"
value={currentPassword}
onChange={e => setCurrentPassword(e.target.value)}
autoComplete="current-password"
required
/>
</div>
<div className="space-y-1">
<Label htmlFor="new">Nueva contraseña</Label>
<Input
id="new"
type="password"
value={newPassword}
onChange={e => setNewPassword(e.target.value)}
autoComplete="new-password"
minLength={8}
required
/>
<p className="text-xs text-muted-foreground">Mínimo 8 caracteres.</p>
</div>
<div className="space-y-1">
<Label htmlFor="confirm">Confirmar nueva contraseña</Label>
<Input
id="confirm"
type="password"
value={confirmPassword}
onChange={e => setConfirmPassword(e.target.value)}
autoComplete="new-password"
required
/>
</div>
{changeError && (
<div className="flex items-start gap-2 text-sm text-red-700 bg-red-50 border border-red-200 rounded px-3 py-2">
<AlertCircle className="h-4 w-4 mt-0.5 flex-shrink-0" />
<span>{changeError}</span>
</div>
)}
{changeOk && (
<div className="flex items-start gap-2 text-sm text-green-700 bg-green-50 border border-green-200 rounded px-3 py-2">
<CheckCircle2 className="h-4 w-4 mt-0.5 flex-shrink-0" />
<span>{changeOk}</span>
</div>
)}
<Button type="submit" disabled={changing || !!changeOk}>
{changing && <Loader2 className="h-4 w-4 mr-2 animate-spin" />}
Actualizar contraseña
</Button>
</form>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-base">
<LogOut className="h-4 w-4" />
Cerrar todas las sesiones
</CardTitle>
<CardDescription>
Útil si perdiste un dispositivo o sospechas que alguien accedió a tu cuenta. Tendrás que iniciar sesión de nuevo en todos tus dispositivos, incluyendo este.
</CardDescription>
</CardHeader>
<CardContent>
<Button variant="outline" onClick={handleLogoutAll} disabled={loggingOut}>
{loggingOut && <Loader2 className="h-4 w-4 mr-2 animate-spin" />}
Cerrar todas mis sesiones
</Button>
</CardContent>
</Card>
</main>
</>
);
}