Drop the is_temporary boolean from the DB schema (new migration), types, API routers/helpers/middleware, and all frontend components and tests. Access control now relies solely on is_client. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
71 lines
2 KiB
TypeScript
71 lines
2 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import { Navigate, Outlet } from "react-router-dom";
|
|
import { match } from "ts-pattern";
|
|
import { redirectClientUserToPortal } from "../lib/clientPortal";
|
|
import { useMaybeUser } from "../providers/UserStoreProvider";
|
|
import { LoadingSpinner } from "./LoadingSpinner";
|
|
|
|
interface ProtectedRouteProps {
|
|
fallback?: string;
|
|
shouldRedirectToCurrentPage?: boolean;
|
|
}
|
|
|
|
export const ProtectedRoute = ({
|
|
fallback,
|
|
shouldRedirectToCurrentPage,
|
|
}: ProtectedRouteProps) => {
|
|
const user = useMaybeUser();
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
const timer = setTimeout(() => {
|
|
setIsLoading(false);
|
|
}, 500);
|
|
return () => clearTimeout(timer);
|
|
}, [user, fallback]);
|
|
|
|
let status:
|
|
| "loading"
|
|
| "should-land-user"
|
|
| "should-redirect"
|
|
| "should-redirect-client"
|
|
| "should-pass" = "loading";
|
|
|
|
const isFirstTimeUser = localStorage.getItem("xtablo-has-seen-landing-page") === null;
|
|
|
|
if (isLoading) {
|
|
status = "loading";
|
|
} else if (!user && isFirstTimeUser) {
|
|
status = "should-land-user";
|
|
} else if (!user) {
|
|
status = "should-redirect";
|
|
} else if (user.is_client) {
|
|
status = "should-redirect-client";
|
|
} else {
|
|
status = "should-pass";
|
|
}
|
|
|
|
const fallbackUrl = fallback ?? "/login";
|
|
|
|
const redirectUrl = shouldRedirectToCurrentPage
|
|
? `${fallbackUrl}?redirect=${encodeURIComponent(
|
|
`${window.location.pathname}${window.location.search}`
|
|
)}`
|
|
: fallbackUrl;
|
|
|
|
return match(status)
|
|
.with("loading", () => <LoadingSpinner />)
|
|
.with("should-land-user", () => <Navigate to="/landing" replace />)
|
|
.with("should-redirect", () => <Navigate to={redirectUrl} replace />)
|
|
.with("should-redirect-client", () => <ClientPortalRedirect />)
|
|
.with("should-pass", () => <Outlet />)
|
|
.exhaustive();
|
|
};
|
|
|
|
const ClientPortalRedirect = () => {
|
|
useEffect(() => {
|
|
redirectClientUserToPortal("/");
|
|
}, []);
|
|
|
|
return <LoadingSpinner />;
|
|
};
|