mirror of
https://github.com/Steffo99/sophon.git
synced 2024-12-22 06:44:21 +00:00
✨ Improve error handling
This commit is contained in:
parent
f748add494
commit
9af5ba714b
7 changed files with 180 additions and 113 deletions
|
@ -37,68 +37,70 @@ import {ThemeProvider} from "./contexts/theme"
|
|||
function App({..._}: RouteComponentProps) {
|
||||
return React.useMemo(
|
||||
() => <>
|
||||
<Chapter>
|
||||
<SophonDescriptionBox/>
|
||||
</Chapter>
|
||||
<InstanceProvider>
|
||||
<InstanceRouter
|
||||
unselectedRoute={() => <>
|
||||
<InstanceFormBox/>
|
||||
</>}
|
||||
selectedRoute={() => <>
|
||||
<Chapter>
|
||||
<InstanceDescriptionBox/>
|
||||
</Chapter>
|
||||
<AuthorizationProvider>
|
||||
<CacheProvider>
|
||||
<AuthorizationRouter
|
||||
unselectedRoute={AuthorizationStepPage}
|
||||
selectedRoute={() => <>
|
||||
<GroupRouter
|
||||
unselectedRoute={GroupStepPage}
|
||||
selectedRoute={({selection}) => <>
|
||||
<GroupProvider resource={selection}>
|
||||
<Chapter>
|
||||
<ResourceDescriptionBox resource={selection} icon={faUsers}/>
|
||||
<GroupMembersBox/>
|
||||
</Chapter>
|
||||
<ProjectRouter
|
||||
groupPk={selection.value.slug}
|
||||
unselectedRoute={({viewSet}) => <>
|
||||
<GroupCreateBox resource={selection}/>
|
||||
<ProjectListBox viewSet={viewSet}/>
|
||||
<ProjectCreateBox viewSet={viewSet}/>
|
||||
</>}
|
||||
selectedRoute={({selection}) => <>
|
||||
<ProjectProvider resource={selection}>
|
||||
<ResourceDescriptionBox resource={selection} icon={faProjectDiagram}/>
|
||||
<NotebookRouter
|
||||
projectPk={selection.value.slug}
|
||||
unselectedRoute={({viewSet}) => <>
|
||||
<ProjectCreateBox resource={selection}/>
|
||||
<NotebookListBox viewSet={viewSet}/>
|
||||
<NotebookCreateBox viewSet={viewSet}/>
|
||||
</>}
|
||||
selectedRoute={({selection}) => <>
|
||||
<NotebookProvider resource={selection}>
|
||||
<NotebookDescriptionBox/>
|
||||
<NotebookCreateBox resource={selection}/>
|
||||
</NotebookProvider>
|
||||
</>}
|
||||
/>
|
||||
</ProjectProvider>
|
||||
</>}
|
||||
/>
|
||||
</GroupProvider>
|
||||
</>}
|
||||
/>
|
||||
</>}
|
||||
/>
|
||||
</CacheProvider>
|
||||
</AuthorizationProvider>
|
||||
</>}
|
||||
/>
|
||||
</InstanceProvider>
|
||||
<ErrorCatcherBox>
|
||||
<Chapter>
|
||||
<SophonDescriptionBox/>
|
||||
</Chapter>
|
||||
<InstanceProvider>
|
||||
<InstanceRouter
|
||||
unselectedRoute={() => <>
|
||||
<InstanceFormBox/>
|
||||
</>}
|
||||
selectedRoute={() => <>
|
||||
<Chapter>
|
||||
<InstanceDescriptionBox/>
|
||||
</Chapter>
|
||||
<AuthorizationProvider>
|
||||
<CacheProvider>
|
||||
<AuthorizationRouter
|
||||
unselectedRoute={AuthorizationStepPage}
|
||||
selectedRoute={() => <>
|
||||
<GroupRouter
|
||||
unselectedRoute={GroupStepPage}
|
||||
selectedRoute={({selection}) => <>
|
||||
<GroupProvider resource={selection}>
|
||||
<Chapter>
|
||||
<ResourceDescriptionBox resource={selection} icon={faUsers}/>
|
||||
<GroupMembersBox/>
|
||||
</Chapter>
|
||||
<ProjectRouter
|
||||
groupPk={selection.value.slug}
|
||||
unselectedRoute={({viewSet}) => <>
|
||||
<GroupCreateBox resource={selection}/>
|
||||
<ProjectListBox viewSet={viewSet}/>
|
||||
<ProjectCreateBox viewSet={viewSet}/>
|
||||
</>}
|
||||
selectedRoute={({selection}) => <>
|
||||
<ProjectProvider resource={selection}>
|
||||
<ResourceDescriptionBox resource={selection} icon={faProjectDiagram}/>
|
||||
<NotebookRouter
|
||||
projectPk={selection.value.slug}
|
||||
unselectedRoute={({viewSet}) => <>
|
||||
<ProjectCreateBox resource={selection}/>
|
||||
<NotebookListBox viewSet={viewSet}/>
|
||||
<NotebookCreateBox viewSet={viewSet}/>
|
||||
</>}
|
||||
selectedRoute={({selection}) => <>
|
||||
<NotebookProvider resource={selection}>
|
||||
<NotebookDescriptionBox/>
|
||||
<NotebookCreateBox resource={selection}/>
|
||||
</NotebookProvider>
|
||||
</>}
|
||||
/>
|
||||
</ProjectProvider>
|
||||
</>}
|
||||
/>
|
||||
</GroupProvider>
|
||||
</>}
|
||||
/>
|
||||
</>}
|
||||
/>
|
||||
</CacheProvider>
|
||||
</AuthorizationProvider>
|
||||
</>}
|
||||
/>
|
||||
</InstanceProvider>
|
||||
</ErrorCatcherBox>
|
||||
</>,
|
||||
[],
|
||||
)
|
||||
|
@ -112,11 +114,9 @@ export default function AppWrapper() {
|
|||
<LayoutThreeCol>
|
||||
<LayoutThreeCol.Center>
|
||||
<ThemedTitle level={1}/>
|
||||
<ErrorCatcherBox>
|
||||
<Reach.Router>
|
||||
<App default/>
|
||||
</Reach.Router>
|
||||
</ErrorCatcherBox>
|
||||
<SophonFooter/>
|
||||
</LayoutThreeCol.Center>
|
||||
</LayoutThreeCol>
|
||||
|
|
34
frontend/src/components/elements/GoBackButton.tsx
Normal file
34
frontend/src/components/elements/GoBackButton.tsx
Normal file
|
@ -0,0 +1,34 @@
|
|||
import {faLevelUpAlt} from "@fortawesome/free-solid-svg-icons"
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
|
||||
import * as Reach from "@reach/router"
|
||||
import {Button} from "@steffo/bluelib-react"
|
||||
import {ButtonProps} from "@steffo/bluelib-react/dist/components/inputs/Button"
|
||||
import * as React from "react"
|
||||
import {useSophonPath} from "../../hooks/useSophonPath"
|
||||
|
||||
|
||||
/**
|
||||
* A button that takes the user back one sophon level (so two levels).
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
export function GoBackButton({onClick, ...props}: Omit<ButtonProps, "children">): JSX.Element {
|
||||
const location = useSophonPath()
|
||||
|
||||
const onClickWrapped = React.useCallback(
|
||||
async event => {
|
||||
event.preventDefault()
|
||||
if(onClick) {
|
||||
onClick(event)
|
||||
}
|
||||
await Reach.navigate("../..")
|
||||
},
|
||||
[onClick],
|
||||
)
|
||||
|
||||
return (
|
||||
<Button onClick={onClickWrapped} disabled={location.count === 0} {...props}>
|
||||
<FontAwesomeIcon icon={faLevelUpAlt}/> Go up
|
||||
</Button>
|
||||
)
|
||||
}
|
40
frontend/src/components/elements/NavigateButton.tsx
Normal file
40
frontend/src/components/elements/NavigateButton.tsx
Normal file
|
@ -0,0 +1,40 @@
|
|||
import * as Reach from "@reach/router"
|
||||
import {Button} from "@steffo/bluelib-react"
|
||||
import {ButtonProps} from "@steffo/bluelib-react/dist/components/inputs/Button"
|
||||
import * as React from "react"
|
||||
|
||||
|
||||
/**
|
||||
* The props of {@link NavigateButton}.
|
||||
*/
|
||||
export interface NavigateButtonProps extends ButtonProps {
|
||||
href: string,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A button functioning like a {@link Link}.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
export function NavigateButton({href, children, onClick, ...props}: NavigateButtonProps): JSX.Element {
|
||||
const location = Reach.useLocation()
|
||||
|
||||
const onClickWrapped = React.useCallback(
|
||||
event => {
|
||||
event.preventDefault()
|
||||
if(onClick) {
|
||||
onClick(event)
|
||||
}
|
||||
if(href) {
|
||||
// noinspection JSIgnoredPromiseFromCall
|
||||
Reach.navigate(href)
|
||||
}
|
||||
},
|
||||
[href, onClick],
|
||||
)
|
||||
|
||||
return (
|
||||
<Button children={children} onClick={onClickWrapped} disabled={location.pathname === href} {...props}/>
|
||||
)
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
import {Box, Form} from "@steffo/bluelib-react"
|
||||
import {faAngleDoubleRight, faBug} from "@fortawesome/free-solid-svg-icons"
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
|
||||
import {Box, Button, Form} from "@steffo/bluelib-react"
|
||||
import * as React from "react"
|
||||
import {IgnoreErrorButton} from "./IgnoreErrorButton"
|
||||
import {ReportBugButton} from "./ReportBugButton"
|
||||
import {GoBackButton} from "../elements/GoBackButton"
|
||||
import {NavigateButton} from "../elements/NavigateButton"
|
||||
|
||||
|
||||
interface ErrorCatcherBoxProps {
|
||||
|
@ -44,8 +46,13 @@ export class ErrorCatcherBox extends React.Component<ErrorCatcherBoxProps, Error
|
|||
{this.state.error.toString()}
|
||||
<Form>
|
||||
<Form.Row>
|
||||
<IgnoreErrorButton onClick={this.clearError}/>
|
||||
<ReportBugButton/>
|
||||
<NavigateButton href={"https://github.com/Steffo99/sophon/issues/new?assignees=&labels=bug&template=1_bug_report.md&title="}>
|
||||
<FontAwesomeIcon icon={faBug}/> Report bug
|
||||
</NavigateButton>
|
||||
<GoBackButton/>
|
||||
<Button onClick={this.clearError}>
|
||||
<FontAwesomeIcon icon={faAngleDoubleRight}/> Try ignoring the error
|
||||
</Button>
|
||||
</Form.Row>
|
||||
</Form>
|
||||
</Box>
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
import {faAngleDoubleRight} from "@fortawesome/free-solid-svg-icons"
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
|
||||
import {Button} from "@steffo/bluelib-react"
|
||||
import * as React from "react"
|
||||
|
||||
|
||||
export interface IgnoreErrorButtonProps {
|
||||
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
}
|
||||
|
||||
|
||||
export function IgnoreErrorButton({onClick}: IgnoreErrorButtonProps): JSX.Element {
|
||||
return (
|
||||
<Button onClick={onClick}>
|
||||
<FontAwesomeIcon icon={faAngleDoubleRight}/> Try ignoring the error
|
||||
</Button>
|
||||
)
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
import {faBug} from "@fortawesome/free-solid-svg-icons"
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
|
||||
import {navigate} from "@reach/router"
|
||||
import {Button} from "@steffo/bluelib-react"
|
||||
import * as React from "react"
|
||||
|
||||
|
||||
export function ReportBugButton(): JSX.Element {
|
||||
const onClick =
|
||||
React.useCallback(
|
||||
async () => {
|
||||
await navigate("https://github.com/Steffo99/sophon/issues/new?assignees=&labels=bug&template=1_bug_report.md&title=")
|
||||
},
|
||||
[],
|
||||
)
|
||||
|
||||
return React.useMemo(
|
||||
() => (
|
||||
<Button>
|
||||
<FontAwesomeIcon onClick={onClick} icon={faBug}/> Report bug
|
||||
</Button>
|
||||
),
|
||||
[onClick],
|
||||
)
|
||||
}
|
|
@ -37,6 +37,11 @@ export interface ParsedPath {
|
|||
* Passed the login page (either by browsing as guest or by logging in).
|
||||
*/
|
||||
loggedIn?: boolean,
|
||||
|
||||
/**
|
||||
* The number of pages that separate this to the website root.
|
||||
*/
|
||||
count: number,
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -44,7 +49,9 @@ export interface ParsedPath {
|
|||
* @param path - The path to split.
|
||||
*/
|
||||
export function parsePath(path: string): ParsedPath {
|
||||
let result: ParsedPath = {}
|
||||
let result: ParsedPath = {
|
||||
count: 0,
|
||||
}
|
||||
|
||||
result.instance = path.match(/[/]i[/]([^/]+)/)?.[1]
|
||||
result.userId = path.match(/[/]u[/]([0-9]+)/)?.[1]
|
||||
|
@ -54,5 +61,27 @@ export function parsePath(path: string): ParsedPath {
|
|||
result.notebook = path.match(/[/]n[/]([A-Za-z0-9_-]+)/)?.[1]
|
||||
result.loggedIn = Boolean(path.match(/[/]l[/]/))
|
||||
|
||||
if(result.instance) {
|
||||
result.count += 1
|
||||
}
|
||||
if(result.userId) {
|
||||
result.count += 1
|
||||
}
|
||||
if(result.userName) {
|
||||
result.count += 1
|
||||
}
|
||||
if(result.researchGroup) {
|
||||
result.count += 1
|
||||
}
|
||||
if(result.researchProject) {
|
||||
result.count += 1
|
||||
}
|
||||
if(result.notebook) {
|
||||
result.count += 1
|
||||
}
|
||||
if(result.loggedIn) {
|
||||
result.count += 1
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue