Die Cache-Header waren schon optimal (index.html no-store, Assets immutable) – aber eine bereits laufende SPA holt sich nach einem Deploy keinen neuen Code, bis der Nutzer neu lädt. Das fängt kein Cache-Header ab. useVersionCheck holt die (server-seitig no-store) index.html periodisch (5 min) + beim Zurückkehren zum Tab, extrahiert die Menge der referenzierten Vite-Asset-Hashes als Signatur und vergleicht sie mit dem Startstand. Ändert sie sich (= neuer Build deployt), zeigt UpdateBanner oben im Layout einen dezenten Hinweis mit "Jetzt neu laden". Kein Backend-/Build-Change; im Dev-Modus No-op (Vite-Dev liefert keine /assets/<hash>-Dateien → leere Signatur → kein Alarm). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
44 lines
1.5 KiB
TypeScript
44 lines
1.5 KiB
TypeScript
import { useState } from 'react';
|
|
import { RefreshCw, X } from 'lucide-react';
|
|
import { useVersionCheck } from '../hooks/useVersionCheck';
|
|
|
|
/**
|
|
* Zeigt ein dezentes Banner, sobald nach einem Deploy eine neue Frontend-
|
|
* Version verfügbar ist (siehe useVersionCheck). "Jetzt neu laden" lädt die
|
|
* Seite neu und holt damit den frischen Code; "später" blendet für die
|
|
* laufende Session aus.
|
|
*/
|
|
export default function UpdateBanner() {
|
|
const updateAvailable = useVersionCheck();
|
|
const [dismissed, setDismissed] = useState(false);
|
|
|
|
if (!updateAvailable || dismissed) return null;
|
|
|
|
return (
|
|
<div className="bg-blue-600 text-white px-6 py-2.5">
|
|
<div className="flex items-center justify-between gap-3">
|
|
<div className="flex items-center gap-2 text-sm">
|
|
<RefreshCw className="w-4 h-4 flex-shrink-0" />
|
|
<span>Eine neue Version ist verfügbar. Bitte neu laden, um sie zu nutzen.</span>
|
|
</div>
|
|
<div className="flex items-center gap-2 flex-shrink-0">
|
|
<button
|
|
onClick={() => window.location.reload()}
|
|
className="px-3 py-1 text-sm font-medium bg-white text-blue-700 rounded hover:bg-blue-50 transition-colors"
|
|
>
|
|
Jetzt neu laden
|
|
</button>
|
|
<button
|
|
onClick={() => setDismissed(true)}
|
|
className="p-1 text-white/80 hover:text-white rounded"
|
|
title="Später"
|
|
aria-label="Hinweis ausblenden"
|
|
>
|
|
<X className="w-4 h-4" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|