Skip to content

Commit b465395

Browse files
agatha197claude
andcommitted
feat(FR-3224): add system announcement management to the Maintenance page
Superadmins can edit an existing announcement from the start-page alert (FR-3223), but there was no WebUI entry point to create the first announcement when none exists. Add a "System announcement" row to the admin Maintenance page (superadmin-only) that opens the existing AnnouncementEditModal, so a superadmin can create, edit, enable, or disable the announcement regardless of whether one already exists. - MaintenanceSettingList: new Announcement settings group (placed last) with a button that opens AnnouncementEditModal (wrapped in BAIUnmountAfterClose), prefilled from the current get_announcement query. - i18n: add maintenance.Announcement / SystemAnnouncement / DescSystemAnnouncement across all locales. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 39f74fd commit b465395

22 files changed

Lines changed: 130 additions & 23 deletions

react/src/components/MaintenanceSettingList.tsx

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,33 @@
33
Copyright (c) 2015-2026 Lablup Inc. All rights reserved.
44
*/
55
import { useSuspendedBackendaiClient } from '../hooks';
6+
import { useSuspenseTanQuery } from '../hooks/reactQueryAlias';
67
import { useSetBAINotification } from '../hooks/useBAINotification';
8+
import AnnouncementEditModal from './AnnouncementEditModal';
79
import SettingList, { SettingGroup } from './SettingList';
8-
import { RedoOutlined } from '@ant-design/icons';
10+
import { NotificationOutlined, RedoOutlined } from '@ant-design/icons';
911
import { Button } from 'antd';
12+
import { BAIUnmountAfterClose } from 'backend.ai-ui';
1013
import { useState } from 'react';
1114
import { useTranslation } from 'react-i18next';
1215

1316
const MaintenanceSettingList = () => {
17+
'use memo';
1418
const [isRecalculating, setIsRecalculating] = useState(false);
1519
const [isRescanning, setIsRescanning] = useState(false);
20+
const [isAnnouncementModalOpen, setIsAnnouncementModalOpen] = useState(false);
1621

1722
const { t } = useTranslation();
1823
const { upsertNotification } = useSetBAINotification();
1924
const baiClient = useSuspendedBackendaiClient();
2025

26+
const { data: announcement } = useSuspenseTanQuery({
27+
queryKey: ['baiClient', 'service', 'get_announcement'],
28+
queryFn: () => {
29+
return baiClient.service.get_announcement();
30+
},
31+
});
32+
2133
const recalculateUsage = () => {
2234
setIsRecalculating(true);
2335

@@ -130,9 +142,41 @@ const MaintenanceSettingList = () => {
130142
},
131143
],
132144
},
145+
{
146+
'data-testid': 'maintenance-announcement',
147+
title: t('maintenance.Announcement'),
148+
settingItems: [
149+
{
150+
type: 'custom',
151+
title: t('maintenance.SystemAnnouncement'),
152+
description: t('maintenance.DescSystemAnnouncement'),
153+
children: (
154+
<Button
155+
icon={<NotificationOutlined />}
156+
onClick={() => setIsAnnouncementModalOpen(true)}
157+
>
158+
{t('summary.EditAnnouncement')}
159+
</Button>
160+
),
161+
showResetButton: false,
162+
},
163+
],
164+
},
133165
];
134166

135-
return <SettingList settingGroups={settingGroupList} showSearchBar />;
167+
return (
168+
<>
169+
<SettingList settingGroups={settingGroupList} showSearchBar />
170+
<BAIUnmountAfterClose>
171+
<AnnouncementEditModal
172+
open={isAnnouncementModalOpen}
173+
initialMessage={announcement?.message ?? ''}
174+
initialEnabled={announcement?.enabled ?? true}
175+
onRequestClose={() => setIsAnnouncementModalOpen(false)}
176+
/>
177+
</BAIUnmountAfterClose>
178+
</>
179+
);
136180
};
137181

138182
export default MaintenanceSettingList;

