Skip to content

Commit

Permalink
feat: Error boundary for staff profiles
Browse files Browse the repository at this point in the history
fixes #538
  • Loading branch information
idabblewith committed Feb 24, 2025
1 parent aa0e058 commit 8fbf0d4
Show file tree
Hide file tree
Showing 8 changed files with 268 additions and 42 deletions.
43 changes: 43 additions & 0 deletions src/components/Base/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from "react";
import ErrorPage from "./ErrorPage";

interface Props {
children: React.ReactNode;
isSuperuser?: boolean;
}

interface State {
hasError: boolean;
error: Error | null;
}

class ErrorBoundary extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { hasError: false, error: null };
}

static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}

componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error("Error caught by boundary:", error, errorInfo);
}

render() {
if (this.state.hasError) {
return (
<ErrorPage
error={this.state.error}
isSuperuser={this.props.isSuperuser}
resetError={() => this.setState({ hasError: false, error: null })}
/>
);
}

return this.props.children;
}
}

export default ErrorBoundary;
70 changes: 70 additions & 0 deletions src/components/Base/ErrorPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React from "react";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { RefreshCw, Home } from "lucide-react";

interface ErrorPageProps {
error?: Error | null;
resetError?: () => void;
isSuperuser?: boolean;
}

const ErrorPage: React.FC<ErrorPageProps> = ({
error,
resetError,
isSuperuser = false,
}) => {
if (isSuperuser && error) {
console.error("Error caught by boundary:", error);
}

return (
<div className="container mx-auto flex min-h-[60vh] items-center justify-center px-4">
<Card className="w-full max-w-2xl">
<CardHeader>
<CardTitle className="text-2xl">Something went wrong</CardTitle>
<CardDescription>
We encountered an unexpected error while loading this page
</CardDescription>
</CardHeader>
<CardContent>
{isSuperuser && error && (
<div className="mt-0 overflow-hidden rounded-lg bg-slate-800 p-4">
<p className="mb-2 text-sm text-slate-400">Error details:</p>
<pre className="overflow-auto text-sm text-white">
{error.toString()}
</pre>
</div>
)}
</CardContent>
<CardFooter className="flex gap-4">
<Button
variant="default"
onClick={() => window.location.reload()}
className="gap-2 bg-blue-600 hover:bg-blue-700"
>
<RefreshCw className="h-4 w-4" />
Reload Page
</Button>
<Button
variant="outline"
onClick={() => (window.location.href = "/")}
className="gap-2"
>
<Home className="h-4 w-4" />
Return Home
</Button>
</CardFooter>
</Card>
</div>
);
};

