Public Access
feat(broadcasts): add admin broadcast page and user opt-out switch
Admin page /admin/broadcasts to write, test and send broadcasts in both languages; confirms the recipient count before send. History shows the counts. A new switch in settings lets users opt out of platform mails; account mails always come. Subject and body are plain text only.
This commit is contained in:
1 parent
0154202ddc
commit
fa0857f55d
13 files changed
+642
-4
No files matched your search
@@ -1,7 +1,7 @@
|
||||
import {useEffect, useState} from 'react';
|
||||
import {Link, NavLink, useLocation, useNavigate} from 'react-router-dom';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {ChartColumn, Coins, Dices, Eye, Languages, LayoutDashboard, Library, LogOut, Megaphone, Moon, Newspaper, Settings, Shield, Sun} from 'lucide-react';
|
||||
import {ChartColumn, Coins, Dices, Eye, Languages, LayoutDashboard, Library, LogOut, Mail, Megaphone, Moon, Newspaper, Settings, Shield, Sun} from 'lucide-react';
|
||||
import {toast} from 'sonner';
|
||||
|
||||
import {Badge} from '@/components/ui/badge';
|
||||
@@ -169,6 +169,9 @@ export default function AppHeader({isAuthenticated, onLogout}) {
|
||||
<DropdownMenuItem asChild>
|
||||
<Link to="/admin/news"><Newspaper/>{t('NAV.ADMIN_NEWS')}</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link to="/admin/broadcasts"><Mail/>{t('NAV.ADMIN_BROADCASTS')}</Link>
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
)}
|
||||
{isAdmin && (
|
||||
|
||||
@@ -55,6 +55,7 @@ describe('AppHeader', () => {
|
||||
|
||||
expect(await screen.findByText('NAV.ADMIN')).toBeTruthy();
|
||||
expect(screen.getByText('NAV.ADMIN_NEWS')).toBeTruthy();
|
||||
expect(screen.getByText('NAV.ADMIN_BROADCASTS')).toBeTruthy();
|
||||
expect(screen.getByText('NAV.VIEW_AS.MENU')).toBeTruthy();
|
||||
expect(screen.queryByText('NAV.VIEW_AS.BADGE')).toBeNull();
|
||||
expect(screen.queryByLabelText('NAV.PENDING_REQUESTS:0')).toBeNull();
|
||||
@@ -84,6 +85,7 @@ describe('AppHeader', () => {
|
||||
openMenu();
|
||||
expect(await screen.findByText('NAV.VIEW_AS.MENU')).toBeTruthy();
|
||||
expect(screen.queryByText('NAV.ADMIN')).toBeNull();
|
||||
expect(screen.queryByText('NAV.ADMIN_BROADCASTS')).toBeNull();
|
||||
});
|
||||
|
||||
it('offers nothing of that to a user', async () => {
|
||||
@@ -96,6 +98,7 @@ describe('AppHeader', () => {
|
||||
expect(screen.getByText('NAV.WHATS_NEW')).toBeTruthy();
|
||||
expect(screen.queryByText('NAV.ADMIN')).toBeNull();
|
||||
expect(screen.queryByText('NAV.ADMIN_NEWS')).toBeNull();
|
||||
expect(screen.queryByText('NAV.ADMIN_BROADCASTS')).toBeNull();
|
||||
expect(screen.queryByText('NAV.VIEW_AS.MENU')).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -11,7 +11,7 @@ import {usePreferences} from '../context/PreferencesContext.jsx';
|
||||
import usePersistSettings from '../hooks/usePersistSettings.js';
|
||||
import useSaveResponse from '../hooks/useSaveResponse.js';
|
||||
import {FAVORITE_PLATFORMS, saveFavoritePlatform} from '../utils/platforms.js';
|
||||
import {LANGUAGES, saveSettings} from '../utils/settings.js';
|
||||
import {LANGUAGES, savePlatformMails, saveSettings} from '../utils/settings.js';
|
||||
|
||||
// Radix Select has no empty value.
|
||||
const NO_FAVORITE = 'NONE';
|
||||
@@ -41,6 +41,13 @@ export default function PreferencesCard() {
|
||||
}
|
||||
};
|
||||
|
||||
const handlePlatformMailsChange = async (next) => {
|
||||
if (await runSave(() => savePlatformMails(next))) {
|
||||
updateProfile((p) => ({...p, settings: {...p.settings, platformMails: next}}));
|
||||
toast.success(t('SETTINGS.SAVED'));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
@@ -83,6 +90,14 @@ export default function PreferencesCard() {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="platformMails">{t('SETTINGS.PLATFORM_MAILS')}</Label>
|
||||
<p className="text-xs text-muted-foreground">{t('SETTINGS.PLATFORM_MAILS_HINT')}</p>
|
||||
</div>
|
||||
<Switch id="platformMails" checked={profile?.settings?.platformMails ?? true}
|
||||
onCheckedChange={handlePlatformMailsChange}/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user