11import * as React from "react" ;
2- import { useLocation } from 'react-router-dom' ;
2+ import { useLocation , useParams } from 'react-router-dom' ;
33import * as R from "remeda" ;
44
55import { CircularProgress } from '@mui/material' ;
66import { ErrorBoundary , Suspense } from '@suspensive/react' ;
77
8- import styled from '@emotion/styled' ;
8+ import { AxiosError , AxiosResponse } from 'axios' ;
9+ import { BackendAPIClientError } from '../apis/client' ;
910import Hooks from "../hooks" ;
1011import Schemas from "../schemas" ;
1112import Utils from '../utils' ;
@@ -14,67 +15,87 @@ import { MDXRenderer } from './mdx';
1415
1516const InitialPageStyle : React . CSSProperties = {
1617 width : '100%' ,
17- height : '100%' ,
1818 display : 'flex' ,
1919 justifyContent : 'center' ,
2020 alignItems : 'center' ,
2121 flexDirection : 'column' ,
22- }
22+ } ;
2323
2424const InitialSectionStyle : React . CSSProperties = {
2525 width : '100%' ,
26- }
26+ } ;
27+
28+ const LoginRequired : React . FC = ( ) => < > 401 Login Required</ > ;
29+ const PermissionDenied : React . FC = ( ) => < > 403 Permission Denied</ > ;
30+ const PageNotFound : React . FC = ( ) => < > 404 Not Found</ > ;
2731
28- export const PageRenderer : React . FC < { id : string } > = ( { id } ) => {
29- const { data } = Hooks . BackendAPI . usePageQuery ( id ) ;
32+ const throwPageNotFound = ( message : string ) => {
33+ const errorStr = `RouteRenderer: ${ message } ` ;
34+ const axiosError = new AxiosError ( errorStr , errorStr , undefined , undefined , { status : 404 } as AxiosResponse ) ;
35+ throw new BackendAPIClientError ( axiosError ) ;
36+ } ;
3037
31- return < div style = { { ... InitialPageStyle , ... Utils . parseCss ( data . css ) } } >
32- {
33- data . sections . map (
34- ( s ) => < div style = { { ... InitialSectionStyle , ... Utils . parseCss ( s . css ) } } key = { s . id } >
35- < MDXRenderer text = { s . body } />
36- </ div >
37- )
38+ const RouteErrorFallback : React . FC < { error : Error , reset : ( ) => void } > = ( { error , reset } ) => {
39+ if ( error instanceof BackendAPIClientError ) {
40+ switch ( error . status ) {
41+ case 401 : return < LoginRequired /> ;
42+ case 403 : return < PermissionDenied /> ;
43+ case 404 : return < PageNotFound /> ;
44+ default : return < ErrorFallback error = { error } reset = { reset } /> ;
3845 }
39- </ div >
46+ }
47+ return < ErrorFallback error = { error } reset = { reset } /> ;
4048} ;
4149
42- const AsyncDynamicRoutePage : React . FC = ( ) => {
43- const location = useLocation ( ) ;
44- const { data } = Hooks . BackendAPI . useFlattenSiteMapQuery ( ) ;
45- const nestedSiteMap = Utils . buildNestedSiteMap ( data ) ;
50+ export const PageRenderer : React . FC < { id ?: string } > = ErrorBoundary . with (
51+ { fallback : RouteErrorFallback } ,
52+ Suspense . with (
53+ { fallback : < CircularProgress /> } ,
54+ ( { id } ) => {
55+ const backendClient = Hooks . BackendAPI . useBackendClient ( ) ;
56+ const { data } = Hooks . BackendAPI . usePageQuery ( backendClient , id || '' ) ;
57+
58+ return < div style = { { ...InitialPageStyle , ...Utils . parseCss ( data . css ) } } >
59+ {
60+ data . sections . map (
61+ ( s ) => < div style = { { ...InitialSectionStyle , ...Utils . parseCss ( s . css ) } } key = { s . id } >
62+ < MDXRenderer text = { s . body } />
63+ </ div >
64+ )
65+ }
66+ </ div >
67+ }
68+ )
69+ ) ;
4670
47- const currentRouteCodes = [ '' , ...location . pathname . split ( '/' ) . filter ( ( code ) => ! R . isEmpty ( code ) ) ] ;
71+ export const RouteRenderer : React . FC = ErrorBoundary . with (
72+ { fallback : RouteErrorFallback } ,
73+ Suspense . with (
74+ { fallback : < CircularProgress /> } ,
75+ ( ) => {
76+ const location = useLocation ( ) ;
4877
49- let currentSitemap : Schemas . NestedSiteMapSchema | null | undefined = nestedSiteMap [ currentRouteCodes [ 0 ] ] ;
50- if ( currentSitemap === undefined ) {
51- return < > 404 Not Found</ > ;
52- }
78+ const backendClient = Hooks . BackendAPI . useBackendClient ( ) ;
79+ const { data } = Hooks . BackendAPI . useFlattenSiteMapQuery ( backendClient ) ;
80+ const nestedSiteMap = Utils . buildNestedSiteMap ( data ) ;
5381
54- for ( const routeCode of currentRouteCodes . slice ( 1 ) ) {
55- if ( ( currentSitemap = currentSitemap . children [ routeCode ] ) === undefined ) {
56- break ;
57- }
58- }
82+ const currentRouteCodes = [ '' , ...location . pathname . split ( '/' ) . filter ( ( code ) => ! R . isEmpty ( code ) ) ] ;
83+ let currentSitemap : Schemas . NestedSiteMapSchema | undefined = nestedSiteMap [ currentRouteCodes [ 0 ] ] ;
84+ if ( currentSitemap === undefined ) throwPageNotFound ( `Route ${ location } not found` ) ;
5985
60- return R . isNullish ( currentSitemap )
61- ? < > 404 Not Found</ >
62- : < PageRenderer id = { currentSitemap . page } />
63- }
86+ for ( const routeCode of currentRouteCodes . slice ( 1 ) )
87+ if ( ( currentSitemap = currentSitemap . children [ routeCode ] ) === undefined )
88+ throwPageNotFound ( `Route ${ location } not found` ) ;
6489
65- const FullPage = styled . div `
66- width: 100%;
67- height: 100%;
68- display: flex;
69- justify-content: center;
70- align-items: center;
71- flex-direction: column;
72- `
90+ return < PageRenderer id = { currentSitemap . page } />
91+ }
92+ )
93+ ) ;
7394
74- export const DynamicRoutePage : React . FC = ( ) => < FullPage >
75- < ErrorBoundary fallback = { ErrorFallback } >
76- < Suspense fallback = { < CircularProgress /> } >
77- < AsyncDynamicRoutePage />
78- </ Suspense >
79- </ ErrorBoundary >
80- </ FullPage > ;
95+ export const PageIdParamRenderer : React . FC = Suspense . with (
96+ { fallback : < CircularProgress /> } ,
97+ ( ) => {
98+ const { id } = useParams ( ) ;
99+ return < PageRenderer id = { id } / >
100+ }
101+ ) ;
0 commit comments