Add static pages list

This commit is contained in:
Kacper Donat 2020-11-13 23:57:33 +01:00
parent 69223141a4
commit 999cde6726
14 changed files with 176 additions and 66 deletions

View File

@ -7,7 +7,6 @@
"@babel/preset-typescript": "^7.10.1",
"@date-io/moment": "^1.3.13",
"@material-ui/core": "^4.10.1",
"@material-ui/data-grid": "^4.0.0-alpha.9",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "^4.0.0-alpha.55",
"@material-ui/pickers": "^3.2.10",

View File

@ -3,7 +3,7 @@ import { PageDTO, pageDtoTransformer } from "./dto/page"
import { axios } from "@/api/index";
import { prepare } from "@/routing";
const STATIC_PAGE_ENDPOINT = "/staticPage/:slug"
export const STATIC_PAGE_ENDPOINT = "/staticPage/:slug"
export async function get(slug: string): Promise<Page> {
const response = await axios.get<PageDTO>(prepare(STATIC_PAGE_ENDPOINT, { slug }))

View File

@ -104,7 +104,7 @@ function App() {
<footer className="footer">
<Container style={{ display: 'flex', alignItems: "center" }}>
<ul className="footer__menu">
<li><Link to="/management">zarządzanie</Link></li>
<li><Link to="/management">{ t("management") }</Link></li>
</ul>
<div className="footer__copyright">{ t('copyright', { date: moment() }) }</div>
</Container>

View File

@ -5,3 +5,5 @@ export interface Page extends Identifiable {
content: Multilingual<string>;
slug: string;
}
export default Page;

View File

@ -1,9 +0,0 @@
import { Identifiable } from "@/data";
import { CourseDTO } from "@/api/dto/course";
export interface EditionDTO extends Identifiable {
editionStart: string;
editionFinish: string;
reportingStart: string;
course: CourseDTO;
}

View File

@ -1,7 +1,9 @@
import * as edition from "./edition"
import * as page from "./page"
export const api = {
edition
edition,
page
}
export default api;

View File

@ -0,0 +1,12 @@
import { Page } from "@/data/page";
import pageDtoTransformer, { PageDTO } from "@/api/dto/page";
import { axios } from "@/api";
const STATIC_PAGE_INDEX_ENDPOINT = "/staticPage";
export { get, STATIC_PAGE_ENDPOINT } from "@/api/page"
export async function all(): Promise<Page[]> {
const response = await axios.get<PageDTO[]>(STATIC_PAGE_INDEX_ENDPOINT);
return response.data.map(dto => pageDtoTransformer.transform(dto));
}

View File

@ -4,10 +4,11 @@ import { useTranslation } from "react-i18next";
import { useAsync } from "@/hooks";
import api from "@/management/api";
import { Async } from "@/components/async";
import { Container } from "@material-ui/core";
import { Container, Typography } from "@material-ui/core";
import MaterialTable, { Action, Column } from "material-table";
import { Edition } from "@/data/edition";
import { Pencil } from "mdi-material-ui";
import { Management } from "../main";
export type EditionDetailsProps = {
edition: string;
@ -19,28 +20,28 @@ export function EditionDetails({ edition, ...props }: EditionDetailsProps) {
return <Async async={ result }>{ edition => <pre>{ JSON.stringify(edition, null, 2) }</pre> }</Async>
}
export function ManagementEditionsList() {
export function EditionsManagement() {
const { t } = useTranslation("management");
const editions = useAsync(useCallback(api.edition.all, []));
const columns: Column<Edition>[] = [
{
title: t("edition.fields.id"),
title: t("edition.field.id"),
field: "id",
cellStyle: { whiteSpace: "nowrap" }
},
{
title: t("edition.fields.start"),
title: t("edition.field.start"),
render: edition => edition.startDate.format("DD.MM.yyyy"),
customSort: (a, b) => b.startDate.unix() - a.startDate.unix(),
},
{
title: t("edition.fields.end"),
title: t("edition.field.end"),
render: edition => edition.endDate.format("DD.MM.yyyy"),
customSort: (a, b) => b.endDate.unix() - a.endDate.unix(),
},
{
title: t("edition.fields.course"),
title: t("edition.field.course"),
customSort: (a, b) => a.course.name.localeCompare(b.course.name),
render: edition => edition.course.name,
},
@ -55,6 +56,9 @@ export function ManagementEditionsList() {
return <Page>
<Page.Header maxWidth="lg">
<Management.Breadcrumbs>
<Typography color="textPrimary">{ t("edition.index.title") }</Typography>
</Management.Breadcrumbs>
<Page.Title>{ t("edition.index.title") }</Page.Title>
</Page.Header>
<Container maxWidth="lg">

51
src/management/main.tsx Normal file
View File

@ -0,0 +1,51 @@
import { BreadcrumbsProps, Container, Link, List, ListItem, ListItemIcon, ListItemText, Paper } from "@material-ui/core";
import { Page } from "@/pages/base";
import React from "react";
import { Link as RouterLink } from "react-router-dom";
import { route } from "@/routing";
import { useTranslation } from "react-i18next";
import { CalendarClock, FileDocumentMultipleOutline } from "mdi-material-ui";
export const Management = {
Breadcrumbs: ({ children, ...props }: BreadcrumbsProps) => {
const { t } = useTranslation();
return <Page.Breadcrumbs { ...props }>
<Link component={ RouterLink } to={ route("management:index") }>{ t("management:title") }</Link>
{ children }
</Page.Breadcrumbs>;
}
}
type ManagementLinkProps = React.PropsWithChildren<{
icon: JSX.Element,
route: string,
}>;
const ManagementLink = ({ icon, route, children }: ManagementLinkProps) =>
<ListItem button component={ RouterLink } to={ route }>
<ListItemIcon>{ icon }</ListItemIcon>
<ListItemText>{ children }</ListItemText>
</ListItem>
export const ManagementIndex = () => {
const { t } = useTranslation();
return <Page>
<Page.Header>
<Page.Title>{ t("management:title") }</Page.Title>
</Page.Header>
<Container>
<Paper elevation={ 2 }>
<List>
<ManagementLink icon={ <CalendarClock /> } route={ route("management:editions") }>
{ t("management:edition.index.title") }
</ManagementLink>
<ManagementLink icon={ <FileDocumentMultipleOutline /> } route={ route("management:static_pages") }>
{ t("management:page.index.title") }
</ManagementLink>
</List>
</Paper>
</Container>
</Page>
}

View File

@ -0,0 +1,76 @@
import { Page } from "@/pages/base";
import { Management } from "@/management/main";
import { Box, Container, Typography } from "@material-ui/core";
import React, { useCallback } from "react";
import { useTranslation } from "react-i18next";
import { useAsync } from "@/hooks";
import api from "@/management/api";
import { Async } from "@/components/async";
import MaterialTable, { Action, Column } from "material-table";
import { default as StaticPage } from "@/data/page";
import { Edition } from "@/data/edition";
import { Delete, Pencil } from "mdi-material-ui";
export const StaticPageManagement = () => {
const { t } = useTranslation("management");
const pages = useAsync(useCallback(api.page.all, []));
const columns: Column<StaticPage>[] = [
{
render: page => page.title.pl,
title: t("page.field.title"),
},
{
field: "slug",
title: t("page.field.slug"),
},
];
const actions: Action<StaticPage>[] = [
{
icon: () => <Pencil />,
onClick: () => {},
},
{
icon: () => <Delete />,
onClick: () => {},
},
]
const PagePreview = ({ page }: { page: StaticPage }) =>
<>
<Box p={2}>
<Typography variant="subtitle2">Polski</Typography>
<Typography variant="h2">{ page.title.pl }</Typography>
<div dangerouslySetInnerHTML={{ __html: page.content.pl }} />
</Box>
<Box p={2}>
<Typography variant="subtitle2">English</Typography>
<Typography variant="h2">{ page.title.en }</Typography>
<div dangerouslySetInnerHTML={{ __html: page.content.en }} />
</Box>
</>
return <Page>
<Page.Header maxWidth="lg">
<Management.Breadcrumbs>
<Typography color="textPrimary">{ t("page.index.title") }</Typography>
</Management.Breadcrumbs>
<Page.Title>{ t("page.index.title") }</Page.Title>
</Page.Header>
<Container maxWidth="lg">
<Async async={ pages }>{
pages => <MaterialTable
title={ t("page.index.title") }
columns={ columns }
actions={ actions }
data={ pages }
detailPanel={ page => <PagePreview page={ page } /> }
options={{ actionsColumnIndex: -1 }}
/>
}</Async>
</Container>
</Page>
}
export default StaticPageManagement;

View File

@ -1,10 +1,15 @@
import { Route } from "@/routing";
import { isManagerMiddleware } from "@/management/middleware";
import { ManagementEditionsList } from "@/management/edition/list";
import { EditionsManagement } from "@/management/edition/list";
import React from "react";
import { ManagementIndex } from "@/management/main";
import StaticPageManagement from "@/management/pages/list";
export const managementRoutes: Route[] = ([
{ name: "editions_index", path: "/editions", content: ManagementEditionsList }
{ name: "index", path: "/", content: ManagementIndex, exact: true },
{ name: "editions", path: "/editions", content: EditionsManagement },
{ name: "static_pages", path: "/static-pages", content: StaticPageManagement }
] as Route[]).map(
({ name, path, middlewares = [], ...route }): Route => ({
name: `management:${ name }`,

View File

@ -1,8 +1,18 @@
title: Zarządzanie
edition:
index:
title: "Edycje praktyk"
fields:
field:
id: Identyfikator
start: Początek
end: Koniec
course: Kierunek
page:
index:
title: Strony statyczne
field:
title: Tytuł
content: Treść
slug: Adres

View File

@ -223,3 +223,4 @@ validation:
contact-coordinator: "Skontaktuj się z koordynatorem"
download: "pobierz"
management: "zarządzanie"

View File

@ -1072,16 +1072,6 @@
react-is "^16.8.0"
react-transition-group "^4.4.0"
"@material-ui/data-grid@^4.0.0-alpha.9":
version "4.0.0-alpha.9"
resolved "https://registry.yarnpkg.com/@material-ui/data-grid/-/data-grid-4.0.0-alpha.9.tgz#a5f48550ea7c1ebdbcde9c7e0889ec9600473fec"
integrity sha512-2aXKQI6BH800i1AQzR3+p2eEET4ejVeGFlJQXBFvzBGodBW/AGfT2WsJGkmkriiVvD6jpsNsC5XF0HMAAcIFPA==
dependencies:
"@material-ui/utils" "^5.0.0-alpha.14"
prop-types "^15.7.2"
reselect "^4.0.0"
tslib "^2.0.0"
"@material-ui/icons@^4.9.1":
version "4.9.1"
resolved "https://registry.yarnpkg.com/@material-ui/icons/-/icons-4.9.1.tgz#fdeadf8cb3d89208945b33dbc50c7c616d0bd665"
@ -1158,17 +1148,6 @@
prop-types "^15.7.2"
react-is "^16.8.0"
"@material-ui/utils@^5.0.0-alpha.14":
version "5.0.0-alpha.15"
resolved "https://registry.yarnpkg.com/@material-ui/utils/-/utils-5.0.0-alpha.15.tgz#b3ff9ae27f0b1ba60fe85ed318c49e4796d7eece"
integrity sha512-hxzqf4W7FVnVWB0x/+FKUwdcR3YUyoe7SeDBdsqPTkcUstU7RvrJztCAhybmeT3nTRQBd28HKGguM6oSCbboUw==
dependencies:
"@babel/runtime" "^7.4.4"
"@types/prop-types" "^15.7.3"
"@types/react-is" "^16.7.1 || ^17.0.0"
prop-types "^15.7.2"
react-is "^16.8.0 || ^17.0.0"
"@mrmlnc/readdir-enhanced@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
@ -1243,7 +1222,7 @@
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
"@types/prop-types@*", "@types/prop-types@^15.7.3":
"@types/prop-types@*":
version "15.7.3"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
@ -1265,13 +1244,6 @@
dependencies:
"@types/react" "*"
"@types/react-is@^16.7.1 || ^17.0.0":
version "16.7.1"
resolved "https://registry.yarnpkg.com/@types/react-is/-/react-is-16.7.1.tgz#d3f1c68c358c00ce116b55ef5410cf486dd08539"
integrity sha512-dMLFD2cCsxtDgMkTydQCM0PxDq8vwc6uN5M/jRktDfYvH3nQj6pjC9OrCXS2lKlYoYTNJorI/dI8x9dpLshexQ==
dependencies:
"@types/react" "*"
"@types/react-redux@^7.1.9":
version "7.1.9"
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.9.tgz#280c13565c9f13ceb727ec21e767abe0e9b4aec3"
@ -7763,11 +7735,6 @@ react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-i
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
"react-is@^16.8.0 || ^17.0.0":
version "17.0.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339"
integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==
react-moment@^0.9.7:
version "0.9.7"
resolved "https://registry.yarnpkg.com/react-moment/-/react-moment-0.9.7.tgz#ca570466595b1aa4f7619e62da18b3bb2de8b6f3"
@ -8095,11 +8062,6 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
reselect@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.0.0.tgz#f2529830e5d3d0e021408b246a206ef4ea4437f7"
integrity sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA==
resolve-cwd@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"
@ -9146,11 +9108,6 @@ tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==
tslib@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c"
integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==
tsutils@^3.17.1:
version "3.17.1"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759"