export default ErrorPage;
3 changes: 2 additions & 1 deletion src/components/Pages/Account/CustomTitleSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,8 @@ const CustomTitleSection = ({ me }: { me: IUserMe }) => {
>
contact HR
</a>{" "}
to update your HR details. This process may take some time.
to update your HR details (recommended). This process may take some
time.
</FormHelperText>
</FormControl>
</Flex>
Expand Down
85 changes: 45 additions & 40 deletions src/components/StaffProfiles/ScienceStaffLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,57 +5,62 @@ import "@/main.css";
import "@/components/StaffProfiles/science_staff.css";
import ScienceHeader from "./Header/ScienceHeader";
import ScienceFooter from "./Footer/ScienceFooter";
import { useUser } from "@/lib/hooks/tanstack/useUser";
import ErrorBoundary from "../Base/ErrorBoundary";

export const ScienceStaffLayout = ({ children }: { children: ReactNode }) => {
const isDesktop = useMediaQuery("(min-width: 768px)");
const { userData, userLoading } = useUser();

return (
<Box
h={"100vh"}
w={"100vw"}
overscrollBehaviorY={"none"}
// overflowY={"scroll"}
minW={"420px"}
display="flex"
flexDirection="column"
pos={"fixed"}
// bg={"red.300"}
>
<ErrorBoundary isSuperuser={userData?.is_superuser}>
<Box
minH={"full"}
// bg={"blue.300"}
top={0}
left={0}
right={0}
bottom={0}
scrollBehavior={"smooth"}
overflowY={"scroll"}
css={{
msOverflowStyle: "none",
scrollbarWidth: "none",
listStyle: "none",
"::-webkit-scrollbar": {
display: "none",
},
}}
h={"100vh"}
w={"100vw"}
overscrollBehaviorY={"none"}
// overflowY={"scroll"}
minW={"420px"}
display="flex"
flexDirection="column"
pos={"fixed"}
// bg={"red.300"}
>
<ScienceHeader isDesktop={isDesktop} />
<Box
as="main"
className="text-slate-900"
flex="1"
display="flex"
flexDirection="column"
overscrollBehaviorY={"none"}
minH={"full"}
pos={"relative"}
// bg={"blue.300"}
top={0}
left={0}
right={0}
bottom={0}
scrollBehavior={"smooth"}
overflowY={"scroll"}
css={{
msOverflowStyle: "none",
scrollbarWidth: "none",
listStyle: "none",
"::-webkit-scrollbar": {
display: "none",
},
}}
>
{children}
</Box>
<Box pos={"relative"} w={"full"} bottom={0}>
<ScienceFooter />
<ScienceHeader isDesktop={isDesktop} />
<Box
as="main"
className="text-slate-900"
flex="1"
display="flex"
flexDirection="column"
overscrollBehaviorY={"none"}
minH={"full"}
pos={"relative"}
>
{children}
</Box>
<Box pos={"relative"} w={"full"} bottom={0}>
<ScienceFooter />
</Box>
</Box>
</Box>
</Box>
</ErrorBoundary>
);
};
14 changes: 14 additions & 0 deletions src/components/StaffProfiles/Staff/Detail/StaffContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { IStaffProfileBaseData, IUserMe } from "@/types";
import { useMediaQuery } from "@/lib/utils/useMediaQuery";
import clsx from "clsx";
import StaffProfileNavMenuItemButton from "./StaffProfileNavMenuItemButton";
import { Button } from "@/components/ui/button";

const StaffContent = ({
buttonsVisible,
Expand All @@ -26,6 +27,11 @@ const StaffContent = ({
const [selectedNav, setSelectedNav] = useState<string>("Overview");
const { colorMode } = useColorMode();
const isDesktop = useMediaQuery("(min-width: 768px)");
// const [shouldError, setShouldError] = useState(false);
// if (shouldError) {
// // This will trigger during render, simulating a failed data requirement
// throw new Error("Failed to load required staff profile data");
// }

const CV_TAB_NAMES = ["CV", "Background", "Experience"];

Expand Down Expand Up @@ -78,6 +84,14 @@ const StaffContent = ({
!isDesktop && "w-[420px]",
)}
>
{/* {viewingUser.is_superuser && (
<Button
className="ml-3 mt-4 bg-red-800"
onClick={() => setShouldError(true)}
>
Trigger error
</Button>
)} */}
{selectedNav === "Overview" ? (
<OverviewSection
userId={usersPk}
Expand Down
79 changes: 79 additions & 0 deletions src/components/ui/card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import * as React from "react"

import { cn } from "@/lib/utils"

const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
"rounded-lg border bg-card text-card-foreground shadow-sm",
className
)}
{...props}
/>
))
Card.displayName = "Card"

const CardHeader = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props}
/>
))
CardHeader.displayName = "CardHeader"

const CardTitle = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
"text-2xl font-semibold leading-none tracking-tight",
className
)}
{...props}
/>
))
CardTitle.displayName = "CardTitle"

const CardDescription = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
CardDescription.displayName = "CardDescription"

const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
))
CardContent.displayName = "CardContent"

const CardFooter = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex items-center p-6 pt-0", className)}
{...props}
/>
))
CardFooter.displayName = "CardFooter"

export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
11 changes: 11 additions & 0 deletions src/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,14 @@
body {
overscroll-behavior-y: none;
}



@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
}
5 changes: 4 additions & 1 deletion src/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import { Users } from "./routes/Users";
import { ScienceStaffLayout } from "./components/StaffProfiles/ScienceStaffLayout";
import { error } from "console";
import ProjectsMap from "./routes/ProjectsMap";
import ErrorBoundary from "./components/Base/ErrorBoundary";
import ErrorPage from "./components/Base/ErrorPage";

const inAppRouteArray = [
// Login
Expand Down Expand Up @@ -356,6 +358,7 @@ const staffProfilesAppArray = [
</ScienceStaffLayout>
),
// errorElement: <ErrorHandler />,
errorElement: <ErrorPage />,
},
{
path: "/staff/:staffProfilePk",
Expand All @@ -364,7 +367,7 @@ const staffProfilesAppArray = [
<ScienceStaffDetail />
</ScienceStaffLayout>
),
// errorElement: <ErrorHandler />,
errorElement: <ErrorPage />,
},
];

Expand Down

0 comments on commit 8fbf0d4

Please sign in to comment.