mirror of
https://github.com/Steffo99/sophon.git
synced 2024-12-22 14:54:22 +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) {
|
function App({..._}: RouteComponentProps) {
|
||||||
return React.useMemo(
|
return React.useMemo(
|
||||||
() => <>
|
() => <>
|
||||||
<Chapter>
|
<ErrorCatcherBox>
|
||||||
<SophonDescriptionBox/>
|
<Chapter>
|
||||||
</Chapter>
|
<SophonDescriptionBox/>
|
||||||
<InstanceProvider>
|
</Chapter>
|
||||||
<InstanceRouter
|
<InstanceProvider>
|
||||||
unselectedRoute={() => <>
|
<InstanceRouter
|
||||||
<InstanceFormBox/>
|
unselectedRoute={() => <>
|
||||||
</>}
|
<InstanceFormBox/>
|
||||||
selectedRoute={() => <>
|
</>}
|
||||||
<Chapter>
|
selectedRoute={() => <>
|
||||||
<InstanceDescriptionBox/>
|
<Chapter>
|
||||||
</Chapter>
|
<InstanceDescriptionBox/>
|
||||||
<AuthorizationProvider>
|
</Chapter>
|
||||||
<CacheProvider>
|
<AuthorizationProvider>
|
||||||
<AuthorizationRouter
|
<CacheProvider>
|
||||||
unselectedRoute={AuthorizationStepPage}
|
<AuthorizationRouter
|
||||||
selectedRoute={() => <>
|
unselectedRoute={AuthorizationStepPage}
|
||||||
<GroupRouter
|
selectedRoute={() => <>
|
||||||
unselectedRoute={GroupStepPage}
|
<GroupRouter
|
||||||
selectedRoute={({selection}) => <>
|
unselectedRoute={GroupStepPage}
|
||||||
<GroupProvider resource={selection}>
|
selectedRoute={({selection}) => <>
|
||||||
<Chapter>
|
<GroupProvider resource={selection}>
|
||||||
<ResourceDescriptionBox resource={selection} icon={faUsers}/>
|
<Chapter>
|
||||||
<GroupMembersBox/>
|
<ResourceDescriptionBox resource={selection} icon={faUsers}/>
|
||||||
</Chapter>
|
<GroupMembersBox/>
|
||||||
<ProjectRouter
|
</Chapter>
|
||||||
groupPk={selection.value.slug}
|
<ProjectRouter
|
||||||
unselectedRoute={({viewSet}) => <>
|
groupPk={selection.value.slug}
|
||||||
<GroupCreateBox resource={selection}/>
|
unselectedRoute={({viewSet}) => <>
|
||||||
<ProjectListBox viewSet={viewSet}/>
|
<GroupCreateBox resource={selection}/>
|
||||||
<ProjectCreateBox viewSet={viewSet}/>
|
<ProjectListBox viewSet={viewSet}/>
|
||||||
</>}
|
<ProjectCreateBox viewSet={viewSet}/>
|
||||||
selectedRoute={({selection}) => <>
|
</>}
|
||||||
<ProjectProvider resource={selection}>
|
selectedRoute={({selection}) => <>
|
||||||
<ResourceDescriptionBox resource={selection} icon={faProjectDiagram}/>
|
<ProjectProvider resource={selection}>
|
||||||
<NotebookRouter
|
<ResourceDescriptionBox resource={selection} icon={faProjectDiagram}/>
|
||||||
projectPk={selection.value.slug}
|
<NotebookRouter
|
||||||
unselectedRoute={({viewSet}) => <>
|
projectPk={selection.value.slug}
|
||||||
<ProjectCreateBox resource={selection}/>
|
unselectedRoute={({viewSet}) => <>
|
||||||
<NotebookListBox viewSet={viewSet}/>
|
<ProjectCreateBox resource={selection}/>
|
||||||
<NotebookCreateBox viewSet={viewSet}/>
|
<NotebookListBox viewSet={viewSet}/>
|
||||||
</>}
|
<NotebookCreateBox viewSet={viewSet}/>
|
||||||
selectedRoute={({selection}) => <>
|
</>}
|
||||||
<NotebookProvider resource={selection}>
|
selectedRoute={({selection}) => <>
|
||||||
<NotebookDescriptionBox/>
|
<NotebookProvider resource={selection}>
|
||||||
<NotebookCreateBox resource={selection}/>
|
<NotebookDescriptionBox/>
|
||||||
</NotebookProvider>
|
<NotebookCreateBox resource={selection}/>
|
||||||
</>}
|
</NotebookProvider>
|
||||||
/>
|
</>}
|
||||||
</ProjectProvider>
|
/>
|
||||||
</>}
|
</ProjectProvider>
|
||||||
/>
|
</>}
|
||||||
</GroupProvider>
|
/>
|
||||||
</>}
|
</GroupProvider>
|
||||||
/>
|
</>}
|
||||||
</>}
|
/>
|
||||||
/>
|
</>}
|
||||||
</CacheProvider>
|
/>
|
||||||
</AuthorizationProvider>
|
</CacheProvider>
|
||||||
</>}
|
</AuthorizationProvider>
|
||||||
/>
|
</>}
|
||||||
</InstanceProvider>
|
/>
|
||||||
|
</InstanceProvider>
|
||||||
|
</ErrorCatcherBox>
|
||||||
</>,
|
</>,
|
||||||
[],
|
[],
|
||||||
)
|
)
|
||||||
|
@ -112,11 +114,9 @@ export default function AppWrapper() {
|
||||||
<LayoutThreeCol>
|
<LayoutThreeCol>
|
||||||
<LayoutThreeCol.Center>
|
<LayoutThreeCol.Center>
|
||||||
<ThemedTitle level={1}/>
|
<ThemedTitle level={1}/>
|
||||||
<ErrorCatcherBox>
|
|
||||||
<Reach.Router>
|
<Reach.Router>
|
||||||
<App default/>
|
<App default/>
|
||||||
</Reach.Router>
|
</Reach.Router>
|
||||||
</ErrorCatcherBox>
|
|
||||||
<SophonFooter/>
|
<SophonFooter/>
|
||||||
</LayoutThreeCol.Center>
|
</LayoutThreeCol.Center>
|
||||||
</LayoutThreeCol>
|
</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 * as React from "react"
|
||||||
import {IgnoreErrorButton} from "./IgnoreErrorButton"
|
import {GoBackButton} from "../elements/GoBackButton"
|
||||||
import {ReportBugButton} from "./ReportBugButton"
|
import {NavigateButton} from "../elements/NavigateButton"
|
||||||
|
|
||||||
|
|
||||||
interface ErrorCatcherBoxProps {
|
interface ErrorCatcherBoxProps {
|
||||||
|
@ -44,8 +46,13 @@ export class ErrorCatcherBox extends React.Component<ErrorCatcherBoxProps, Error
|
||||||
{this.state.error.toString()}
|
{this.state.error.toString()}
|
||||||
<Form>
|
<Form>
|
||||||
<Form.Row>
|
<Form.Row>
|
||||||
<IgnoreErrorButton onClick={this.clearError}/>
|
<NavigateButton href={"https://github.com/Steffo99/sophon/issues/new?assignees=&labels=bug&template=1_bug_report.md&title="}>
|
||||||
<ReportBugButton/>
|
<FontAwesomeIcon icon={faBug}/> Report bug
|
||||||
|
</NavigateButton>
|
||||||
|
<GoBackButton/>
|
||||||
|
<Button onClick={this.clearError}>
|
||||||
|
<FontAwesomeIcon icon={faAngleDoubleRight}/> Try ignoring the error
|
||||||
|
</Button>
|
||||||
</Form.Row>
|
</Form.Row>
|
||||||
</Form>
|
</Form>
|
||||||
</Box>
|
</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).
|
* Passed the login page (either by browsing as guest or by logging in).
|
||||||
*/
|
*/
|
||||||
loggedIn?: boolean,
|
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.
|
* @param path - The path to split.
|
||||||
*/
|
*/
|
||||||
export function parsePath(path: string): ParsedPath {
|
export function parsePath(path: string): ParsedPath {
|
||||||
let result: ParsedPath = {}
|
let result: ParsedPath = {
|
||||||
|
count: 0,
|
||||||
|
}
|
||||||
|
|
||||||
result.instance = path.match(/[/]i[/]([^/]+)/)?.[1]
|
result.instance = path.match(/[/]i[/]([^/]+)/)?.[1]
|
||||||
result.userId = path.match(/[/]u[/]([0-9]+)/)?.[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.notebook = path.match(/[/]n[/]([A-Za-z0-9_-]+)/)?.[1]
|
||||||
result.loggedIn = Boolean(path.match(/[/]l[/]/))
|
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
|
return result
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue