feat: implement comprehensive notification engine with policies, preferences, and automated dispatching services
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import Location from "@components/Location/Location";
|
||||
import { useLocationStore } from "@store/Location.Store";
|
||||
import { useNotificationPreferencesStore } from "@store/NotificationPreferences.Store";
|
||||
import { useSessionStore } from "@store/Sesion.Store";
|
||||
import { useEventHandlerStore, EVENT_TYPES } from "@store/EventHandler.Store";
|
||||
import { useAlert } from "@store/Alert.Store";
|
||||
import { getClientPreferences, upsertClientPreferences } from "@services/Notifications.Service";
|
||||
import { NotificationChannel } from "@models/NotificationPreferences.model";
|
||||
import AccountCircleOutlinedIcon from "@mui/icons-material/AccountCircleOutlined";
|
||||
import NotificationsIcon from "@mui/icons-material/NotificationsOutlined";
|
||||
import MaterialAlert from "@mui/material/Alert";
|
||||
import styles from "./NotificationsPreferencesPage.module.css";
|
||||
|
||||
const CHANNELS: { key: NotificationChannel; label: string }[] = [
|
||||
{ key: "whatsapp", label: "WhatsApp" },
|
||||
{ key: "email", label: "Email" },
|
||||
{ key: "system", label: "Sistema" }
|
||||
];
|
||||
|
||||
type ChannelPreference = "default" | "preferred" | "muted";
|
||||
|
||||
export default function NotificationsPreferencesPage() {
|
||||
const location = useLocationStore();
|
||||
const store = useNotificationPreferencesStore();
|
||||
const SessionInfo = useSessionStore();
|
||||
const eventHandler = useEventHandlerStore();
|
||||
const alert = useAlert();
|
||||
|
||||
useEffect(() => {
|
||||
location.clear();
|
||||
|
||||
location.add({
|
||||
url: "/user/profile",
|
||||
title: "Perfil de usuario",
|
||||
icon: <AccountCircleOutlinedIcon sx={{ fontSize: "32px", mr: "10px" }} />
|
||||
});
|
||||
|
||||
location.add({
|
||||
url: "",
|
||||
title: "Preferencias de notificación",
|
||||
icon: <NotificationsIcon sx={{ fontSize: "32px", mr: "10px" }} />
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!SessionInfo.userId) {
|
||||
return;
|
||||
}
|
||||
|
||||
store.setClientId(SessionInfo.userId);
|
||||
store.setMode("client-preferences");
|
||||
|
||||
eventHandler.setEventType(EVENT_TYPES.LOADING);
|
||||
getClientPreferences(SessionInfo.userId, SessionInfo.userId)
|
||||
.then((preferences) => {
|
||||
store.setClientPreferences(preferences);
|
||||
store.updateFormData({
|
||||
preferredChannels: preferences.preferredChannels || [],
|
||||
mutedChannels: preferences.mutedChannels || []
|
||||
});
|
||||
store.setDirty(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.showError(error.format());
|
||||
})
|
||||
.finally(() => {
|
||||
eventHandler.setEventType(EVENT_TYPES.SLEEP);
|
||||
});
|
||||
}, [SessionInfo.userId]);
|
||||
|
||||
const getChannelPreference = (channel: NotificationChannel): ChannelPreference => {
|
||||
if (store.formData.mutedChannels.includes(channel)) {
|
||||
return "muted";
|
||||
}
|
||||
|
||||
if (store.formData.preferredChannels.includes(channel)) {
|
||||
return "preferred";
|
||||
}
|
||||
|
||||
return "default";
|
||||
};
|
||||
|
||||
const selectPreference = (channel: NotificationChannel, preference: ChannelPreference) => {
|
||||
store.updateFormData({
|
||||
preferredChannels:
|
||||
preference === "preferred"
|
||||
? [...store.formData.preferredChannels.filter((c) => c !== channel), channel]
|
||||
: store.formData.preferredChannels.filter((c) => c !== channel),
|
||||
mutedChannels:
|
||||
preference === "muted"
|
||||
? [...store.formData.mutedChannels.filter((c) => c !== channel), channel]
|
||||
: store.formData.mutedChannels.filter((c) => c !== channel)
|
||||
});
|
||||
eventHandler.setEventType(EVENT_TYPES.NEED_SAVE);
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
eventHandler.setEventType(EVENT_TYPES.LOADING);
|
||||
upsertClientPreferences(SessionInfo.userId, SessionInfo.userId, {
|
||||
preferredChannels: store.formData.preferredChannels as NotificationChannel[],
|
||||
mutedChannels: store.formData.mutedChannels as NotificationChannel[]
|
||||
})
|
||||
.then((preferences) => {
|
||||
store.setClientPreferences(preferences);
|
||||
store.setDirty(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.showError(error.format());
|
||||
})
|
||||
.finally(() => {
|
||||
eventHandler.setEventType(EVENT_TYPES.SLEEP);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (eventHandler.eventType === EVENT_TYPES.SAVE) {
|
||||
handleSave();
|
||||
}
|
||||
}, [eventHandler.eventType]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Location />
|
||||
|
||||
<div className={styles.container}>
|
||||
<MaterialAlert severity="info" sx={{ marginBottom: "0" }}>
|
||||
<b>Preferencias globales:</b> Configure cómo prefiere recibir notificaciones. Si deja un canal en estándar, cada organización podrá usar su configuración general.
|
||||
</MaterialAlert>
|
||||
|
||||
<div className={styles.channels}>
|
||||
{CHANNELS.map((channel) => {
|
||||
const selectedPreference = getChannelPreference(channel.key);
|
||||
return (
|
||||
<div key={channel.key} className={styles.card}>
|
||||
<div>
|
||||
<div className={styles.channelTitle}>{channel.label}</div>
|
||||
<div className={styles.channelDescription}>
|
||||
{selectedPreference === "preferred" && "Priorizar este canal para tus notificaciones."}
|
||||
{selectedPreference === "muted" && "No recibir notificaciones por este canal."}
|
||||
{selectedPreference === "default" && "Usar la configuración estándar de cada organización."}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.options}>
|
||||
{[
|
||||
{ key: "default" as ChannelPreference, label: "Estándar" },
|
||||
{ key: "preferred" as ChannelPreference, label: "Preferido" },
|
||||
{ key: "muted" as ChannelPreference, label: "Silenciado" }
|
||||
].map((option) => {
|
||||
const isSelected = selectedPreference === option.key;
|
||||
return (
|
||||
<button
|
||||
key={option.key}
|
||||
type="button"
|
||||
onClick={() => selectPreference(channel.key, option.key)}
|
||||
className={`${styles.optionButton} ${isSelected ? styles.optionButtonActive : ""}`}
|
||||
>
|
||||
{option.label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{store.isDirty && (
|
||||
<button onClick={handleSave} className={styles.saveButton}>
|
||||
Guardar preferencias
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: "40px" }}> </div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user