Update: nueva version Horux Despachos
This commit is contained in:
166
apps/web/app/(dashboard)/configuracion/seguridad/page.tsx
Normal file
166
apps/web/app/(dashboard)/configuracion/seguridad/page.tsx
Normal file
@@ -0,0 +1,166 @@
|
||||
'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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user