2023-09-19 14:06:56 -04:00
|
|
|
import {
|
|
|
|
faBug,
|
|
|
|
faHome,
|
|
|
|
faRefresh,
|
|
|
|
faTrash,
|
|
|
|
} from '@fortawesome/free-solid-svg-icons'
|
2025-04-01 23:54:26 -07:00
|
|
|
import { isRouteErrorResponse, useRouteError } from 'react-router-dom'
|
|
|
|
|
|
|
|
import { ActionButton } from '@src/components/ActionButton'
|
|
|
|
import { isDesktop } from '@src/lib/isDesktop'
|
2023-09-15 22:37:40 -04:00
|
|
|
|
2025-03-11 13:46:07 -04:00
|
|
|
/** Type narrowing function of unknown error to a string */
|
|
|
|
function errorMessage(error: unknown): string {
|
|
|
|
if (isRouteErrorResponse(error)) {
|
|
|
|
return `${error.status} ${error.statusText}`
|
|
|
|
} else if (error != undefined && error instanceof Error) {
|
|
|
|
return error.message
|
|
|
|
} else if (error && typeof error === 'object') {
|
|
|
|
return JSON.stringify(error)
|
|
|
|
} else if (typeof error === 'string') {
|
|
|
|
return error
|
|
|
|
} else {
|
|
|
|
return 'Unknown error'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Generate a GitHub issue URL from the error */
|
|
|
|
function generateToUrl(error: unknown) {
|
|
|
|
const title: string = 'An unexpected error occurred'
|
|
|
|
const body = errorMessage(error)
|
|
|
|
const result = `https://github.com/KittyCAD/modeling-app/issues/new?title=${title}&body=${body}`
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2023-07-13 07:22:08 -04:00
|
|
|
export const ErrorPage = () => {
|
2023-09-15 22:37:40 -04:00
|
|
|
let error = useRouteError()
|
2025-03-11 13:46:07 -04:00
|
|
|
// We log the error to the console no matter what
|
2023-09-15 22:37:40 -04:00
|
|
|
console.error('error', error)
|
|
|
|
|
2023-07-13 07:22:08 -04:00
|
|
|
return (
|
|
|
|
<div className="flex flex-col items-center justify-center h-screen">
|
2023-09-15 22:37:40 -04:00
|
|
|
<section className="max-w-full xl:max-w-4xl mx-auto">
|
2024-05-01 08:24:07 -04:00
|
|
|
<h1 className="text-4xl mb-8 font-bold" data-testid="unexpected-error">
|
2023-09-15 22:37:40 -04:00
|
|
|
An unexpected error occurred
|
|
|
|
</h1>
|
2025-03-11 13:46:07 -04:00
|
|
|
<p className="mb-8 w-full overflow-aut">
|
|
|
|
<>{errorMessage(error)}</>
|
|
|
|
</p>
|
2023-09-19 14:06:56 -04:00
|
|
|
<div className="flex justify-between gap-2 mt-6">
|
2024-08-16 07:15:42 -04:00
|
|
|
{isDesktop() && (
|
2024-05-01 08:24:07 -04:00
|
|
|
<ActionButton
|
|
|
|
Element="link"
|
|
|
|
to={'/'}
|
2024-05-10 19:02:11 -04:00
|
|
|
iconStart={{ icon: faHome }}
|
2024-05-01 08:24:07 -04:00
|
|
|
data-testid="unexpected-error-home"
|
|
|
|
>
|
2023-09-19 14:06:56 -04:00
|
|
|
Go Home
|
|
|
|
</ActionButton>
|
|
|
|
)}
|
|
|
|
<ActionButton
|
|
|
|
Element="button"
|
2024-05-10 19:02:11 -04:00
|
|
|
iconStart={{ icon: faRefresh }}
|
2023-09-19 14:06:56 -04:00
|
|
|
onClick={() => window.location.reload()}
|
|
|
|
>
|
|
|
|
Reload
|
|
|
|
</ActionButton>
|
|
|
|
<ActionButton
|
|
|
|
Element="button"
|
2024-05-10 19:02:11 -04:00
|
|
|
iconStart={{ icon: faTrash }}
|
2023-09-19 14:06:56 -04:00
|
|
|
onClick={() => {
|
|
|
|
window.localStorage.clear()
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
Clear storage
|
|
|
|
</ActionButton>
|
|
|
|
<ActionButton
|
2023-10-04 18:00:55 -04:00
|
|
|
Element="externalLink"
|
2024-05-10 19:02:11 -04:00
|
|
|
iconStart={{ icon: faBug }}
|
2025-03-11 13:46:07 -04:00
|
|
|
to={generateToUrl(error)}
|
2023-09-19 14:06:56 -04:00
|
|
|
>
|
|
|
|
Report Bug
|
|
|
|
</ActionButton>
|
|
|
|
</div>
|
2023-09-15 22:37:40 -04:00
|
|
|
</section>
|
2023-07-13 07:22:08 -04:00
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|