feat: add What's new feature
Build, Test & Push Frontend / quality-check (push) Successful in 25s
Build, Test & Push Frontend / docker (push) Successful in 39s

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.
This commit is contained in:
lobo committed 2026-10-01 10:03:39 +02:00
1 parent 79b086c525
commit b811255c16
17 files changed
+690 -1

No files matched your search

+3
View File
@@ -30,3 +30,6 @@ export const MAX_CORRECTION_GENRES = 10;
export const MAX_DISPLAY_NAME_LENGTH = 24;
// Auth CreateHintRequest @Size(max = 500)
export const MAX_HINT_LENGTH = 500;
// Auth NewsRequest @Size(max = 100) and @Size(max = 2000)
export const MAX_NEWS_TITLE_LENGTH = 100;
export const MAX_NEWS_TEXT_LENGTH = 2000;
+39
View File
@@ -0,0 +1,39 @@
import {apiFetch} from './api.js';
const AUTH = () => import.meta.env.VITE_API_URL;
const JSON_HEADERS = {'Content-Type': 'application/json'};
/**
* GET /api/news: {results: [{id, titleEn, titleDe, textEn, textDe, publishedAt, seen}]}, every entry, newest first.
*/
export const fetchNews = () => apiFetch(`${AUTH()}/api/news`);
/**
* All entries published until now count as seen for this user.
*/
export const markNewsSeen = () => apiFetch(`${AUTH()}/api/news/seen`, {method: 'PUT'});
/**
* PLATFORM_ADMIN only (403 otherwise): {results: [...]}, every entry with createdBy and createdAt, newest first.
*/
export const fetchAdminNews = () => apiFetch(`${AUTH()}/api/admin/news`);
/**
* 201 {id}, or 400 for a blank English title or text, or a title over 100 or a text over 2000 characters.
*/
export const createNews = (news) =>
apiFetch(`${AUTH()}/api/admin/news`, {method: 'POST', headers: JSON_HEADERS, body: JSON.stringify(news)});
/**
* 204, 404 for an unknown id. The published date stays.
*/
export const updateNews = (newsId, news) =>
apiFetch(`${AUTH()}/api/admin/news/${newsId}`, {method: 'PUT', headers: JSON_HEADERS, body: JSON.stringify(news)});
export const deleteNews = (newsId) => apiFetch(`${AUTH()}/api/admin/news/${newsId}`, {method: 'DELETE'});
const pick = (de, en, language) => (language?.startsWith('de') && de ? de : en);
export const newsTitle = (entry, language) => pick(entry.titleDe, entry.titleEn, language);
export const newsText = (entry, language) => pick(entry.textDe, entry.textEn, language);