87 lines
2.9 KiB
TypeScript
87 lines
2.9 KiB
TypeScript
import React from "react";
|
||
import { useAuth } from "~/contexts/auth-context";
|
||
import { Navigate, useLocation } from "react-router";
|
||
import toast from "react-hot-toast";
|
||
import { LoadingPage } from "~/components/ui/loading";
|
||
|
||
interface ProtectedRouteProps {
|
||
children: React.ReactNode;
|
||
fallback?: React.ReactNode;
|
||
requireAuth?: boolean;
|
||
redirectTo?: string;
|
||
}
|
||
|
||
export function ProtectedRoute({
|
||
children,
|
||
fallback,
|
||
requireAuth = true,
|
||
redirectTo = "/login",
|
||
}: ProtectedRouteProps) {
|
||
const { isAuthenticated, isLoading, token, user } = useAuth();
|
||
const location = useLocation();
|
||
|
||
// Show loading while checking authentication
|
||
if (isLoading) {
|
||
return (
|
||
fallback || (
|
||
<div
|
||
className="min-h-screen flex items-center justify-center"
|
||
style={{
|
||
background:
|
||
"linear-gradient(135deg, var(--color-login-dark-start) 0%, var(--color-login-dark-end) 100%)",
|
||
}}
|
||
>
|
||
<div className="text-center space-y-6 max-w-md mx-auto p-8">
|
||
<div className="flex justify-center">
|
||
<div className="w-8 h-8 border-2 border-[var(--color-login-primary)] border-t-transparent rounded-full animate-spin"></div>
|
||
</div>
|
||
<div className="space-y-2">
|
||
<h2 className="text-lg font-medium font-persian text-white">
|
||
در حال بررسی احراز هویت...
|
||
</h2>
|
||
<p className="text-sm font-persian leading-relaxed text-gray-300">
|
||
لطفاً منتظر بمانید
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
);
|
||
}
|
||
|
||
// If authentication is required but user is not authenticated
|
||
if (requireAuth && !isAuthenticated) {
|
||
toast.error("برای دسترسی به این صفحه باید وارد شوید");
|
||
|
||
// Save the current location so we can redirect back after login
|
||
const currentPath = location.pathname + location.search;
|
||
const loginPath = `${redirectTo}?returnTo=${encodeURIComponent(currentPath)}`;
|
||
|
||
return <Navigate to={loginPath} replace />;
|
||
}
|
||
|
||
// If authentication is required but token is missing/invalid
|
||
if (requireAuth && isAuthenticated && (!token || !token.accessToken)) {
|
||
toast.error("جلسه کاری شما منقضی شده است. لطفاً دوباره وارد شوید");
|
||
|
||
// Clear any stored authentication data
|
||
localStorage.removeItem("auth_user");
|
||
localStorage.removeItem("auth_token");
|
||
|
||
return <Navigate to="/login" replace />;
|
||
}
|
||
|
||
// If user is authenticated but trying to access login page
|
||
if (!requireAuth && isAuthenticated && location.pathname === "/login") {
|
||
return <Navigate to="/dashboard" replace />;
|
||
}
|
||
|
||
// If all checks pass, render the protected content
|
||
return <>{children}</>;
|
||
}
|
||
|
||
// Helper component for public routes
|
||
export function PublicRoute({ children }: { children: React.ReactNode }) {
|
||
return <ProtectedRoute requireAuth={false}>{children}</ProtectedRoute>;
|
||
}
|