Public Access
Dashboard dialog shows unseen news entries. Users mark them seen with "Got it" or revisit via the /whats-new page. Admins publish, edit, and delete entries on /admin/news. Full i18n support and test coverage included.
58 lines
2.2 KiB
React
58 lines
2.2 KiB
React
import {useEffect, useState} from 'react';
|
|
import {useTranslation} from 'react-i18next';
|
|
import {Loader2} from 'lucide-react';
|
|
|
|
import {Card, CardContent, CardHeader, CardTitle} from '@/components/ui/card';
|
|
import NewsEntry from '../components/NewsEntry.jsx';
|
|
import PageContainer from '../components/PageContainer.jsx';
|
|
import {fetchNews, markNewsSeen} from '../utils/news.js';
|
|
|
|
/**
|
|
* Every news entry, newest first. Opening the page marks them seen.
|
|
*/
|
|
export default function WhatsNewPage() {
|
|
const {t} = useTranslation();
|
|
const [entries, setEntries] = useState([]);
|
|
const [status, setStatus] = useState('loading');
|
|
|
|
useEffect(() => {
|
|
(async () => {
|
|
try {
|
|
const response = await fetchNews();
|
|
if (response.status === 401) return;
|
|
if (!response.ok) {
|
|
setStatus('error');
|
|
return;
|
|
}
|
|
setEntries((await response.json()).results);
|
|
setStatus('ready');
|
|
// A failed mark is silent: the dashboard dialog shows the entries again.
|
|
markNewsSeen().catch(() => undefined);
|
|
} catch (err) {
|
|
console.error('News error:', err);
|
|
setStatus('error');
|
|
}
|
|
})();
|
|
}, []);
|
|
|
|
return (
|
|
<PageContainer className="space-y-4 px-4 py-6 md:px-8">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>{t('NEWS.TITLE')}</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
{status === 'loading' && <Loader2 className="size-5 animate-spin text-muted-foreground"/>}
|
|
{status === 'error' && <p className="text-sm text-destructive">{t('NEWS.LOAD_ERROR')}</p>}
|
|
{status === 'ready' && entries.length === 0 && <p className="text-sm text-muted-foreground">{t('NEWS.EMPTY')}</p>}
|
|
{status === 'ready' && entries.length > 0 && (
|
|
<ul className="divide-y">
|
|
{entries.map((entry) => <li key={entry.id} className="py-3"><NewsEntry entry={entry}/></li>)}
|
|
</ul>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
</PageContainer>
|
|
);
|
|
}
|