resources/i18n/de.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1916,11 +1916,13 @@
19161916
"UpTo3000Logs": "(Bis zu 3000 aktuelle Protokolle)"
19171917
},
19181918
"maintenance": {
1919+
"Announcement": "Ankündigung",
19191920
"CleanupImages": "Bilder bereinigen",
19201921
"CleanupOldImages": "Alte Bilder bereinigen",
19211922
"DescCleanupOldImages": "Bereinigen Sie alte Images aus der Docker-Image-Liste.",
19221923
"DescMatchDatabase": "Berechnen Sie die Nutzungsdatenbank mit dem aktuellen Status neu. Nützlich, wenn Docker versehentlich fehlschlägt oder einige Sitzungen zwangsweise beendet wurden.",
19231924
"DescRescanImageList": "Bildliste aus registrierten Repositorys erneut scannen. Es kann lange dauern, also warten Sie bitte nach der Ausführung.",
1925+
"DescSystemAnnouncement": "Erstellen oder bearbeiten Sie das Ankündigungsbanner, das allen Benutzern oben auf der Seite angezeigt wird.",
19241926
"Fix": "Fix",
19251927
"ImagesEnvironment": "Bilder / Umgebung",
19261928
"MatchDatabase": "Nutzungsdatenbank mit aktuellem Status abgleichen",
@@ -1933,7 +1935,8 @@
19331935
"RescanImageList": "Bilderliste aus Repository erneut scannen",
19341936
"RescanImageScanning": "Erneut scannen...",
19351937
"RescanImages": "Bilder erneut scannen",
1936-
"Scanning": "Scannen..."
1938+
"Scanning": "Scannen...",
1939+
"SystemAnnouncement": "Systemankündigung"
19371940
},
19381941
"maxLength": {
19391942
"200chars": "(maximal 200 Zeichen)",

resources/i18n/el.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1914,11 +1914,13 @@
19141914
"UpTo3000Logs": "(Έως 3000 πρόσφατα αρχεία καταγραφής)"
19151915
},
19161916
"maintenance": {
1917+
"Announcement": "Ανακοίνωση",
19171918
"CleanupImages": "Εικόνες καθαρισμού",
19181919
"CleanupOldImages": "Εκκαθάριση παλιών εικόνων",
19191920
"DescCleanupOldImages": "Καθαρίστε παλιές εικόνες από τη λίστα εικόνων του docker.",
19201921
"DescMatchDatabase": "Επαναλάβετε τον υπολογισμό της βάσης δεδομένων χρήσης με την τρέχουσα κατάσταση. Χρήσιμο σε περίπτωση αποτυχίας του λιμενεργάτη, ή ορισμένων συνεδριών τερματίστηκαν βίαια.",
19211922
"DescRescanImageList": "Σάρωση λίστας εικόνων από καταχωρημένα αποθετήρια. Μπορεί να χρειαστεί πολύς χρόνος, οπότε περιμένετε μετά την εκτέλεση.",
1923+
"DescSystemAnnouncement": "Δημιουργήστε ή επεξεργαστείτε το banner ανακοίνωσης που εμφανίζεται σε όλους τους χρήστες στην κορυφή της σελίδας.",
19221924
"Fix": "Διορθώσετε",
19231925
"ImagesEnvironment": "Εικόνες / Περιβάλλον",
19241926
"MatchDatabase": "Ταιριάξτε τη βάση δεδομένων χρήσης με την τρέχουσα κατάσταση",
@@ -1931,7 +1933,8 @@
19311933
"RescanImageList": "Επαναλάβετε τη σάρωση της λίστας εικόνων από το αποθετήριο",
19321934
"RescanImageScanning": "Επανασύνδεση ...",
19331935
"RescanImages": "Σάρωση εικόνων",
1934-
"Scanning": "Ερευνα..."
1936+
"Scanning": "Ερευνα...",
1937+
"SystemAnnouncement": "Ανακοίνωση συστήματος"
19351938
},
19361939
"maxLength": {
19371940
"200chars": "(έως 200 χαρακτήρες)",

resources/i18n/en.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1901,11 +1901,13 @@
19011901
"UpTo3000Logs": "(Up to 3000 recent logs)"
19021902
},
19031903
"maintenance": {
1904+
"Announcement": "Announcement",
19041905
"CleanupImages": "Cleanup Images",
19051906
"CleanupOldImages": "Cleanup old images",
19061907
"DescCleanupOldImages": "Clean up old images from docker image list.",
19071908
"DescMatchDatabase": "Recalculate usage database with current state. Useful if docker fails by accident, or some sessions have been forcibly terminated.",
19081909
"DescRescanImageList": "Rescan image list from registered repositories. It may take a long time, so please wait after execution.",
1910+
"DescSystemAnnouncement": "Create or edit the announcement banner shown to all users at the top of the page.",
19091911
"Fix": "Fix",
19101912
"ImagesEnvironment": "Images / Environment",
19111913
"MatchDatabase": "Match usage database with current state",
@@ -1918,7 +1920,8 @@
19181920
"RescanImageList": "Rescan image list from repository",
19191921
"RescanImageScanning": "Rescanning...",
19201922
"RescanImages": "Rescan Images",
1921-
"Scanning": "Scanning..."
1923+
"Scanning": "Scanning...",
1924+
"SystemAnnouncement": "System announcement"
19221925
},
19231926
"maxLength": {
19241927
"200chars": "(maximum 200 chars)",

resources/i18n/es.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1914,11 +1914,13 @@
19141914
"UpTo3000Logs": "(Hasta 3000 registros recientes)"
19151915
},
19161916
"maintenance": {
1917+
"Announcement": "Anuncio",
19171918
"CleanupImages": "Limpieza de imágenes",
19181919
"CleanupOldImages": "Limpiar imágenes antiguas",
19191920
"DescCleanupOldImages": "Limpiar imágenes antiguas de la lista de imágenes docker.",
19201921
"DescMatchDatabase": "Recalcular la base de datos de uso con el estado actual. Util si docker falla por accidente, o algunas sesiones han sido terminadas forzosamente.",
19211922
"DescRescanImageList": "Volver a escanear la lista de imágenes de los repositorios registrados. Puede tardar mucho tiempo, así que espere después de la ejecución.",
1923+
"DescSystemAnnouncement": "Cree o edite el banner de anuncios que se muestra a todos los usuarios en la parte superior de la página.",
19221924
"Fix": "Fijar",
19231925
"ImagesEnvironment": "Imágenes / Medio ambiente",
19241926
"MatchDatabase": "Hacer coincidir la base de datos de uso con el estado actual",
@@ -1931,7 +1933,8 @@
19311933
"RescanImageList": "Volver a escanear la lista de imágenes del repositorio",
19321934
"RescanImageScanning": "Reexploración...",
19331935
"RescanImages": "Volver a escanear imágenes",
1934-
"Scanning": "Escaneando..."
1936+
"Scanning": "Escaneando...",
1937+
"SystemAnnouncement": "Anuncio del sistema"
19351938
},
19361939
"maxLength": {
19371940
"200chars": "(máximo 200 caracteres)",

resources/i18n/fi.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1914,11 +1914,13 @@
19141914
"UpTo3000Logs": "(Enintään 3000 viimeisintä lokia)"
19151915
},
19161916
"maintenance": {
1917+
"Announcement": "Ilmoitus",
19171918
"CleanupImages": "Siivous Kuvat",
19181919
"CleanupOldImages": "Vanhojen kuvien siivoaminen",
19191920
"DescCleanupOldImages": "Vanhojen kuvien siivoaminen docker-kuvaluettelosta.",
19201921
"DescMatchDatabase": "Laske käyttötietokanta uudelleen senhetkisen tilan mukaan. Hyödyllinen, jos docker epäonnistuu vahingossa tai jotkin istunnot on lopetettu väkisin.",
19211922
"DescRescanImageList": "Skannaa kuvaluettelo rekisteröidyistä arkistoista. Se voi kestää kauan, joten odota suorituksen jälkeen.",
1923+
"DescSystemAnnouncement": "Luo tai muokkaa ilmoitusbanneria, joka näytetään kaikille käyttäjille sivun yläosassa.",
19221924
"Fix": "Korjaa",
19231925
"ImagesEnvironment": "Kuvat / Ympäristö",
19241926
"MatchDatabase": "Sovita käyttötietokanta nykyiseen tilaan",
@@ -1931,7 +1933,8 @@
19311933
"RescanImageList": "Kuvaluettelon uudelleenetsintä arkistosta",
19321934
"RescanImageScanning": "Uudelleen skannaus...",
19331935
"RescanImages": "Kuvien uudelleenkuvaus",
1934-
"Scanning": "Skannaus..."
1936+
"Scanning": "Skannaus...",
1937+
"SystemAnnouncement": "Järjestelmäilmoitus"
19351938
},
19361939
"maxLength": {
19371940
"200chars": "(enintään 200 merkkiä)",

resources/i18n/fr.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1915,11 +1915,13 @@
19151915
"UpTo3000Logs": "(Jusqu'à 3000 journaux récents)"
19161916
},
19171917
"maintenance": {
1918+
"Announcement": "Annonce",
19181919
"CleanupImages": "Nettoyer les images",
19191920
"CleanupOldImages": "Nettoyer les anciennes images",
19201921
"DescCleanupOldImages": "Nettoyez les anciennes images de la liste d'images docker.",
19211922
"DescMatchDatabase": "Recalculer la base de données d'utilisation avec l'état actuel. Utile si docker échoue par accident ou si certaines sessions ont été interrompues de force.",
19221923
"DescRescanImageList": "Renumérisez la liste d'images à partir des référentiels enregistrés. Cela peut prendre beaucoup de temps, veuillez donc patienter après l'exécution.",
1924+
"DescSystemAnnouncement": "Créez ou modifiez la bannière d'annonce affichée à tous les utilisateurs en haut de la page.",
19231925
"Fix": "Réparer",
19241926
"ImagesEnvironment": "Images / Environnement",
19251927
"MatchDatabase": "Faire correspondre la base de données d'utilisation avec l'état actuel",
@@ -1932,7 +1934,8 @@
19321934
"RescanImageList": "Renumériser la liste d'images à partir du référentiel",
19331935
"RescanImageScanning": "Renumériser...",
19341936
"RescanImages": "Renumériser les images",
1935-
"Scanning": "Balayage..."
1937+
"Scanning": "Balayage...",
1938+
"SystemAnnouncement": "Annonce système"
19361939
},
19371940
"maxLength": {
19381941
"200chars": "(maximum 200 caractères)",

resources/i18n/id.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1917,11 +1917,13 @@
19171917
"UpTo3000Logs": "(Hingga 3000 log terbaru)"
19181918
},
19191919
"maintenance": {
1920+
"Announcement": "Pengumuman",
19201921
"CleanupImages": "Bereskan Image",
19211922
"CleanupOldImages": "Bereskan image lama",
19221923
"DescCleanupOldImages": "Pemberesan image lama dari daftar image docker.",
19231924
"DescMatchDatabase": "Hitung ulang database penggunaan dengan kondisi saat ini. Berguna jika docker gagal secara tidak sengaja, atau jika beberapa sesi telah dihentikan secara paksa.",
19241925
"DescRescanImageList": "Pindai ulang daftar image dari repositori terdaftar. Mungkin butuh waktu lama, jadi harap tunggu setelah eksekusi.",
1926+
"DescSystemAnnouncement": "Buat atau edit banner pengumuman yang ditampilkan kepada semua pengguna di bagian atas halaman.",
19251927
"Fix": "Perbaiki",
19261928
"ImagesEnvironment": "Image / Lingkungan",
19271929
"MatchDatabase": "Cocokkan database penggunaan dengan status saat ini",
@@ -1934,7 +1936,8 @@
19341936
"RescanImageList": "Pindai ulang daftar image dari repositori",
19351937
"RescanImageScanning": "Memindai ulang...",
19361938
"RescanImages": "Pindai Ulang Image",
1937-
"Scanning": "Memindai..."
1939+
"Scanning": "Memindai...",
1940+
"SystemAnnouncement": "Pengumuman sistem"
19381941
},
19391942
"maxLength": {
19401943
"200chars": "(maksimum 200 karakter)",

resources/i18n/it.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1914,11 +1914,13 @@
19141914
"UpTo3000Logs": "(Fino a 3000 registri recenti)"
19151915
},
19161916
"maintenance": {
1917+
"Announcement": "Annuncio",
19171918
"CleanupImages": "Immagini di pulizia",
19181919
"CleanupOldImages": "Pulisci le vecchie immagini",
19191920
"DescCleanupOldImages": "Pulisci le vecchie immagini dall'elenco delle immagini della finestra mobile.",
19201921
"DescMatchDatabase": "Ricalcola il database di utilizzo con lo stato corrente. Utile se la finestra mobile si guasta per errore o alcune sessioni sono state terminate forzatamente.",
19211922
"DescRescanImageList": "Riscansiona l'elenco delle immagini dai repository registrati. Potrebbe volerci molto tempo, quindi attendere dopo l'esecuzione.",
1923+
"DescSystemAnnouncement": "Crea o modifica il banner degli annunci mostrato a tutti gli utenti nella parte superiore della pagina.",
19221924
"Fix": "Correzione",
19231925
"ImagesEnvironment": "Immagini / Ambiente",
19241926
"MatchDatabase": "Abbina il database di utilizzo allo stato attuale",
@@ -1931,7 +1933,8 @@
19311933
"RescanImageList": "Riscansiona l'elenco delle immagini dal repository",
19321934
"RescanImageScanning": "Nuova scansione...",
19331935
"RescanImages": "Nuova scansione delle immagini",
1934-
"Scanning": "Scansione in corso..."
1936+
"Scanning": "Scansione in corso...",
1937+
"SystemAnnouncement": "Annuncio di sistema"
19351938
},
19361939
"maxLength": {
19371940
"200chars": "(massimo 200 caratteri)",

resources/i18n/ja.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1916,11 +1916,13 @@
19161916
"UpTo3000Logs": "(最大3000の最近のログ)"
19171917
},
19181918
"maintenance": {
1919+
"Announcement": "お知らせ",
19191920
"CleanupImages": "クリーンアップ画像",
19201921
"CleanupOldImages": "古い画像をクリーンアップする",
19211922
"DescCleanupOldImages": "Dockerイメージリストから古いイメージをクリーンアップします。",
19221923
"DescMatchDatabase": "現在の状態で使用状況データベースを再計算します。 dockerが誤って失敗した場合、または一部のセッションが強制的に終了した場合に役立ちます。",
19231924
"DescRescanImageList": "登録されたリポジトリから画像リストを再スキャンします。 時間がかかる場合がありますので、実行後しばらくお待ちください。",
1925+
"DescSystemAnnouncement": "すべてのユーザーにページ上部に表示されるお知らせバナーを作成または編集します。",
19241926
"Fix": "修正",
19251927
"ImagesEnvironment": "画像/環境",
19261928
"MatchDatabase": "使用状況データベースを現在の状態と照合する",
@@ -1933,7 +1935,8 @@
19331935
"RescanImageList": "リポジトリから画像リストを再スキャンします",
19341936
"RescanImageScanning": "再スキャン...",
19351937
"RescanImages": "画像を再スキャンする",
1936-
"Scanning": "走査..."
1938+
"Scanning": "走査...",
1939+
"SystemAnnouncement": "システムのお知らせ"
19371940
},
19381941
"maxLength": {
19391942
"200chars": "(最大200文字)",

0 commit comments

Comments
 (0)