feat: podpora per-user notifikací s Discord, ntfy a Teams (#39)
Uživatelé mohou v nastavení konfigurovat vlastní webhook URL/topic pro Discord, MS Teams a ntfy, a zvolit události k odběru. Notifikace jsou odesílány pouze uživatelům se stejnou zvolenou lokalitou.
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import { useRef } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { Modal, Button, Form } from "react-bootstrap"
|
||||
import { useSettings, ThemePreference } from "../../context/settings";
|
||||
import { NotificationSettings, UdalostEnum, getNotificationSettings, updateNotificationSettings } from "../../../../types";
|
||||
import { useAuth } from "../../context/auth";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean,
|
||||
@@ -10,12 +12,56 @@ type Props = {
|
||||
|
||||
/** Modální dialog pro uživatelská nastavení. */
|
||||
export default function SettingsModal({ isOpen, onClose, onSave }: Readonly<Props>) {
|
||||
const auth = useAuth();
|
||||
const settings = useSettings();
|
||||
const bankAccountRef = useRef<HTMLInputElement>(null);
|
||||
const nameRef = useRef<HTMLInputElement>(null);
|
||||
const hideSoupsRef = useRef<HTMLInputElement>(null);
|
||||
const themeRef = useRef<HTMLSelectElement>(null);
|
||||
|
||||
const ntfyTopicRef = useRef<HTMLInputElement>(null);
|
||||
const discordWebhookRef = useRef<HTMLInputElement>(null);
|
||||
const teamsWebhookRef = useRef<HTMLInputElement>(null);
|
||||
const [notifSettings, setNotifSettings] = useState<NotificationSettings>({});
|
||||
const [enabledEvents, setEnabledEvents] = useState<UdalostEnum[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen && auth?.login) {
|
||||
getNotificationSettings().then(response => {
|
||||
if (response.data) {
|
||||
setNotifSettings(response.data);
|
||||
setEnabledEvents(response.data.enabledEvents ?? []);
|
||||
}
|
||||
}).catch(() => {});
|
||||
}
|
||||
}, [isOpen, auth?.login]);
|
||||
|
||||
const toggleEvent = (event: UdalostEnum) => {
|
||||
setEnabledEvents(prev =>
|
||||
prev.includes(event) ? prev.filter(e => e !== event) : [...prev, event]
|
||||
);
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
// Uložení notifikačních nastavení na server
|
||||
await updateNotificationSettings({
|
||||
body: {
|
||||
ntfyTopic: ntfyTopicRef.current?.value || undefined,
|
||||
discordWebhookUrl: discordWebhookRef.current?.value || undefined,
|
||||
teamsWebhookUrl: teamsWebhookRef.current?.value || undefined,
|
||||
enabledEvents,
|
||||
}
|
||||
}).catch(() => {});
|
||||
|
||||
// Uložení ostatních nastavení (localStorage)
|
||||
onSave(
|
||||
bankAccountRef.current?.value,
|
||||
nameRef.current?.value,
|
||||
hideSoupsRef.current?.checked,
|
||||
themeRef.current?.value as ThemePreference,
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal show={isOpen} onHide={onClose} size="lg">
|
||||
<Modal.Header closeButton>
|
||||
@@ -51,6 +97,75 @@ export default function SettingsModal({ isOpen, onClose, onSave }: Readonly<Prop
|
||||
|
||||
<hr />
|
||||
|
||||
<h4>Notifikace</h4>
|
||||
<p>
|
||||
Nastavením notifikací budete dostávat upozornění o událostech (např. "Jdeme na oběd") přímo do vámi zvoleného komunikačního kanálu.
|
||||
</p>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>ntfy téma (topic)</Form.Label>
|
||||
<Form.Control
|
||||
ref={ntfyTopicRef}
|
||||
type="text"
|
||||
placeholder="moje-tema"
|
||||
defaultValue={notifSettings.ntfyTopic}
|
||||
key={notifSettings.ntfyTopic ?? 'ntfy-empty'}
|
||||
onKeyDown={e => e.stopPropagation()}
|
||||
/>
|
||||
<Form.Text className="text-muted">
|
||||
Téma pro ntfy push notifikace. Nechte prázdné pro vypnutí.
|
||||
</Form.Text>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Discord webhook URL</Form.Label>
|
||||
<Form.Control
|
||||
ref={discordWebhookRef}
|
||||
type="text"
|
||||
placeholder="https://discord.com/api/webhooks/..."
|
||||
defaultValue={notifSettings.discordWebhookUrl}
|
||||
key={notifSettings.discordWebhookUrl ?? 'discord-empty'}
|
||||
onKeyDown={e => e.stopPropagation()}
|
||||
/>
|
||||
<Form.Text className="text-muted">
|
||||
URL webhooku Discord kanálu. Nechte prázdné pro vypnutí.
|
||||
</Form.Text>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>MS Teams webhook URL</Form.Label>
|
||||
<Form.Control
|
||||
ref={teamsWebhookRef}
|
||||
type="text"
|
||||
placeholder="https://outlook.office.com/webhook/..."
|
||||
defaultValue={notifSettings.teamsWebhookUrl}
|
||||
key={notifSettings.teamsWebhookUrl ?? 'teams-empty'}
|
||||
onKeyDown={e => e.stopPropagation()}
|
||||
/>
|
||||
<Form.Text className="text-muted">
|
||||
URL webhooku MS Teams kanálu. Nechte prázdné pro vypnutí.
|
||||
</Form.Text>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Události k odběru</Form.Label>
|
||||
{Object.values(UdalostEnum).map(event => (
|
||||
<Form.Check
|
||||
key={event}
|
||||
id={`notif-event-${event}`}
|
||||
type="checkbox"
|
||||
label={event}
|
||||
checked={enabledEvents.includes(event)}
|
||||
onChange={() => toggleEvent(event)}
|
||||
/>
|
||||
))}
|
||||
<Form.Text className="text-muted">
|
||||
Zvolte události, o kterých chcete být notifikováni. Notifikace jsou odesílány pouze uživatelům se stejnou zvolenou lokalitou.
|
||||
</Form.Text>
|
||||
</Form.Group>
|
||||
|
||||
<hr />
|
||||
|
||||
<h4>Bankovní účet</h4>
|
||||
<p>
|
||||
Nastavením čísla účtu umožníte automatické generování QR kódů pro úhradu za vámi provedené objednávky v rámci Pizza day.
|
||||
@@ -88,7 +203,7 @@ export default function SettingsModal({ isOpen, onClose, onSave }: Readonly<Prop
|
||||
<Button variant="secondary" onClick={onClose}>
|
||||
Storno
|
||||
</Button>
|
||||
<Button onClick={() => onSave(bankAccountRef.current?.value, nameRef.current?.value, hideSoupsRef.current?.checked, themeRef.current?.value as ThemePreference)}>
|
||||
<Button onClick={handleSave}>
|
||||
Uložit
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
|
||||
Reference in New Issue
Block a user