Add edition breadcrumb

This commit is contained in:
Kacper Donat 2021-01-06 20:51:39 +01:00
parent 1deaebda3d
commit a38409e2d0
6 changed files with 111 additions and 73 deletions

View File

@ -1,6 +1,6 @@
import React, { HTMLProps, useEffect } from 'react';
import { Link, Route, Switch } from "react-router-dom"
import { processMiddlewares, route, routes } from "@/routing";
import { processMiddlewares, route, Routes, routes } from "@/routing";
import { useSelector } from "react-redux";
import { AppState } from "@/state/reducer";
import { Trans, useTranslation } from "react-i18next";
@ -97,14 +97,7 @@ function App() {
</Container>
</header>
<main id="content">
{ <Switch>
{ routes.map(({ name, content, middlewares = [], ...route }) =>
<Route { ...route } key={ name } render={ () => {
const Next = () => processMiddlewares([ ...middlewares, content ])
return <Next />
} } />
) }
</Switch> }
<Routes routes={ routes.filter(route => !route.tags || route.tags.length == 0) }/>
</main>
<footer className="footer">
<Container style={{ display: 'flex', alignItems: "center" }}>

View File

@ -1,17 +1,17 @@
import React, { useCallback } from "react";
import React, { useCallback, useContext } from "react";
import { useTranslation } from "react-i18next";
import { useRouteMatch } from "react-router-dom";
import { useAsync } from "@/hooks";
import api from "@/management/api";
import { useRouteMatch, Link as RouterLink } from "react-router-dom";
import { Page } from "@/pages/base";
import { Container, Paper, Typography } from "@material-ui/core";
import { Container, Link, Paper, Typography } from "@material-ui/core";
import { Management, ManagementLink } from "@/management/main";
import { Edition } from "@/data/edition";
import { Async } from "@/components/async";
import { AccountMultiple, BriefcaseAccount, CertificateOutline, CogOutline, FileChartOutline, FormatPageBreak } from "mdi-material-ui";
import { route } from "@/routing";
import { route, routes, Routes } from "@/routing";
import { useSpacing } from "@/styles";
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
import { useAsync } from "@/hooks";
import api from "@/management/api";
import { Async } from "@/components/async";
const useSectionStyles = makeStyles((theme: Theme) => createStyles({
header: {
@ -23,60 +23,85 @@ const useSectionStyles = makeStyles((theme: Theme) => createStyles({
}))
export function title(edition: Edition) {
return `${edition.course.name} - ${edition.startDate.year()}`
return `${ edition.course.name } - ${ edition.startDate.year() }`
}
export const ManageEditionPage = () => {
export const EditionContext = React.createContext<Edition | null>(null);
export const EditionManagement = ({ edition }: EditionManagementProps) => {
const { t } = useTranslation("management");
const { params } = useRouteMatch();
const edition = useAsync<Edition>(useCallback(() => api.edition.details(params.edition), [params.edition]))
const spacing = useSpacing(2);
const classes = useSectionStyles();
return <Page>
<Async async={ edition }>{ edition =>
<>
<Page.Header>
<Management.Breadcrumbs>
<Typography color="textPrimary">{ title(edition) }</Typography>
</Management.Breadcrumbs>
<Page.Title>{ title(edition) }</Page.Title>
</Page.Header>
<Container className={ spacing.vertical }>
<Paper elevation={ 2 }>
<Typography className={ classes.header }>{ t("edition.manage.internships") }</Typography>
<Management.Menu>
<ManagementLink icon={ <AccountMultiple/> } route={ route("management:edition_report_form", { edition: edition.id || "" }) }>
{ t("management:edition.students.title") }
</ManagementLink>
<ManagementLink icon={ <BriefcaseAccount/> } route={ route("management:edition_report_form", { edition: edition.id || "" }) }>
{ t("management:edition.internships.title") }
</ManagementLink>
<ManagementLink icon={ <FormatPageBreak/> } route={ route("management:edition_report_form", { edition: edition.id || "" }) }>
{ t("management:edition.ipp.title") }
</ManagementLink>
<ManagementLink icon={ <FileChartOutline/> } route={ route("management:edition_report_form", { edition: edition.id || "" }) }>
{ t("management:edition.reports.title") }
</ManagementLink>
<ManagementLink icon={ <CertificateOutline/> } route={ route("management:edition_report_form", { edition: edition.id || "" }) }>
{ t("management:edition.dean-approvals.title") }
</ManagementLink>
</Management.Menu>
</Paper>
<Paper elevation={ 2 }>
<Typography className={ classes.header }>{ t("edition.manage.management") }</Typography>
<Management.Menu>
<ManagementLink icon={ <FormatPageBreak/> } route={ route("management:edition_report_form", { edition: edition.id || "" }) }>
{ t("management:edition.report-fields.title") }
</ManagementLink>
<ManagementLink icon={ <CogOutline/> } route={ route("management:edition_settings", { edition: edition.id || "" }) }>
{ t("management:edition.settings.title") }
</ManagementLink>
</Management.Menu>
</Paper>
</Container>
</>
}</Async>
<Page.Header>
<Management.Breadcrumbs>
<Typography color="textPrimary">{ title(edition) }</Typography>
</Management.Breadcrumbs>
<Page.Title>{ title(edition) }</Page.Title>
</Page.Header>
<Container className={ spacing.vertical }>
<Paper elevation={ 2 }>
<Typography className={ classes.header }>{ t("edition.manage.internships") }</Typography>
<Management.Menu>
<ManagementLink icon={ <AccountMultiple/> } route={ route("management:edition_report_form", { edition: edition.id || "" }) }>
{ t("management:edition.students.title") }
</ManagementLink>
<ManagementLink icon={ <BriefcaseAccount/> } route={ route("management:edition_report_form", { edition: edition.id || "" }) }>
{ t("management:edition.internships.title") }
</ManagementLink>
<ManagementLink icon={ <FormatPageBreak/> } route={ route("management:edition_report_form", { edition: edition.id || "" }) }>
{ t("management:edition.ipp.title") }
</ManagementLink>
<ManagementLink icon={ <FileChartOutline/> } route={ route("management:edition_report_form", { edition: edition.id || "" }) }>
{ t("management:edition.reports.title") }
</ManagementLink>
<ManagementLink icon={ <CertificateOutline/> } route={ route("management:edition_report_form", { edition: edition.id || "" }) }>
{ t("management:edition.dean-approvals.title") }
</ManagementLink>
</Management.Menu>
</Paper>
<Paper elevation={ 2 }>
<Typography className={ classes.header }>{ t("edition.manage.management") }</Typography>
<Management.Menu>
<ManagementLink icon={ <FormatPageBreak/> } route={ route("management:edition_report_form", { edition: edition.id || "" }) }>
{ t("management:edition.report-fields.title") }
</ManagementLink>
<ManagementLink icon={ <CogOutline/> } route={ route("management:edition_settings", { edition: edition.id || "" }) }>
{ t("management:edition.settings.title") }
</ManagementLink>
</Management.Menu>
</Paper>
</Container>
</Page>
}
EditionManagement.Breadcrumbs = ({ children }: { children: React.ReactChild }) => {
const edition = useContext<Edition | null>(EditionContext);
return <Management.Breadcrumbs>
{ edition && (children
? <Link to={ route("management:edition_manage", { edition: edition.id || "" }) } component={ RouterLink }>{ title(edition) }</Link>
: <Typography color="textPrimary">{ title(edition) }</Typography>
) }
{ children }
</Management.Breadcrumbs>
}
export type EditionManagementProps = {
edition: Edition;
}
export const EditionRouter = () => {
const { params } = useRouteMatch();
const edition = useAsync<Edition>(useCallback(() => api.edition.details(params.edition), [params.edition]));
return <Async async={ edition }>{
result => <EditionContext.Provider value={ result }>
<Routes routes={ routes.filter(route => (route.tags || []).includes("edition")) } edition={ result }/>
</EditionContext.Provider>
}</Async>
}

View File

@ -14,6 +14,7 @@ import { Edit } from "@material-ui/icons";
import { createPortal } from "react-dom";
import { createDeleteAction } from "@/management/common/DeleteResourceAction";
import { EditFieldDefinitionDialog } from "@/management/edition/report/fields/edit";
import { EditionManagement } from "../../manage";
const title = "edition.report-fields.title";
@ -67,9 +68,9 @@ export const EditionReportFields = () => {
return <Page>
<Page.Header maxWidth="lg">
<Management.Breadcrumbs>
<EditionManagement.Breadcrumbs>
<Typography color="textPrimary">{ t(title) }</Typography>
</Management.Breadcrumbs>
</EditionManagement.Breadcrumbs>
<Page.Title>{ t(title) }</Page.Title>
</Page.Header>
<Container maxWidth="lg">

View File

@ -14,6 +14,7 @@ import { EditionForm } from "@/management/edition/form";
import { Actions } from "@/components";
import { Save } from "@material-ui/icons";
import { Cancel } from "mdi-material-ui";
import { EditionManagement } from "./manage";
const title = "edition.settings.title";
@ -28,9 +29,9 @@ export function EditionSettings() {
return <Page>
<Page.Header maxWidth="md">
<Management.Breadcrumbs>
<EditionManagement.Breadcrumbs>
<Typography color="textPrimary">{ t(title) }</Typography>
</Management.Breadcrumbs>
</EditionManagement.Breadcrumbs>
<Page.Title>{ t(title) }</Page.Title>
</Page.Header>
<Container maxWidth="md">

View File

@ -5,16 +5,17 @@ import React from "react";
import { ManagementIndex } from "@/management/main";
import StaticPageManagement from "@/management/page/list";
import { InternshipTypeManagement } from "@/management/type/list";
import { ManageEditionPage } from "@/management/edition/manage";
import { EditionRouter, EditionManagement } from "@/management/edition/manage";
import { EditionReportFields } from "@/management/edition/report/fields/list";
import { EditionSettings } from "@/management/edition/settings";
export const managementRoutes: Route[] = ([
{ name: "index", path: "/", content: ManagementIndex, exact: true },
{ name: "edition_report_form", path: "/editions/:edition/report", content: EditionReportFields },
{ name: "edition_settings", path: "/editions/:edition/settings", content: EditionSettings },
{ name: "edition_manage", path: "/editions/:edition", content: ManageEditionPage },
{ name: "edition_router", path: "/editions/:edition", content: EditionRouter },
{ name: "edition_report_form", path: "/editions/:edition/report", content: EditionReportFields, tags: ["edition"] },
{ name: "edition_settings", path: "/editions/:edition/settings", content: EditionSettings, tags: ["edition"] },
{ name: "edition_manage", path: "/editions/:edition", content: EditionManagement, tags: ["edition"] },
{ name: "editions", path: "/editions", content: EditionsManagement },
{ name: "types", path: "/types", content: InternshipTypeManagement },

View File

@ -1,6 +1,6 @@
import React, { ReactComponentElement } from "react";
import { MainPage } from "@/pages/main";
import { RouteProps } from "react-router-dom";
import { RouteProps, Switch, Route as RouteComponent } from "react-router-dom";
import { InternshipProposalFormPage, InternshipProposalPreviewPage } from "@/pages/internship/proposal";
import { FallbackPage } from "@/pages/fallback";
import SubmitPlanPage from "@/pages/internship/plan";
@ -15,7 +15,8 @@ import SubmitReportPage from "@/pages/internship/report";
export type Route = {
name?: string;
content: () => ReactComponentElement<any>,
tags?: string[];
content: (props?: any) => ReactComponentElement<any>,
condition?: () => boolean,
middlewares?: Middleware<any, any>[],
} & RouteProps;
@ -56,7 +57,7 @@ export const routes: Route[] = [
// fallback route for 404 pages
{ name: "fallback", path: "*", content: () => <FallbackPage/> },
]
].map(route => ({ tags: [], ...route }))
const routeNameMap = new Map(routes.filter(({ name }) => !!name).map(({ name, path }) => [name, path instanceof Array ? path[0] : path])) as Map<string, string>
@ -80,3 +81,19 @@ export const query = (url: string, params: URLParams) => {
return url + (query.length > 0 ? `?${ query }` : '');
}
export type RoutesProps = {
routes: Route[];
[prop: string]: any;
};
export function Routes({ routes, ...props }: RoutesProps) {
return <Switch>
{ routes.map(({ name, content, middlewares = [], ...route }) =>
<RouteComponent { ...route } key={ name } render={ () => {
const Next = () => processMiddlewares([ ...middlewares, (_, ...props) => content(...props) ], props)
return <Next />
} } />
) }
</Switch>
}