Sistema de Notificaciones y Baja One-Click

This commit is contained in:
2026-03-12 13:52:33 -03:00
parent f1a9bb9099
commit 96fca4d9c7
21 changed files with 1384 additions and 79 deletions

View File

@@ -2,6 +2,10 @@ import { useState, useEffect } from "react";
import { ProfileService } from "../services/profile.service";
import { useAuth } from "../context/AuthContext";
import { AuthService } from "../services/auth.service";
import {
NotificationPreferencesService,
type NotificationPreferences,
} from "../services/notification-preferences.service";
export default function PerfilPage() {
const { user, refreshSession } = useAuth();
@@ -19,10 +23,46 @@ export default function PerfilPage() {
phoneNumber: "",
});
// Estado de preferencias de notificación
const [notifPrefs, setNotifPrefs] = useState<NotificationPreferences>({
sistema: true,
marketing: true,
rendimiento: true,
mensajes: true,
});
const [savingPrefs, setSavingPrefs] = useState(false);
const [prefsSaved, setPrefsSaved] = useState(false);
const [loadingPrefs, setLoadingPrefs] = useState(true);
useEffect(() => {
loadProfile();
loadNotifPrefs();
}, []);
const loadNotifPrefs = async () => {
try {
const data = await NotificationPreferencesService.getPreferences();
setNotifPrefs(data);
} catch (err) {
console.error("Error cargando preferencias de notificación", err);
} finally {
setLoadingPrefs(false);
}
};
const handleSaveNotifPrefs = async () => {
setSavingPrefs(true);
try {
await NotificationPreferencesService.updatePreferences(notifPrefs);
setPrefsSaved(true);
setTimeout(() => setPrefsSaved(false), 3000);
} catch (err) {
alert("Error al guardar las preferencias de notificación.");
} finally {
setSavingPrefs(false);
}
};
const loadProfile = async () => {
try {
const data = await ProfileService.getProfile();
@@ -97,7 +137,7 @@ export default function PerfilPage() {
</div>
</div>
{/* Edit Form */}
{/* Formulario de datos personales */}
<div className="lg:col-span-2">
<form
onSubmit={handleSubmit}
@@ -189,6 +229,118 @@ export default function PerfilPage() {
</button>
</div>
</form>
{/* ─── Preferencias de Notificación ─── */}
<div className="mt-8 glass p-8 rounded-[2.5rem] border border-white/5">
<div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 bg-blue-600/15 rounded-2xl flex items-center justify-center text-xl border border-blue-500/20">
🔔
</div>
<div>
<h3 className="text-sm font-black uppercase tracking-widest text-white">
Preferencias de Notificación
</h3>
<p className="text-[10px] text-gray-500 font-medium mt-0.5">
Elegir qué correos querés recibir
</p>
</div>
</div>
{loadingPrefs ? (
<div className="flex items-center gap-3 py-4 text-gray-500 text-xs">
<div className="w-5 h-5 border-2 border-blue-500/40 border-t-blue-500 rounded-full animate-spin" />
Cargando preferencias...
</div>
) : (
<div className="space-y-4">
{([
{
key: "sistema" as const,
label: "Avisos del Sistema",
desc: "Vencimiento de avisos, renovaciones y alertas importantes.",
icon: "⚙️",
},
{
key: "marketing" as const,
label: "Promociones y Marketing",
desc: "Ofertas especiales, recordatorio de carrito abandonado.",
icon: "🎁",
},
{
key: "rendimiento" as const,
label: "Resumen Semanal",
desc: "Visitas y favoritos de tus avisos publicados.",
icon: "📊",
},
{
key: "mensajes" as const,
label: "Recordatorio de Mensajes",
desc: "Aviso cuando tenés mensajes sin leer por más de 4 horas.",
icon: "💬",
},
] as const).map(({ key, label, desc, icon }) => (
<label
key={key}
className="flex items-center justify-between gap-4 p-4 rounded-2xl bg-white/3
border border-white/5 hover:bg-white/5 transition-all cursor-pointer group"
>
<div className="flex items-center gap-3">
<span className="text-xl">{icon}</span>
<div>
<p className="text-xs font-black text-white uppercase tracking-wider">
{label}
</p>
<p className="text-[10px] text-gray-500 font-medium mt-0.5">{desc}</p>
</div>
</div>
{/* Toggle switch */}
<div className="relative flex-shrink-0">
<input
type="checkbox"
className="sr-only"
checked={notifPrefs[key]}
onChange={(e) =>
setNotifPrefs((prev) => ({ ...prev, [key]: e.target.checked }))
}
/>
<div
className={`w-12 h-6 rounded-full transition-all duration-300 cursor-pointer
${notifPrefs[key]
? 'bg-blue-600 shadow-lg shadow-blue-600/30'
: 'bg-white/10'}`}
>
<div
className={`absolute top-1 w-4 h-4 bg-white rounded-full shadow transition-all duration-300
${notifPrefs[key] ? 'left-7' : 'left-1'}`}
/>
</div>
</div>
</label>
))}
<div className="pt-4 border-t border-white/5 flex items-center gap-4">
<button
type="button"
onClick={handleSaveNotifPrefs}
disabled={savingPrefs}
className="bg-blue-600 hover:bg-blue-500 text-white py-3.5 px-10 rounded-2xl
text-[10px] font-black uppercase tracking-widest transition-all
shadow-lg shadow-blue-600/20 active:scale-95 disabled:opacity-50"
>
{savingPrefs ? "Guardando..." : "Guardar Preferencias"}
</button>
{prefsSaved && (
<span className="text-green-400 text-[10px] font-black uppercase tracking-widest
animate-pulse">
Preferencias guardadas
</span>
)}
</div>
</div>
)}
</div>
</div>
</div>
{showEmailModal && (