feat: add daily digest email notification module
Build and Push Multi-Platform Images / build-and-push (push) Successful in 50s
Build and Push Multi-Platform Images / build-and-push (push) Successful in 50s
- New DailyDigestModule with scheduled summary email for open dashboard items - Extract StatsService from StatsController for reuse in digest - Add DailyDigestEnabled, UserEmail, UserPreferredUsername to UserSettings entity - Sync email/username from OIDC token on each get/update call - Add dailyDigestEnabled to UserSettingsDto and update API - Notifications tab in UserSettingsPage with enable toggle and "Jetzt senden" button Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ export interface UserSettingsData {
|
||||
mailSignatureHtml: string | null;
|
||||
defaultLabelTemplateId: number | null;
|
||||
emailRecipientHistory: string[] | null;
|
||||
dailyDigestEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface SenderOption {
|
||||
@@ -31,4 +32,7 @@ export const userSettingsApi = {
|
||||
|
||||
getSenders: () =>
|
||||
api.get<SenderOption[]>('/api/user-settings/senders').then((r) => r.data),
|
||||
|
||||
sendDigestNow: () =>
|
||||
api.post<{ ok: boolean; error?: string }>('/api/daily-digest/send-now').then((r) => r.data),
|
||||
};
|
||||
|
||||
@@ -197,6 +197,71 @@ function MailSettingsTab() {
|
||||
);
|
||||
}
|
||||
|
||||
function NotificationsTab() {
|
||||
const [enabled, setEnabled] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [sending, setSending] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
userSettingsApi.get()
|
||||
.then((data) => setEnabled(data.dailyDigestEnabled ?? false))
|
||||
.catch(() => message.error('Einstellungen konnten nicht geladen werden'))
|
||||
.finally(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
const handleSave = async () => {
|
||||
setSaving(true);
|
||||
try {
|
||||
await userSettingsApi.update({ dailyDigestEnabled: enabled });
|
||||
message.success('Einstellungen gespeichert');
|
||||
} catch {
|
||||
message.error('Speichern fehlgeschlagen');
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSendNow = async () => {
|
||||
setSending(true);
|
||||
try {
|
||||
const result = await userSettingsApi.sendDigestNow();
|
||||
if (result.ok) {
|
||||
message.success('Tagesübersicht wurde gesendet');
|
||||
} else {
|
||||
message.error(result.error ?? 'Senden fehlgeschlagen');
|
||||
}
|
||||
} catch {
|
||||
message.error('Senden fehlgeschlagen');
|
||||
} finally {
|
||||
setSending(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) return null;
|
||||
|
||||
return (
|
||||
<Form layout="vertical" style={{ maxWidth: 600 }}>
|
||||
<Form.Item
|
||||
label="Tägliche E-Mail-Zusammenfassung"
|
||||
extra="Sie erhalten jeden Morgen eine E-Mail mit der Übersicht aller offenen Vorgänge aus dem Dashboard."
|
||||
>
|
||||
<Switch checked={enabled} onChange={setEnabled} />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Space>
|
||||
<Button type="primary" loading={saving} onClick={handleSave}>
|
||||
Speichern
|
||||
</Button>
|
||||
<Button loading={sending} onClick={handleSendNow}>
|
||||
Jetzt senden
|
||||
</Button>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
export default function UserSettingsPage() {
|
||||
return (
|
||||
<div>
|
||||
@@ -214,6 +279,11 @@ export default function UserSettingsPage() {
|
||||
label: 'Etikettendruck',
|
||||
children: <LabelSettingsTab />,
|
||||
},
|
||||
{
|
||||
key: 'notifications',
|
||||
label: 'Benachrichtigungen',
|
||||
children: <NotificationsTab />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user