feat(notfound): add NotFound page with 404 handling and localization
All checks were successful
Build & Push Docker Image / build (push) Successful in 6m33s
All checks were successful
Build & Push Docker Image / build (push) Successful in 6m33s
This commit is contained in:
@@ -18,6 +18,7 @@ import FederationInbox from './pages/FederationInbox';
|
|||||||
import FederatedRoomDetail from './pages/FederatedRoomDetail';
|
import FederatedRoomDetail from './pages/FederatedRoomDetail';
|
||||||
import Calendar from './pages/Calendar';
|
import Calendar from './pages/Calendar';
|
||||||
import OAuthCallback from './pages/OAuthCallback';
|
import OAuthCallback from './pages/OAuthCallback';
|
||||||
|
import NotFound from './pages/NotFound';
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const { user, loading } = useAuth();
|
const { user, loading } = useAuth();
|
||||||
@@ -65,8 +66,8 @@ export default function App() {
|
|||||||
<Route path="/federation/rooms/:id" element={<FederatedRoomDetail />} />
|
<Route path="/federation/rooms/:id" element={<FederatedRoomDetail />} />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
{/* Catch all */}
|
{/* 404 */}
|
||||||
<Route path="*" element={<Navigate to="/" />} />
|
<Route path="*" element={<NotFound />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -412,7 +412,7 @@
|
|||||||
"oauthClientSecret": "Client-Secret",
|
"oauthClientSecret": "Client-Secret",
|
||||||
"oauthClientSecretHint": "Leer lassen, um das bestehende Secret beizubehalten",
|
"oauthClientSecretHint": "Leer lassen, um das bestehende Secret beizubehalten",
|
||||||
"oauthDisplayName": "Button-Beschriftung",
|
"oauthDisplayName": "Button-Beschriftung",
|
||||||
"oauthDisplayNameHint": "Wird auf der Login-Seite angezeigt, z. B. „Firmen-SSO"",
|
"oauthDisplayNameHint": "Wird auf der Login-Seite angezeigt, z. B. Firmen-SSO",
|
||||||
"oauthAutoRegister": "Neue Benutzer automatisch registrieren",
|
"oauthAutoRegister": "Neue Benutzer automatisch registrieren",
|
||||||
"oauthAutoRegisterHint": "Erstellt automatisch Konten für Benutzer, die sich zum ersten Mal per OAuth anmelden.",
|
"oauthAutoRegisterHint": "Erstellt automatisch Konten für Benutzer, die sich zum ersten Mal per OAuth anmelden.",
|
||||||
"oauthSaved": "OAuth-Konfiguration gespeichert",
|
"oauthSaved": "OAuth-Konfiguration gespeichert",
|
||||||
@@ -588,5 +588,11 @@
|
|||||||
"note": "Der Termin wurde automatisch aus deinem Kalender entfernt.",
|
"note": "Der Termin wurde automatisch aus deinem Kalender entfernt.",
|
||||||
"footer": "Diese Nachricht wurde automatisch von {appName} versendet."
|
"footer": "Diese Nachricht wurde automatisch von {appName} versendet."
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notFound": {
|
||||||
|
"title": "Seite nicht gefunden",
|
||||||
|
"description": "Die Seite, die du suchst, existiert nicht oder wurde verschoben.",
|
||||||
|
"goBack": "Zurück",
|
||||||
|
"goHome": "Zur Startseite"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -588,5 +588,11 @@
|
|||||||
"note": "The event has been automatically removed from your calendar.",
|
"note": "The event has been automatically removed from your calendar.",
|
||||||
"footer": "This message was sent automatically by {appName}."
|
"footer": "This message was sent automatically by {appName}."
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notFound": {
|
||||||
|
"title": "Page not found",
|
||||||
|
"description": "The page you are looking for doesn't exist or has been moved.",
|
||||||
|
"goBack": "Go back",
|
||||||
|
"goHome": "Back to home"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
59
src/pages/NotFound.jsx
Normal file
59
src/pages/NotFound.jsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { useLanguage } from '../contexts/LanguageContext';
|
||||||
|
import { Ghost, ArrowLeft, Home } from 'lucide-react';
|
||||||
|
|
||||||
|
export default function NotFound() {
|
||||||
|
const { t } = useLanguage();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen flex items-center justify-center p-6 relative overflow-hidden">
|
||||||
|
{/* Animated background */}
|
||||||
|
<div className="absolute inset-0 bg-th-bg">
|
||||||
|
<div className="absolute inset-0 opacity-20">
|
||||||
|
<div className="absolute top-1/3 left-1/3 w-96 h-96 bg-th-accent rounded-full blur-[128px] animate-pulse" />
|
||||||
|
<div className="absolute bottom-1/3 right-1/3 w-72 h-72 bg-purple-500 rounded-full blur-[128px] animate-pulse" style={{ animationDelay: '3s' }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative w-full max-w-md text-center">
|
||||||
|
<div className="card p-10 backdrop-blur-xl bg-th-card/80 border border-th-border shadow-2xl rounded-2xl">
|
||||||
|
{/* Ghost icon with subtle animation */}
|
||||||
|
<div className="flex justify-center mb-6">
|
||||||
|
<div className="w-20 h-20 bg-th-accent/10 rounded-full flex items-center justify-center animate-bounce" style={{ animationDuration: '2s' }}>
|
||||||
|
<Ghost size={40} className="text-th-accent" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 404 number */}
|
||||||
|
<h1 className="text-7xl font-extrabold text-th-text mb-2 tracking-tight">404</h1>
|
||||||
|
|
||||||
|
<h2 className="text-xl font-semibold text-th-text mb-2">
|
||||||
|
{t('notFound.title')}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<p className="text-th-text-s mb-8">
|
||||||
|
{t('notFound.description')}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Action buttons */}
|
||||||
|
<div className="flex flex-col sm:flex-row items-center justify-center gap-3">
|
||||||
|
<button
|
||||||
|
onClick={() => window.history.back()}
|
||||||
|
className="btn-secondary w-full sm:w-auto px-5 py-2.5 flex items-center justify-center gap-2"
|
||||||
|
>
|
||||||
|
<ArrowLeft size={16} />
|
||||||
|
{t('notFound.goBack')}
|
||||||
|
</button>
|
||||||
|
<Link
|
||||||
|
to="/"
|
||||||
|
className="btn-primary w-full sm:w-auto px-5 py-2.5 flex items-center justify-center gap-2"
|
||||||
|
>
|
||||||
|
<Home size={16} />
|
||||||
|
{t('notFound.goHome')}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user