fix(260923-le6): Proxmox-Karte verweist Nicht-Admins nicht auf den Aktualisieren-Knopf

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-23 15:31:58 +02:00
parent 2eb86e14ea
commit 2f8dd14bfb
4 changed files with 69 additions and 6 deletions
@@ -8,6 +8,8 @@ vi.mock('next-intl', () => ({
'card.unknownValue': 'unbekannt', 'card.unknownValue': 'unbekannt',
'card.lastPolledLabel': 'Letzte Abfrage', 'card.lastPolledLabel': 'Letzte Abfrage',
'card.notPolledYet': 'Noch keine Abfrage gelaufen. Klicken Sie oben auf „{refreshLabel}“.', 'card.notPolledYet': 'Noch keine Abfrage gelaufen. Klicken Sie oben auf „{refreshLabel}“.',
'card.notPolledYetAutomatic':
'Noch keine Abfrage gelaufen. Die Werte erscheinen nach der nächsten automatischen Abfrage.',
'card.refresh': 'Jetzt aktualisieren', 'card.refresh': 'Jetzt aktualisieren',
'card.lastOkLabel': 'Letzte erfolgreiche Messung', 'card.lastOkLabel': 'Letzte erfolgreiche Messung',
'card.pve.nodeCount': 'Knoten', 'card.pve.nodeCount': 'Knoten',
@@ -255,7 +257,7 @@ describe('ServerCard', () => {
expect(screen.getByText(/Letzte Abfrage/)).toBeInTheDocument(); expect(screen.getByText(/Letzte Abfrage/)).toBeInTheDocument();
}); });
it('Nachbesserung Befund 2: ein frisch angelegter, noch nie abgefragter Server zeigt den ruhigen Hinweis statt "Ein unerwarteter Fehler ist aufgetreten"', async () => { it('Nachbesserung Befund 2: ein frisch angelegter, noch nie abgefragter Server zeigt Admins den ruhigen Hinweis mit Knopfverweis statt "Ein unerwarteter Fehler ist aufgetreten"', async () => {
const { ServerCard } = await import('./ServerCard'); const { ServerCard } = await import('./ServerCard');
// Zustand direkt nach `createServer`: leere Zwischenlagerzeile, noch nie abgefragt. // Zustand direkt nach `createServer`: leere Zwischenlagerzeile, noch nie abgefragt.
const server = makeServer({ const server = makeServer({
@@ -273,12 +275,68 @@ describe('ServerCard', () => {
}, },
}); });
render(<ServerCard server={server} isAdmin />);
expect(
screen.getByText('Noch keine Abfrage gelaufen. Klicken Sie oben auf „Jetzt aktualisieren“.'),
).toBeInTheDocument();
// Ohne die Korrektur erschiene hier faelschlich die Sammelmeldung — roter Test.
expect(screen.queryByText('Unerwarteter Fehler.')).not.toBeInTheDocument();
});
it('260923-le6: isAdmin={false}, noch nie abgefragt -> automatischer Hinweis ohne Knopfverweis', async () => {
const { ServerCard } = await import('./ServerCard');
const server = makeServer({
status: {
id: 's1',
serverId: 'srv-1',
lastPolledAt: null,
lastOkAt: null,
reachable: false,
errorKind: null,
errorDetail: null,
rawSample: null,
updatedAt: '2026-09-23T10:00:00.000Z',
metrics: null,
},
});
render(<ServerCard server={server} isAdmin={false} />);
expect(
screen.getByText(
'Noch keine Abfrage gelaufen. Die Werte erscheinen nach der nächsten automatischen Abfrage.',
),
).toBeInTheDocument();
expect(screen.queryByText(/Jetzt aktualisieren/)).not.toBeInTheDocument();
expect(screen.queryByText('Unerwarteter Fehler.')).not.toBeInTheDocument();
});
it('260923-le6: isAdmin weggelassen, noch nie abgefragt -> verhaelt sich wie isAdmin={false} (sichere Vorgabe)', async () => {
const { ServerCard } = await import('./ServerCard');
const server = makeServer({
status: {
id: 's1',
serverId: 'srv-1',
lastPolledAt: null,
lastOkAt: null,
reachable: false,
errorKind: null,
errorDetail: null,
rawSample: null,
updatedAt: '2026-09-23T10:00:00.000Z',
metrics: null,
},
});
render(<ServerCard server={server} />); render(<ServerCard server={server} />);
expect( expect(
screen.getByText('Noch keine Abfrage gelaufen. Klicken Sie oben auf „Jetzt aktualisieren“.'), screen.getByText(
'Noch keine Abfrage gelaufen. Die Werte erscheinen nach der nächsten automatischen Abfrage.',
),
).toBeInTheDocument(); ).toBeInTheDocument();
// Ohne die Korrektur erschiene hier faelschlich die Sammelmeldung — roter Test. expect(screen.queryByText(/Jetzt aktualisieren/)).not.toBeInTheDocument();
expect(screen.queryByText('Unerwarteter Fehler.')).not.toBeInTheDocument(); expect(screen.queryByText('Unerwarteter Fehler.')).not.toBeInTheDocument();
}); });
@@ -146,13 +146,14 @@ function errorMessage(t: ReturnType<typeof useTranslations>, kind: ProxmoxErrorK
interface ServerCardProps { interface ServerCardProps {
server: ProxmoxServer; server: ProxmoxServer;
isAdmin?: boolean;
} }
/** /**
* Anzeige EINES Servers (Aufgabe 6) — verzweigt ueber `productType` auf der * Anzeige EINES Servers (Aufgabe 6) — verzweigt ueber `productType` auf der
* unterscheidbaren Union aus Aufgabe 3. * unterscheidbaren Union aus Aufgabe 3.
*/ */
export function ServerCard({ server }: ServerCardProps) { export function ServerCard({ server, isAdmin = false }: ServerCardProps) {
const t = useTranslations('proxmox'); const t = useTranslations('proxmox');
const unknown = t('card.unknownValue'); const unknown = t('card.unknownValue');
const status = server.status; const status = server.status;
@@ -178,11 +179,13 @@ export function ServerCard({ server }: ServerCardProps) {
(`lastPolledAt === null`), zeigt einen ruhigen Hinweis statt der (`lastPolledAt === null`), zeigt einen ruhigen Hinweis statt der
Fehlermeldung — die leere Zwischenlagerzeile aus `createServer` hat Fehlermeldung — die leere Zwischenlagerzeile aus `createServer` hat
`reachable: false` und `errorKind: null`, was sonst faelschlich als `reachable: false` und `errorKind: null`, was sonst faelschlich als
"unbekannter Fehler" erschien. "unbekannter Fehler" erschien. Nicht-Admins sehen den Knopf nicht
(der Poll-Endpunkt verlangt ADMIN/SUPER_ADMIN) und bekommen deshalb
den Text ohne Knopfverweis (260923-le6).
*/} */}
{status && !status.lastPolledAt && ( {status && !status.lastPolledAt && (
<p className="mt-2 text-xs text-muted-foreground"> <p className="mt-2 text-xs text-muted-foreground">
{t('card.notPolledYet', { refreshLabel: t('card.refresh') })} {isAdmin ? t('card.notPolledYet', { refreshLabel: t('card.refresh') }) : t('card.notPolledYetAutomatic')}
</p> </p>
)} )}
+1
View File
@@ -713,6 +713,7 @@
"unknownValue": "unbekannt", "unknownValue": "unbekannt",
"lastPolledLabel": "Letzte Abfrage", "lastPolledLabel": "Letzte Abfrage",
"notPolledYet": "Noch keine Abfrage gelaufen. Klicken Sie oben auf „{refreshLabel}“.", "notPolledYet": "Noch keine Abfrage gelaufen. Klicken Sie oben auf „{refreshLabel}“.",
"notPolledYetAutomatic": "Noch keine Abfrage gelaufen. Die Werte erscheinen nach der nächsten automatischen Abfrage.",
"lastOkLabel": "Letzte erfolgreiche Messung", "lastOkLabel": "Letzte erfolgreiche Messung",
"refresh": "Jetzt aktualisieren", "refresh": "Jetzt aktualisieren",
"refreshing": "Wird aktualisiert...", "refreshing": "Wird aktualisiert...",
+1
View File
@@ -713,6 +713,7 @@
"unknownValue": "unknown", "unknownValue": "unknown",
"lastPolledLabel": "Last poll", "lastPolledLabel": "Last poll",
"notPolledYet": "No poll has run yet. Click \"{refreshLabel}\" above.", "notPolledYet": "No poll has run yet. Click \"{refreshLabel}\" above.",
"notPolledYetAutomatic": "No poll has run yet. The values will appear after the next automatic poll.",
"lastOkLabel": "Last successful measurement", "lastOkLabel": "Last successful measurement",
"refresh": "Refresh now", "refresh": "Refresh now",
"refreshing": "Refreshing...", "refreshing": "Refreshing...",