App.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import { useEffect, useCallback } from 'react';
  2. import { Routes, Route, Navigate, useNavigate } from 'react-router-dom';
  3. import { useAuth } from '@picobaas/client/react';
  4. import Layout from './components/Layout';
  5. import HomePage from './pages/HomePage';
  6. import LoginPage from './pages/LoginPage';
  7. import RegisterPage from './pages/RegisterPage';
  8. import ForgotPasswordPage from './pages/ForgotPasswordPage';
  9. import ResetPasswordPage from './pages/ResetPasswordPage';
  10. import VerifyEmailPage from './pages/VerifyEmailPage';
  11. import DashboardPage from './pages/DashboardPage';
  12. import ImageDetailPage from './pages/ImageDetailPage';
  13. import SharedImagePage from './pages/SharedImagePage';
  14. import ImageAnalyticsPage from './pages/ImageAnalyticsPage';
  15. // Hook to handle session expiry - auto logout when session expires
  16. function useSessionExpiry() {
  17. const { logout, isAuthenticated } = useAuth();
  18. const navigate = useNavigate();
  19. const handleSessionExpired = useCallback(async (event: Event) => {
  20. const customEvent = event as CustomEvent<{ reason: string }>;
  21. console.warn('Session expired:', customEvent.detail?.reason);
  22. // Only handle if user was authenticated
  23. if (isAuthenticated) {
  24. await logout();
  25. navigate('/login', {
  26. replace: true,
  27. state: { message: 'Your session has expired. Please log in again.' }
  28. });
  29. }
  30. }, [logout, navigate, isAuthenticated]);
  31. useEffect(() => {
  32. window.addEventListener('baas:session-expired', handleSessionExpired);
  33. return () => {
  34. window.removeEventListener('baas:session-expired', handleSessionExpired);
  35. };
  36. }, [handleSessionExpired]);
  37. }
  38. // Protected route wrapper
  39. function ProtectedRoute({ children }: { children: React.ReactNode }) {
  40. const { isAuthenticated, isLoading } = useAuth();
  41. if (isLoading) {
  42. return (
  43. <div className="min-h-screen flex items-center justify-center">
  44. <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary-600"></div>
  45. </div>
  46. );
  47. }
  48. if (!isAuthenticated) {
  49. return <Navigate to="/login" replace />;
  50. }
  51. return <>{children}</>;
  52. }
  53. function App() {
  54. // Auto-logout when session expires
  55. useSessionExpiry();
  56. return (
  57. <Routes>
  58. {/* Public routes */}
  59. <Route path="/" element={<Layout />}>
  60. <Route index element={<HomePage />} />
  61. <Route path="login" element={<LoginPage />} />
  62. <Route path="register" element={<RegisterPage />} />
  63. <Route path="forgot-password" element={<ForgotPasswordPage />} />
  64. <Route path="reset-password" element={<ResetPasswordPage />} />
  65. <Route path="verify-email" element={<VerifyEmailPage />} />
  66. {/* Protected routes */}
  67. <Route
  68. path="dashboard"
  69. element={
  70. <ProtectedRoute>
  71. <DashboardPage />
  72. </ProtectedRoute>
  73. }
  74. />
  75. <Route
  76. path="image/:imageId"
  77. element={
  78. <ProtectedRoute>
  79. <ImageDetailPage />
  80. </ProtectedRoute>
  81. }
  82. />
  83. <Route
  84. path="analytics/:shortCode"
  85. element={
  86. <ProtectedRoute>
  87. <ImageAnalyticsPage />
  88. </ProtectedRoute>
  89. }
  90. />
  91. </Route>
  92. {/* Shared image view (no layout) */}
  93. <Route path="s/:shareId" element={<SharedImagePage />} />
  94. {/* Catch all */}
  95. <Route path="*" element={<Navigate to="/" replace />} />
  96. </Routes>
  97. );
  98. }
  99. export default App;