feat: added not-found and error pages

pull/1/head
Antonio De Lucreziis 2 years ago
parent 4e417266c4
commit 478d986a7d

@ -6,24 +6,26 @@ import { ServerContext } from './hooks'
import { UserProvider } from './hooks/useCurrentUser' import { UserProvider } from './hooks/useCurrentUser'
import { AdminPage } from './pages/AdminPage' import { AdminPage } from './pages/AdminPage'
import { ErrorPage } from './pages/ErrorPage'
import { HomePage } from './pages/HomePage' import { HomePage } from './pages/HomePage'
import { LoginPage } from './pages/LoginPage' import { LoginPage } from './pages/LoginPage'
import { NotFoundPage } from './pages/NotFoundPage'
import { ProblemPage } from './pages/ProblemPage' import { ProblemPage } from './pages/ProblemPage'
import { ProfilePage } from './pages/ProfilePage' import { ProfilePage } from './pages/ProfilePage'
import { ScoresPage } from './pages/ScoresPage' import { ScoresPage } from './pages/ScoresPage'
import { UserPage } from './pages/UserPage' import { UserPage } from './pages/UserPage'
const Redirect = ({ to }: { to: string }) => { // const Redirect = ({ to }: { to: string }) => {
useEffect(() => { // useEffect(() => {
route(prependBaseUrl(to), true) // route(prependBaseUrl(to), true)
}, []) // }, [])
return ( // return (
<> // <>
Redirecting to <pre>{to}</pre>... // Redirecting to <pre>{to}</pre>...
</> // </>
) // )
} // }
export const App = ({ url }: { url?: string }) => { export const App = ({ url }: { url?: string }) => {
// during server side rendering don't prepend the BASE_URL // during server side rendering don't prepend the BASE_URL
@ -60,10 +62,13 @@ export const App = ({ url }: { url?: string }) => {
// @ts-ignore // @ts-ignore
path={pbu('/scores')} path={pbu('/scores')}
/> />
<Redirect <ErrorPage
// @ts-ignore
path={pbu('/error')}
/>
<NotFoundPage
// @ts-ignore // @ts-ignore
default default
to="/"
/> />
</Router> </Router>
</UserProvider> </UserProvider>

@ -9,7 +9,7 @@ const ROLE_LABEL: Record<UserRole, string> = {
['student']: 'Studente', ['student']: 'Studente',
} }
export const Header = ({ }) => { export const Header = ({}) => {
const [user] = useCurrentUser() const [user] = useCurrentUser()
return ( return (

@ -0,0 +1,32 @@
import { Header } from '../components/Header'
function splitFirst(s: string, sep: string): string[] {
const i = s.indexOf(sep)
if (i === -1) {
return [s, '']
} else {
return [s.substring(0, i), s.substring(i + sep.length)]
}
}
const defaultErrorMessage = <>Purtroppo c'è stato un errore inaspettato</>
export const ErrorPage = ({ url }: { url: string }) => {
const query = Object.fromEntries(
splitFirst(url, '?')[1]
.split('&')
.map(kv => splitFirst(kv, '='))
.map(([k, v]) => [k, decodeURIComponent(v)])
)
return (
<>
<Header />
<main class="page-error">
<div class="title">Errore</div>
<p>{query.message.trim().length > 0 ? query.message : defaultErrorMessage}</p>
</main>
</>
)
}

@ -0,0 +1,12 @@
import { Header } from '../components/Header'
export const NotFoundPage = ({}) => {
return (
<>
<Header />
<main class="page-not-found">
<div class="title">Pagina non trovata!</div>
</main>
</>
)
}

@ -223,7 +223,7 @@ p {
margin: 0; margin: 0;
} }
p+p { p + p {
margin-top: 0.5rem; margin-top: 0.5rem;
} }
@ -476,11 +476,11 @@ main.page-scores {
} }
&:hover { &:hover {
&:nth-child(3n + 1)+.cell+.cell::after { &:nth-child(3n + 1) + .cell + .cell::after {
background: #00000006; background: #00000006;
} }
&:nth-child(3n + 2)+.cell::after { &:nth-child(3n + 2) + .cell::after {
background: #00000006; background: #00000006;
} }
} }
@ -529,6 +529,8 @@ header {
background: #fdfdfd; background: #fdfdfd;
.logo { .logo {
z-index: 1;
font-size: 42px; font-size: 42px;
font-family: 'Lato'; font-family: 'Lato';
font-weight: 300; font-weight: 300;
@ -544,6 +546,8 @@ header {
width: 100%; width: 100%;
height: 100%; height: 100%;
z-index: 0;
.nav-left { .nav-left {
gap: 1rem; gap: 1rem;
display: flex; display: flex;
@ -797,8 +801,7 @@ header {
} }
// On mobile // On mobile
@media screen and (max-width: $device-s-width), @media screen and (max-width: $device-s-width), (pointer: coarse) {
(pointer: coarse) {
main { main {
padding: 2rem 1rem 6rem; padding: 2rem 1rem 6rem;
@ -824,16 +827,15 @@ header {
align-items: center; align-items: center;
gap: 1rem; gap: 1rem;
.nav-left, .nav-right { .nav-left,
.nav-right {
left: unset; left: unset;
right: unset; right: unset;
position: relative; position: relative;
flex-direction: column; flex-direction: column;
} }
} }
} }
.problem { .problem {
@ -874,8 +876,7 @@ header {
} }
} }
@media screen and (max-width: $device-m-width), @media screen and (max-width: $device-m-width), (pointer: coarse) {
(pointer: coarse) {
.markdown-editor { .markdown-editor {
grid-template-columns: auto; grid-template-columns: auto;
grid-template-rows: auto auto; grid-template-rows: auto auto;
@ -885,4 +886,4 @@ header {
justify-self: center; justify-self: center;
} }
} }
} }

@ -48,5 +48,13 @@
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typescript": "^4.8.4" "typescript": "^4.8.4"
},
"prettier": {
"printWidth": 140,
"singleQuote": true,
"quoteProps": "consistent",
"tabWidth": 4,
"semi": false,
"arrowParens": "avoid"
} }
} }

@ -14,7 +14,7 @@ import { RenderFunction } from './shared/ssr'
// Load ".env" // Load ".env"
dotenv.config() dotenv.config()
const HTML_ROUTES = ['/', '/login', '/problem/:id', '/u/:uid', '/admin', '/profile', '/scores'] // const HTML_ROUTES = ['/', '/login', '/problem/:id', '/u/:uid', '/admin', '/profile', '/scores']
const config = { const config = {
isDevelopment: process.env.MODE === 'development', isDevelopment: process.env.MODE === 'development',
@ -26,12 +26,12 @@ if (config.isDevelopment) {
console.log(`[Config] PORT = ${config.port}`) console.log(`[Config] PORT = ${config.port}`)
} }
function mountIndexHtmlRoutes(r: Router, serveIndexHtml: Handler) { // function mountIndexHtmlRoutes(r: Router, serveIndexHtml: Handler) {
for (const route of HTML_ROUTES) { // for (const route of HTML_ROUTES) {
console.log(`[Server] Mounted index html for "${route}"`) // console.log(`[Server] Mounted index html for "${route}"`)
r.get(route, serveIndexHtml) // r.get(route, serveIndexHtml)
} // }
} // }
async function createDevRouter() { async function createDevRouter() {
const r = express.Router() const r = express.Router()
@ -42,7 +42,8 @@ async function createDevRouter() {
}) })
r.use(vite.middlewares) r.use(vite.middlewares)
mountIndexHtmlRoutes(r, async (req, res, next) => {
r.use('*', async (req, res, next) => {
try { try {
const indexHtml = await fs.readFile(path.resolve('./index.html'), 'utf-8') const indexHtml = await fs.readFile(path.resolve('./index.html'), 'utf-8')
const transformedTemplate = await vite.transformIndexHtml(req.originalUrl, indexHtml) const transformedTemplate = await vite.transformIndexHtml(req.originalUrl, indexHtml)
@ -66,10 +67,6 @@ async function createDevRouter() {
} }
}) })
r.use('*', (_req, res) => {
res.redirect('/')
})
return r return r
} }
@ -81,7 +78,9 @@ async function createProductionRouter() {
const r = express.Router() const r = express.Router()
mountIndexHtmlRoutes(r, async (req, res) => { r.use('/', express.static('dist/entry-client'))
r.use('*', async (req, res) => {
const transformedTemplate = await fs.readFile(path.resolve('./dist/entry-client/index.html'), 'utf-8') const transformedTemplate = await fs.readFile(path.resolve('./dist/entry-client/index.html'), 'utf-8')
const { html, metadata } = render(req.originalUrl) const { html, metadata } = render(req.originalUrl)
@ -96,12 +95,6 @@ async function createProductionRouter() {
res.send(transformedTemplate.replace('<!-- INJECT META TAGS -->', metaTagsHtml).replace('<!-- SSR OUTLET -->', html)) res.send(transformedTemplate.replace('<!-- INJECT META TAGS -->', metaTagsHtml).replace('<!-- SSR OUTLET -->', html))
}) })
r.use('/', express.static('dist/entry-client'))
r.use('*', (_req, res) => {
res.redirect('/')
})
return r return r
} }

Loading…
Cancel
Save