From 999cde6726b23bb1138bd6ed496f3e1c57ba9d11 Mon Sep 17 00:00:00 2001 From: Kacper Donat <kadet1090@gmail.com> Date: Fri, 13 Nov 2020 23:57:33 +0100 Subject: [PATCH] Add static pages list --- package.json | 1 - src/api/page.ts | 2 +- src/app.tsx | 2 +- src/data/page.ts | 2 + src/management/api/dto/edition.ts | 9 ---- src/management/api/index.ts | 4 +- src/management/api/page.ts | 12 +++++ src/management/edition/list.tsx | 16 ++++--- src/management/main.tsx | 51 +++++++++++++++++++++ src/management/pages/list.tsx | 76 +++++++++++++++++++++++++++++++ src/management/routing.tsx | 9 +++- translations/management.pl.yaml | 12 ++++- translations/pl.yaml | 1 + yarn.lock | 45 +----------------- 14 files changed, 176 insertions(+), 66 deletions(-) delete mode 100644 src/management/api/dto/edition.ts create mode 100644 src/management/api/page.ts create mode 100644 src/management/main.tsx create mode 100644 src/management/pages/list.tsx diff --git a/package.json b/package.json index 43c30a5..2482de1 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/api/page.ts b/src/api/page.ts index 1a5cd02..a94def4 100644 --- a/src/api/page.ts +++ b/src/api/page.ts @@ -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 })) diff --git a/src/app.tsx b/src/app.tsx index 621b34a..95e05e1 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -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> diff --git a/src/data/page.ts b/src/data/page.ts index ca7e3c9..e775e36 100644 --- a/src/data/page.ts +++ b/src/data/page.ts @@ -5,3 +5,5 @@ export interface Page extends Identifiable { content: Multilingual<string>; slug: string; } + +export default Page; diff --git a/src/management/api/dto/edition.ts b/src/management/api/dto/edition.ts deleted file mode 100644 index a12d5b1..0000000 --- a/src/management/api/dto/edition.ts +++ /dev/null @@ -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; -} diff --git a/src/management/api/index.ts b/src/management/api/index.ts index ea6d6a7..5a3e680 100644 --- a/src/management/api/index.ts +++ b/src/management/api/index.ts @@ -1,7 +1,9 @@ import * as edition from "./edition" +import * as page from "./page" export const api = { - edition + edition, + page } export default api; diff --git a/src/management/api/page.ts b/src/management/api/page.ts new file mode 100644 index 0000000..155d607 --- /dev/null +++ b/src/management/api/page.ts @@ -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)); +} diff --git a/src/management/edition/list.tsx b/src/management/edition/list.tsx index 4c53e0a..846ef35 100644 --- a/src/management/edition/list.tsx +++ b/src/management/edition/list.tsx @@ -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"> diff --git a/src/management/main.tsx b/src/management/main.tsx new file mode 100644 index 0000000..fc91e9d --- /dev/null +++ b/src/management/main.tsx @@ -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> +} diff --git a/src/management/pages/list.tsx b/src/management/pages/list.tsx new file mode 100644 index 0000000..3a2648a --- /dev/null +++ b/src/management/pages/list.tsx @@ -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; diff --git a/src/management/routing.tsx b/src/management/routing.tsx index 510d970..bbdc7b8 100644 --- a/src/management/routing.tsx +++ b/src/management/routing.tsx @@ -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 }`, diff --git a/translations/management.pl.yaml b/translations/management.pl.yaml index f90117a..83e57f7 100644 --- a/translations/management.pl.yaml +++ b/translations/management.pl.yaml @@ -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 diff --git a/translations/pl.yaml b/translations/pl.yaml index 819cb77..020723c 100644 --- a/translations/pl.yaml +++ b/translations/pl.yaml @@ -223,3 +223,4 @@ validation: contact-coordinator: "Skontaktuj się z koordynatorem" download: "pobierz" +management: "zarządzanie" diff --git a/yarn.lock b/yarn.lock index a27287f..5f3d70b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"