From 774e89a3ec3089761854949c0bcbd44454987e3e Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Thu, 9 Jul 2020 21:33:14 +0200 Subject: [PATCH 01/12] Add front page and basic routing --- package.json | 4 ++ src/app.tsx | 15 +++--- src/pages/base.tsx | 16 ++++++ src/pages/main.tsx | 56 +++++++++++++++++++++ src/routing.tsx | 25 ++++++++++ src/ui/theme.ts | 3 +- webpack.config.js | 1 + yarn.lock | 118 +++++++++++++++++++++++++++++++++++++++++++-- 8 files changed, 224 insertions(+), 14 deletions(-) create mode 100644 src/pages/base.tsx create mode 100644 src/pages/main.tsx create mode 100644 src/routing.tsx diff --git a/package.json b/package.json index 705fa02..6880974 100644 --- a/package.json +++ b/package.json @@ -9,15 +9,18 @@ "@material-ui/core": "^4.10.1", "@material-ui/lab": "^4.0.0-alpha.55", "@material-ui/pickers": "^3.2.10", + "@types/classnames": "^2.2.10", "@types/node": "^12.0.0", "@types/react": "^16.9.0", "@types/react-dom": "^16.9.0", + "@types/react-router-dom": "^5.1.5", "@typescript-eslint/eslint-plugin": "^2.10.0", "@typescript-eslint/parser": "^2.10.0", "babel-core": "^6.26.3", "babel-loader": "^8.1.0", "babel-plugin-import": "^1.13.0", "babel-preset-react-app": "^9.1.2", + "classnames": "^2.2.6", "clean-webpack-plugin": "^3.0.0", "css-loader": "3.4.2", "date-holidays": "^1.5.3", @@ -35,6 +38,7 @@ "react-app-polyfill": "^1.0.6", "react-dev-utils": "^10.2.1", "react-dom": "^16.13.1", + "react-router-dom": "^5.2.0", "sass-loader": "8.0.2", "style-loader": "0.23.1", "ts-loader": "^7.0.5", diff --git a/src/app.tsx b/src/app.tsx index 009dce1..db5e4cf 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -5,12 +5,14 @@ import { studentTheme } from "./ui/theme"; import { InternshipForm } from "@/forms/Internship"; import { MuiPickersUtilsProvider } from "@material-ui/pickers"; import MomentUtils from "@date-io/moment"; +import { BrowserRouter, Switch, Route } from "react-router-dom" import "moment/locale/pl" moment.locale("pl") import '@/styles/overrides.scss' import moment, { Moment } from "moment"; +import { routes } from "@/routing"; class LocalizedMomentUtils extends MomentUtils { getDatePickerHeaderText(date: Moment): string { @@ -23,14 +25,11 @@ function App() { -
- - Zgłoszenie Praktyki - UX Demo - - - -
+ +
+ { routes.map(({ name, content, ...route }) => { content() }) } +
+
diff --git a/src/pages/base.tsx b/src/pages/base.tsx new file mode 100644 index 0000000..7d487d4 --- /dev/null +++ b/src/pages/base.tsx @@ -0,0 +1,16 @@ +import React, { HTMLProps, useEffect } from "react"; +import classNames from "classnames" + +export type PageProps = { + title?: string; +} & HTMLProps; + +export const Page = ({ title, children, ...props }: PageProps) => { + useEffect(() => { + document.title = title ? title + " | Praktyka Studencka" : "Praktyka Studencka"; + }, [ title ]) + + return
+ { children } +
+} diff --git a/src/pages/main.tsx b/src/pages/main.tsx new file mode 100644 index 0000000..9f94398 --- /dev/null +++ b/src/pages/main.tsx @@ -0,0 +1,56 @@ +import React from "react"; +import { Page } from "@/pages/base"; +import { Container, Typography, Button, Divider, Box, Stepper, Step, StepLabel, StepContent } from "@material-ui/core"; +import { InternshipForm } from "@/forms/Internship"; +import { Link } from "react-router-dom"; +import { route } from "@/routing"; + +export const MainPage = () => { + return + + + + + Uzupełnienie danych w systemie + + do: 05.07.2020 + + + lol + + + Zgłoszenie praktyki + + + + + + + +} + +export const InternshipFormPage = () => { + return + + Zgłoszenie Praktyki + UX Demo + + + + +} + +export const NotFoundPage = () => { + return + + 404 + Strona nie została znaleziona + + + + + + + + +} diff --git a/src/routing.tsx b/src/routing.tsx new file mode 100644 index 0000000..2137bfa --- /dev/null +++ b/src/routing.tsx @@ -0,0 +1,25 @@ +import React, { ReactComponentElement } from "react"; +import { InternshipFormPage, MainPage, NotFoundPage } from "@/pages/main"; +import { RouteProps } from "react-router-dom"; + +type Route = { + name?: string; + content: () => ReactComponentElement, +} & RouteProps; + +export const routes: Route[] = [ + { name: "home", path: "/", exact: true, content: () => }, + + { name: "internship_proposal", path: "/internship/proposal", exact: true, content: () => }, + + // fallback route for 404 pages + { path: "*", content: () => } +] + +const routeNameMap = new Map(routes.filter(({name}) => !!name).map(({ name, path }) => [ name, path instanceof Array ? path[0] : path ])) as Map + +export function route(name: string, params: { [param: string]: string } = { }) { + const url = routeNameMap.get(name) || ""; + + return Object.entries(params).reduce((url, [name, value]) => url.replace(`:${name}`, value), url); +} diff --git a/src/ui/theme.ts b/src/ui/theme.ts index 31471dc..a6286bd 100644 --- a/src/ui/theme.ts +++ b/src/ui/theme.ts @@ -6,5 +6,6 @@ export const studentTheme = createMuiTheme({ spacing: 3, xs: 12, } - } + }, + spacing: 8 }) diff --git a/webpack.config.js b/webpack.config.js index 0c6a61f..916d8ea 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -48,6 +48,7 @@ const config = { port: 3000, host: 'system-praktyk-front.localhost', disableHostCheck: true, + historyApiFallback: true, }, optimization: { usedExports: true diff --git a/yarn.lock b/yarn.lock index 7316358..0302808 100644 --- a/yarn.lock +++ b/yarn.lock @@ -919,7 +919,7 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": version "7.10.2" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.2.tgz#d103f21f2602497d38348a32e008637d506db839" integrity sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg== @@ -1123,6 +1123,11 @@ resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA== +"@types/classnames@^2.2.10": + version "2.2.10" + resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.10.tgz#cc658ca319b6355399efc1f5b9e818f1a24bf999" + integrity sha512-1UzDldn9GfYYEsWWnn/P4wkTlkZDH7lDb0wBMGbtIQc9zXEQq7FlKBdZUn6OBqD8sKZZ2RQO2mAjGpXiDGoRmQ== + "@types/eslint-visitor-keys@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" @@ -1136,6 +1141,11 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/history@*": + version "4.7.6" + resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.6.tgz#ed8fc802c45b8e8f54419c2d054e55c9ea344356" + integrity sha512-GRTZLeLJ8ia00ZH8mxMO8t0aC9M1N9bN461Z2eaRurJo6Fpa+utgCwLzI4jQHcrdzuzp5WPN9jRwpsCQ1VhJ5w== + "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.4": version "7.0.5" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd" @@ -1178,6 +1188,23 @@ dependencies: "@types/react" "*" +"@types/react-router-dom@^5.1.5": + version "5.1.5" + resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.5.tgz#7c334a2ea785dbad2b2dcdd83d2cf3d9973da090" + integrity sha512-ArBM4B1g3BWLGbaGvwBGO75GNFbLDUthrDojV2vHLih/Tq8M+tgvY1DSwkuNrPSwdp/GUL93WSEpTZs8nVyJLw== + dependencies: + "@types/history" "*" + "@types/react" "*" + "@types/react-router" "*" + +"@types/react-router@*": + version "5.1.8" + resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.8.tgz#4614e5ba7559657438e17766bb95ef6ed6acc3fa" + integrity sha512-HzOyJb+wFmyEhyfp4D4NYrumi+LQgQL/68HvJO+q6XtuHSDvw6Aqov7sCAhjbNq3bUPgPqbdvjXC5HeB2oEAPg== + dependencies: + "@types/history" "*" + "@types/react" "*" + "@types/react-transition-group@^4.2.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.0.tgz#882839db465df1320e4753e6e9f70ca7e9b4d46d" @@ -2407,6 +2434,11 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +classnames@^2.2.6: + version "2.2.6" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" + integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== + clean-css@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78" @@ -4240,6 +4272,18 @@ hex-color-regex@^1.1.0: resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== +history@^4.9.0: + version "4.10.1" + resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" + integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew== + dependencies: + "@babel/runtime" "^7.1.2" + loose-envify "^1.2.0" + resolve-pathname "^3.0.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + value-equal "^1.0.1" + hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -4249,7 +4293,7 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^3.3.2: +hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -4905,6 +4949,11 @@ is-wsl@^2.1.1: dependencies: is-docker "^2.0.0" +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -5301,7 +5350,7 @@ loglevel@^1.6.6: resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.8.tgz#8a25fb75d092230ecd4457270d80b54e28011171" integrity sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA== -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -5522,6 +5571,14 @@ mimic-fn@^2.0.0, mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mini-create-react-context@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.0.tgz#df60501c83151db69e28eac0ef08b4002efab040" + integrity sha512-b0TytUgFSbgFJGzJqXPKCFCBWigAjpjo+Fl7Vf7ZbKRDptszpppKxXH6DRXEABZ/gcEQczeb0iZ7JvL8e8jjCA== + dependencies: + "@babel/runtime" "^7.5.5" + tiny-warning "^1.0.3" + minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" @@ -6220,6 +6277,13 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= +path-to-regexp@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" + integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== + dependencies: + isarray "0.0.1" + path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -7234,11 +7298,40 @@ react-error-overlay@^6.0.7: resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108" integrity sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA== -react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1: +react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-router-dom@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662" + integrity sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA== + dependencies: + "@babel/runtime" "^7.1.2" + history "^4.9.0" + loose-envify "^1.3.1" + prop-types "^15.6.2" + react-router "5.2.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + +react-router@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.2.0.tgz#424e75641ca8747fbf76e5ecca69781aa37ea293" + integrity sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw== + dependencies: + "@babel/runtime" "^7.1.2" + history "^4.9.0" + hoist-non-react-statics "^3.1.0" + loose-envify "^1.3.1" + mini-create-react-context "^0.4.0" + path-to-regexp "^1.7.0" + prop-types "^15.6.2" + react-is "^16.6.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + react-transition-group@^4.0.0, react-transition-group@^4.4.0: version "4.4.1" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9" @@ -7512,6 +7605,11 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +resolve-pathname@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" + integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -8380,7 +8478,12 @@ timsort@^0.3.0: resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= -tiny-warning@^1.0.2: +tiny-invariant@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" + integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw== + +tiny-warning@^1.0.0, tiny-warning@^1.0.2, tiny-warning@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== @@ -8724,6 +8827,11 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" +value-equal@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" + integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== + vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" -- 2.45.2 From 2eabcc8b93f5fc2e5038a6687fe2fb68c3a9af70 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Fri, 10 Jul 2020 20:10:31 +0200 Subject: [PATCH 02/12] Add new theme based on new GUT style guide --- public/img/pg-logotyp.svg | 1 + public/index.html | 1 + src/app.tsx | 23 ++++++++++++++++------- src/pages/base.tsx | 17 +++++++++++++++++ src/pages/main.tsx | 18 +++++++++++------- src/styles/_variables.scss | 5 +++++ src/styles/header.scss | 29 +++++++++++++++++++++++++++++ src/styles/overrides.scss | 5 +++-- src/styles/page.scss | 14 ++++++++++++++ src/ui/theme.ts | 9 +++++++++ 10 files changed, 106 insertions(+), 16 deletions(-) create mode 100644 public/img/pg-logotyp.svg create mode 100644 src/styles/_variables.scss create mode 100644 src/styles/header.scss create mode 100644 src/styles/page.scss diff --git a/public/img/pg-logotyp.svg b/public/img/pg-logotyp.svg new file mode 100644 index 0000000..3cf3f61 --- /dev/null +++ b/public/img/pg-logotyp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/index.html b/public/index.html index 6482fd8..076a951 100644 --- a/public/index.html +++ b/public/index.html @@ -7,6 +7,7 @@ Zgłoszenie praktyki studenckiej + diff --git a/src/app.tsx b/src/app.tsx index db5e4cf..c9b9018 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,18 +1,18 @@ import React from 'react'; -import { Container, Typography } from "@material-ui/core"; import { MuiThemeProvider as ThemeProvider, StylesProvider } from "@material-ui/core/styles"; import { studentTheme } from "./ui/theme"; -import { InternshipForm } from "@/forms/Internship"; import { MuiPickersUtilsProvider } from "@material-ui/pickers"; import MomentUtils from "@date-io/moment"; -import { BrowserRouter, Switch, Route } from "react-router-dom" +import { BrowserRouter, Switch, Route, Link } from "react-router-dom" import "moment/locale/pl" moment.locale("pl") import '@/styles/overrides.scss' +import '@/styles/header.scss' + import moment, { Moment } from "moment"; -import { routes } from "@/routing"; +import { route, routes } from "@/routing"; class LocalizedMomentUtils extends MomentUtils { getDatePickerHeaderText(date: Moment): string { @@ -26,9 +26,18 @@ function App() { -
- { routes.map(({ name, content, ...route }) => { content() }) } -
+
+ +
+ + +
+
+ { routes.map(({ name, content, ...route }) => { content() }) }
diff --git a/src/pages/base.tsx b/src/pages/base.tsx index 7d487d4..b8f1ff3 100644 --- a/src/pages/base.tsx +++ b/src/pages/base.tsx @@ -1,10 +1,16 @@ import React, { HTMLProps, useEffect } from "react"; import classNames from "classnames" +import { Breadcrumbs, BreadcrumbsProps, Container, Typography } from "@material-ui/core"; +import "@/styles/page.scss" export type PageProps = { title?: string; } & HTMLProps; +export type PageHeaderProps = { + maxWidth?: "sm" | "md" | "lg" | false +} & HTMLProps + export const Page = ({ title, children, ...props }: PageProps) => { useEffect(() => { document.title = title ? title + " | Praktyka Studencka" : "Praktyka Studencka"; @@ -14,3 +20,14 @@ export const Page = ({ title, children, ...props }: PageProps) => { { children } } + +Page.Header = ({ children, maxWidth = false, ...props }: PageHeaderProps) => +
+ + { children } + +
+ +Page.Title = ({ children }: HTMLProps) => { children } + +Page.Breadcrumbs = ({ children, ...props }: BreadcrumbsProps) => { children } diff --git a/src/pages/main.tsx b/src/pages/main.tsx index 9f94398..8fcb80d 100644 --- a/src/pages/main.tsx +++ b/src/pages/main.tsx @@ -1,8 +1,8 @@ import React from "react"; import { Page } from "@/pages/base"; -import { Container, Typography, Button, Divider, Box, Stepper, Step, StepLabel, StepContent } from "@material-ui/core"; +import { Container, Typography, Button, Divider, Box, Stepper, Step, StepLabel, StepContent, Link } from "@material-ui/core"; import { InternshipForm } from "@/forms/Internship"; -import { Link } from "react-router-dom"; +import { Link as RouterLink } from "react-router-dom"; import { route } from "@/routing"; export const MainPage = () => { @@ -21,7 +21,7 @@ export const MainPage = () => { Zgłoszenie praktyki - + @@ -31,10 +31,14 @@ export const MainPage = () => { export const InternshipFormPage = () => { return + + + Moja praktyka + Zgłoszenie praktyki + + Zgłoszenie praktyki + - Zgłoszenie Praktyki - UX Demo - @@ -50,7 +54,7 @@ export const NotFoundPage = () => { - + } diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss new file mode 100644 index 0000000..520af2e --- /dev/null +++ b/src/styles/_variables.scss @@ -0,0 +1,5 @@ +$brand: #043865; + +$main: #052f52; +$main-dark: #072138; + diff --git a/src/styles/header.scss b/src/styles/header.scss new file mode 100644 index 0000000..b13891b --- /dev/null +++ b/src/styles/header.scss @@ -0,0 +1,29 @@ +@import "variables"; + +.header { + height: 110px; + background: $main; + display: flex; +} + +.header__logo { + width: 152px; + background: $brand; + display: flex; + align-items: center; + justify-content: center; + + img { + width: 100px; + } +} + +.header__top { + background-color: $main-dark; + height: 38px; + width: 100%; +} + +.header__nav { + flex: 1 1 auto; +} diff --git a/src/styles/overrides.scss b/src/styles/overrides.scss index e143cac..78aa4a2 100644 --- a/src/styles/overrides.scss +++ b/src/styles/overrides.scss @@ -11,6 +11,7 @@ margin-top: 1.5rem; } -#root { - margin: 100px 0; +html, body { + margin: 0; + padding: 0; } diff --git a/src/styles/page.scss b/src/styles/page.scss new file mode 100644 index 0000000..8b41597 --- /dev/null +++ b/src/styles/page.scss @@ -0,0 +1,14 @@ +@import "variables"; + +.page__header { + background-color: #e9f0f5; + padding: 20px 0; + margin-bottom: 2rem; +} + +.page__title { + color: $brand; + font-size: 3rem; + font-weight: 400; + line-height: 1.5; +} diff --git a/src/ui/theme.ts b/src/ui/theme.ts index a6286bd..f03db04 100644 --- a/src/ui/theme.ts +++ b/src/ui/theme.ts @@ -5,6 +5,15 @@ export const studentTheme = createMuiTheme({ MuiGrid: { spacing: 3, xs: 12, + }, + MuiContainer: { + maxWidth: "md" + } + }, + palette: { + primary: { + main: "#043865", + dark: "#072138" } }, spacing: 8 -- 2.45.2 From 0adbb531518210fac051e0e4f757dc502f066e82 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sat, 11 Jul 2020 16:47:15 +0200 Subject: [PATCH 03/12] Add top bar logged user info --- src/app.tsx | 65 ++++++++++++++++++++++++--------------- src/forms/Internship.tsx | 8 ++--- src/pages/base.tsx | 8 ++--- src/pages/main.tsx | 3 +- src/styles/header.scss | 33 ++++++++++++++++++++ src/styles/overrides.scss | 6 ++++ src/ui/theme.ts | 20 ++++++++++-- 7 files changed, 106 insertions(+), 37 deletions(-) diff --git a/src/app.tsx b/src/app.tsx index c9b9018..e90e92b 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -3,16 +3,17 @@ import { MuiThemeProvider as ThemeProvider, StylesProvider } from "@material-ui/ import { studentTheme } from "./ui/theme"; import { MuiPickersUtilsProvider } from "@material-ui/pickers"; import MomentUtils from "@date-io/moment"; -import { BrowserRouter, Switch, Route, Link } from "react-router-dom" +import { BrowserRouter, Link, Route, Switch } from "react-router-dom" import "moment/locale/pl" -moment.locale("pl") - import '@/styles/overrides.scss' import '@/styles/header.scss' import moment, { Moment } from "moment"; import { route, routes } from "@/routing"; +import { Button, Divider } from '@material-ui/core'; + +moment.locale("pl") class LocalizedMomentUtils extends MomentUtils { getDatePickerHeaderText(date: Moment): string { @@ -21,28 +22,42 @@ class LocalizedMomentUtils extends MomentUtils { } function App() { - return ( - - - - -
- -
- - -
-
- { routes.map(({ name, content, ...route }) => { content() }) } -
-
-
-
- ); + return ( + + + + +
+ +
+ + +
+
+ { routes.map(({ name, content, ...route }) => { content() }) } +
+
+
+
+ ); } export default App; diff --git a/src/forms/Internship.tsx b/src/forms/Internship.tsx index 56a5800..967b1b6 100644 --- a/src/forms/Internship.tsx +++ b/src/forms/Internship.tsx @@ -144,13 +144,13 @@ export const InternshipForm: React.FunctionComponent = prop return (
- Dane osoby odbywającej praktykę + Dane osoby odbywającej praktykę - Rodzaj i program praktyki + Rodzaj i program praktyki - Czas trwania praktyki + Czas trwania praktyki - Miejsce odbywania praktyki + Miejsce odbywania praktyki
diff --git a/src/pages/base.tsx b/src/pages/base.tsx index b8f1ff3..be27987 100644 --- a/src/pages/base.tsx +++ b/src/pages/base.tsx @@ -1,11 +1,11 @@ import React, { HTMLProps, useEffect } from "react"; import classNames from "classnames" -import { Breadcrumbs, BreadcrumbsProps, Container, Typography } from "@material-ui/core"; +import { Box, BoxProps, Breadcrumbs, BreadcrumbsProps, Container, Typography } from "@material-ui/core"; import "@/styles/page.scss" export type PageProps = { title?: string; -} & HTMLProps; +} & BoxProps; export type PageHeaderProps = { maxWidth?: "sm" | "md" | "lg" | false @@ -16,9 +16,9 @@ export const Page = ({ title, children, ...props }: PageProps) => { document.title = title ? title + " | Praktyka Studencka" : "Praktyka Studencka"; }, [ title ]) - return
+ return { children } -
+ } Page.Header = ({ children, maxWidth = false, ...props }: PageHeaderProps) => diff --git a/src/pages/main.tsx b/src/pages/main.tsx index 8fcb80d..d343098 100644 --- a/src/pages/main.tsx +++ b/src/pages/main.tsx @@ -6,8 +6,9 @@ import { Link as RouterLink } from "react-router-dom"; import { route } from "@/routing"; export const MainPage = () => { - return + return + Moja praktyka diff --git a/src/styles/header.scss b/src/styles/header.scss index b13891b..2397beb 100644 --- a/src/styles/header.scss +++ b/src/styles/header.scss @@ -4,6 +4,8 @@ height: 110px; background: $main; display: flex; + + color: white; } .header__logo { @@ -22,8 +24,39 @@ background-color: $main-dark; height: 38px; width: 100%; + display: flex; + align-items: center; + font-size: 0.75rem; + color: #e4f1fe; + padding: 0 1rem; + + a { + color: #e4f1fe + } + + .header__divider { + margin: 0 .5rem; + background: lighten($main-dark, 3%); + width: 1px; + height: 60%; + } } .header__nav { flex: 1 1 auto; } + +.header__top .header__menu { + margin-right: auto; +} + +.header__language-switcher { + padding: 0; + display: flex; + text-transform: uppercase; + + li { + padding: 7px; + list-style: none; + } +} diff --git a/src/styles/overrides.scss b/src/styles/overrides.scss index 78aa4a2..08cb80e 100644 --- a/src/styles/overrides.scss +++ b/src/styles/overrides.scss @@ -14,4 +14,10 @@ html, body { margin: 0; padding: 0; + + font-family: "Roboto", "Helvetica", "Arial", sans-serif; +} + +* { + box-sizing: border-box; } diff --git a/src/ui/theme.ts b/src/ui/theme.ts index f03db04..8f770f0 100644 --- a/src/ui/theme.ts +++ b/src/ui/theme.ts @@ -1,6 +1,6 @@ -import { createMuiTheme } from "@material-ui/core/styles"; +import { createMuiTheme, responsiveFontSizes } from "@material-ui/core/styles"; -export const studentTheme = createMuiTheme({ +export const studentTheme = responsiveFontSizes(createMuiTheme({ props: { MuiGrid: { spacing: 3, @@ -16,5 +16,19 @@ export const studentTheme = createMuiTheme({ dark: "#072138" } }, + typography: { + h2: { + fontSize: "3rem", + fontWeight: 400, + color: "#043865", + }, + h3: { + fontSize: "2.25rem", + lineHeight: "1.2", + }, + h4: { + fontSize: "1.75rem" + }, + }, spacing: 8 -}) +})) -- 2.45.2 From 6d8985e441c4504b47be8f91ab3da582ed789fde Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sat, 11 Jul 2020 18:30:56 +0200 Subject: [PATCH 04/12] Use ETI Logo instead of PG --- public/img/eti-logo.svg | 98 +++++++++++++++++++++++++++++++++++++++++ src/app.tsx | 2 +- src/styles/header.scss | 3 +- 3 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 public/img/eti-logo.svg diff --git a/public/img/eti-logo.svg b/public/img/eti-logo.svg new file mode 100644 index 0000000..c046c51 --- /dev/null +++ b/public/img/eti-logo.svg @@ -0,0 +1,98 @@ + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/src/app.tsx b/src/app.tsx index e90e92b..8a5d56e 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -30,7 +30,7 @@ function App() {
diff --git a/src/styles/header.scss b/src/styles/header.scss index 2397beb..4f5c722 100644 --- a/src/styles/header.scss +++ b/src/styles/header.scss @@ -16,7 +16,8 @@ justify-content: center; img { - width: 100px; + max-width: 100px; + max-height: 72px; } } -- 2.45.2 From 1a81c7ead5b23243987b3d4fbe6622aaccae1c49 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Mon, 13 Jul 2020 20:41:38 +0200 Subject: [PATCH 05/12] Add redux for global app state management --- package.json | 5 ++ src/app.tsx | 106 +++++++++++++++++++++++------------ src/state/actions/base.ts | 3 + src/state/actions/index.ts | 0 src/state/actions/student.ts | 16 ++++++ src/state/reducer/index.ts | 10 ++++ src/state/reducer/student.ts | 20 +++++++ src/state/store.ts | 7 +++ yarn.lock | 58 ++++++++++++++++++- 9 files changed, 187 insertions(+), 38 deletions(-) create mode 100644 src/state/actions/base.ts create mode 100644 src/state/actions/index.ts create mode 100644 src/state/actions/student.ts create mode 100644 src/state/reducer/index.ts create mode 100644 src/state/reducer/student.ts create mode 100644 src/state/store.ts diff --git a/package.json b/package.json index 6880974..7188204 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,9 @@ "@types/node": "^12.0.0", "@types/react": "^16.9.0", "@types/react-dom": "^16.9.0", + "@types/react-redux": "^7.1.9", "@types/react-router-dom": "^5.1.5", + "@types/redux": "^3.6.0", "@typescript-eslint/eslint-plugin": "^2.10.0", "@typescript-eslint/parser": "^2.10.0", "babel-core": "^6.26.3", @@ -38,7 +40,10 @@ "react-app-polyfill": "^1.0.6", "react-dev-utils": "^10.2.1", "react-dom": "^16.13.1", + "react-redux": "^7.2.0", "react-router-dom": "^5.2.0", + "redux": "^4.0.5", + "redux-devtools-extension": "^2.13.8", "sass-loader": "8.0.2", "style-loader": "0.23.1", "ts-loader": "^7.0.5", diff --git a/src/app.tsx b/src/app.tsx index 8a5d56e..d196c3d 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { Dispatch } from 'react'; import { MuiThemeProvider as ThemeProvider, StylesProvider } from "@material-ui/core/styles"; import { studentTheme } from "./ui/theme"; import { MuiPickersUtilsProvider } from "@material-ui/pickers"; @@ -11,7 +11,11 @@ import '@/styles/header.scss' import moment, { Moment } from "moment"; import { route, routes } from "@/routing"; -import { Button, Divider } from '@material-ui/core'; +import { Provider, useDispatch, useSelector } from "react-redux"; +import store from "@/state/store"; +import { AppState } from "@/state/reducer"; +import { StudentAction, StudentActions } from "@/state/actions/student"; +import { sampleStudent } from "@/provider/dummy/student"; moment.locale("pl") @@ -21,42 +25,72 @@ class LocalizedMomentUtils extends MomentUtils { } } +const UserMenu = () => { + const student = useSelector(state => state.student); + + const dispatch = useDispatch>(); + + const handleUserLogin = () => { + dispatch({ + type: StudentActions.Login, + student: sampleStudent, + }) + } + + const handleUserLogout = () => { + dispatch({ type: StudentActions.Logout }) + } + + return student ? <> + zalogowany jako: Jan Kowalski + {' '} + (wyloguj się) + : <> + zaloguj się + ; +} + +const AppHeader = () => { + return
+ +
+ + +
+
+} + + function App() { return ( - - - - -
- -
- - -
-
- { routes.map(({ name, content, ...route }) => { content() }) } -
-
-
-
+ + + + + + + { routes.map(({ name, content, ...route }) => { content() }) } + + + + + ); } diff --git a/src/state/actions/base.ts b/src/state/actions/base.ts new file mode 100644 index 0000000..42285e4 --- /dev/null +++ b/src/state/actions/base.ts @@ -0,0 +1,3 @@ +export interface Action { + readonly type: TType; +} diff --git a/src/state/actions/index.ts b/src/state/actions/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/state/actions/student.ts b/src/state/actions/student.ts new file mode 100644 index 0000000..29c6ae0 --- /dev/null +++ b/src/state/actions/student.ts @@ -0,0 +1,16 @@ +import { Action } from "@/state/actions/base"; +import { Student } from "@/data"; + +export enum StudentActions { + Login = 'LOGIN', + Logout = 'LOGOUT' +} + +export interface LoginAction extends Action { + student: Student +} + +export type LogoutAction = Action; + +export type StudentAction = LoginAction | LogoutAction; + diff --git a/src/state/reducer/index.ts b/src/state/reducer/index.ts new file mode 100644 index 0000000..5f2faad --- /dev/null +++ b/src/state/reducer/index.ts @@ -0,0 +1,10 @@ +import { combineReducers } from "redux"; +import studentReducer from "./student" + +const rootReducer = combineReducers({ + student: studentReducer, +}) + +export type AppState = ReturnType; + +export default rootReducer; diff --git a/src/state/reducer/student.ts b/src/state/reducer/student.ts new file mode 100644 index 0000000..687f308 --- /dev/null +++ b/src/state/reducer/student.ts @@ -0,0 +1,20 @@ +import { Student } from "@/data"; +import { StudentAction, StudentActions } from "@/state/actions/student"; + +export type StudentState = Student | null; + +const initialStudentState: StudentState = null; + +const studentReducer = (state: StudentState = initialStudentState, action: StudentAction): StudentState => { + switch (action.type) { + case StudentActions.Login: + return action.student; + + case StudentActions.Logout: + return null; + } + + return state; +} + +export default studentReducer; diff --git a/src/state/store.ts b/src/state/store.ts new file mode 100644 index 0000000..7e045a3 --- /dev/null +++ b/src/state/store.ts @@ -0,0 +1,7 @@ +import { createStore } from "redux"; +import rootReducer from "@/state/reducer"; +import { devToolsEnhancer } from "redux-devtools-extension"; + +const store = createStore(rootReducer, devToolsEnhancer({ })); + +export default store; diff --git a/yarn.lock b/yarn.lock index 0302808..48105c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1146,6 +1146,14 @@ resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.6.tgz#ed8fc802c45b8e8f54419c2d054e55c9ea344356" integrity sha512-GRTZLeLJ8ia00ZH8mxMO8t0aC9M1N9bN461Z2eaRurJo6Fpa+utgCwLzI4jQHcrdzuzp5WPN9jRwpsCQ1VhJ5w== +"@types/hoist-non-react-statics@^3.3.0": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" + integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.4": version "7.0.5" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd" @@ -1188,6 +1196,16 @@ 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" + integrity sha512-mpC0jqxhP4mhmOl3P4ipRsgTgbNofMRXJb08Ms6gekViLj61v1hOZEKWDCyWsdONr6EjEA6ZHXC446wdywDe0w== + dependencies: + "@types/hoist-non-react-statics" "^3.3.0" + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + redux "^4.0.0" + "@types/react-router-dom@^5.1.5": version "5.1.5" resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.5.tgz#7c334a2ea785dbad2b2dcdd83d2cf3d9973da090" @@ -1220,6 +1238,13 @@ "@types/prop-types" "*" csstype "^2.2.0" +"@types/redux@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@types/redux/-/redux-3.6.0.tgz#f1ebe1e5411518072e4fdfca5c76e16e74c1399a" + integrity sha1-8evh5UEVGAcuT9/KXHbhbnTBOZo= + dependencies: + redux "*" + "@types/source-list-map@*": version "0.1.2" resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" @@ -4293,7 +4318,7 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.2: +hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -7298,11 +7323,22 @@ react-error-overlay@^6.0.7: resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108" integrity sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA== -react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1: +react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.9.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-redux@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.0.tgz#f970f62192b3981642fec46fd0db18a074fe879d" + integrity sha512-EvCAZYGfOLqwV7gh849xy9/pt55rJXPwmYvI4lilPM5rUT/1NxuuN59ipdBksRVSvz0KInbPnp4IfoXJXCqiDA== + dependencies: + "@babel/runtime" "^7.5.5" + hoist-non-react-statics "^3.3.0" + loose-envify "^1.4.0" + prop-types "^15.7.2" + react-is "^16.9.0" + react-router-dom@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662" @@ -7421,6 +7457,19 @@ redent@^1.0.0: indent-string "^2.1.0" strip-indent "^1.0.1" +redux-devtools-extension@^2.13.8: + version "2.13.8" + resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.8.tgz#37b982688626e5e4993ff87220c9bbb7cd2d96e1" + integrity sha512-8qlpooP2QqPtZHQZRhx3x3OP5skEV1py/zUdMY28WNAocbafxdG2tRD1MWE7sp8obGMNYuLWanhhQ7EQvT1FBg== + +redux@*, redux@^4.0.0, redux@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f" + integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w== + dependencies: + loose-envify "^1.4.0" + symbol-observable "^1.2.0" + regenerate-unicode-properties@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" @@ -8405,6 +8454,11 @@ svgo@^1.0.0: unquote "~1.1.1" util.promisify "~1.0.0" +symbol-observable@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" + integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== + tapable@^1.0.0, tapable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" -- 2.45.2 From e5cf8ff1074c4019690d40a228b35965062f3510 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Mon, 13 Jul 2020 23:10:05 +0200 Subject: [PATCH 06/12] Add translations --- package.json | 6 ++- src/app.tsx | 96 ++++++++++++++++++++++++------------------ src/i18n.ts | 26 ++++++++++++ src/index.tsx | 1 + src/pages/main.tsx | 16 ++++--- src/styles/header.scss | 6 ++- translations/en.yaml | 17 ++++++++ translations/pl.yaml | 17 ++++++++ webpack.config.js | 4 ++ yarn.lock | 51 +++++++++++++++++++++- 10 files changed, 189 insertions(+), 51 deletions(-) create mode 100644 src/i18n.ts create mode 100644 translations/en.yaml create mode 100644 translations/pl.yaml diff --git a/package.json b/package.json index 7188204..8f9be5c 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,8 @@ "date-holidays": "^1.5.3", "file-loader": "4.3.0", "html-webpack-plugin": "4.0.0-beta.11", + "i18next": "^19.6.0", + "i18next-browser-languagedetector": "^5.0.0", "moment": "^2.26.0", "node-sass": "^4.14.1", "optimize-css-assets-webpack-plugin": "5.0.3", @@ -40,6 +42,7 @@ "react-app-polyfill": "^1.0.6", "react-dev-utils": "^10.2.1", "react-dom": "^16.13.1", + "react-i18next": "^11.7.0", "react-redux": "^7.2.0", "react-router-dom": "^5.2.0", "redux": "^4.0.5", @@ -52,7 +55,8 @@ "webpack": "4.42.0", "webpack-cli": "^3.3.11", "webpack-dev-server": "3.10.3", - "workbox-webpack-plugin": "4.3.1" + "workbox-webpack-plugin": "4.3.1", + "yaml-loader": "^0.6.0" }, "scripts": { "serve": "webpack-dev-server --mode development", diff --git a/src/app.tsx b/src/app.tsx index d196c3d..f078d76 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,14 +1,9 @@ -import React, { Dispatch } from 'react'; +import React, { Dispatch, HTMLProps } from 'react'; import { MuiThemeProvider as ThemeProvider, StylesProvider } from "@material-ui/core/styles"; import { studentTheme } from "./ui/theme"; import { MuiPickersUtilsProvider } from "@material-ui/pickers"; import MomentUtils from "@date-io/moment"; import { BrowserRouter, Link, Route, Switch } from "react-router-dom" - -import "moment/locale/pl" -import '@/styles/overrides.scss' -import '@/styles/header.scss' - import moment, { Moment } from "moment"; import { route, routes } from "@/routing"; import { Provider, useDispatch, useSelector } from "react-redux"; @@ -16,6 +11,15 @@ import store from "@/state/store"; import { AppState } from "@/state/reducer"; import { StudentAction, StudentActions } from "@/state/actions/student"; import { sampleStudent } from "@/provider/dummy/student"; +import { Trans, useTranslation } from "react-i18next"; +import { StudentState } from "@/state/reducer/student"; +import { Student } from "@/data"; + +import "moment/locale/pl" +import '@/styles/overrides.scss' +import '@/styles/header.scss' +import classNames from "classnames"; +import { Button } from "@material-ui/core"; moment.locale("pl") @@ -25,10 +29,10 @@ class LocalizedMomentUtils extends MomentUtils { } } -const UserMenu = () => { - const student = useSelector(state => state.student); - +const UserMenu = (props: HTMLProps) => { + const student = useSelector(state => state.student as Student); const dispatch = useDispatch>(); + const { t } = useTranslation(); const handleUserLogin = () => { dispatch({ @@ -41,41 +45,32 @@ const UserMenu = () => { dispatch({ type: StudentActions.Logout }) } - return student ? <> - zalogowany jako: Jan Kowalski - {' '} - (wyloguj się) - : <> - zaloguj się - ; + return
    + { + student ? <> + logged in as {{ name: student.name }} + {' '} + ({ t('logout') }) + : <> + { t('login') } + + } +
; } -const AppHeader = () => { - return
- -
- - -
-
-} +const LanguageSwitcher = ({ className, ...props }: HTMLProps) => { + const { i18n } = useTranslation(); + const handleLanguageChange = (language: string) => () => { + i18n.changeLanguage(language); + } + + return
    + { ['pl', 'en'].map(language =>
  • + { language } +
  • ) } +
+} function App() { return ( @@ -84,7 +79,24 @@ function App() { - +
+ +
+ + +
+
{ routes.map(({ name, content, ...route }) => { content() }) }
diff --git a/src/i18n.ts b/src/i18n.ts new file mode 100644 index 0000000..a38e4f0 --- /dev/null +++ b/src/i18n.ts @@ -0,0 +1,26 @@ +import i18n from "i18next"; +import { initReactI18next } from "react-i18next"; +import I18nextBrowserLanguageDetector from "i18next-browser-languagedetector"; + +const resources = { + en: { + translation: require('../translations/en.yaml'), + }, + pl: { + translation: require('../translations/pl.yaml'), + } +} + +i18n + .use(I18nextBrowserLanguageDetector) + .use(initReactI18next) + .init({ + resources, + fallbackLng: "en", + debug: true, + interpolation: { + escapeValue: false + } + }) + +export default i18n; diff --git a/src/index.tsx b/src/index.tsx index 902d328..4bbd47a 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,5 +1,6 @@ import React from 'react'; import ReactDOM from 'react-dom'; +import "./i18n" import App from './app'; ReactDOM.render( diff --git a/src/pages/main.tsx b/src/pages/main.tsx index d343098..9bf4978 100644 --- a/src/pages/main.tsx +++ b/src/pages/main.tsx @@ -4,25 +4,29 @@ import { Container, Typography, Button, Divider, Box, Stepper, Step, StepLabel, import { InternshipForm } from "@/forms/Internship"; import { Link as RouterLink } from "react-router-dom"; import { route } from "@/routing"; +import { useTranslation } from "react-i18next"; export const MainPage = () => { + const { t } = useTranslation(); + return - Moja praktyka + { t("sections.my-internship.header") } - Uzupełnienie danych w systemie + { t('steps.personal-data.header') } - do: 05.07.2020 + { t('until', { date: '05.07.2020' }) } - lol - Zgłoszenie praktyki + { t('steps.internship-proposal.header')} - + diff --git a/src/styles/header.scss b/src/styles/header.scss index 4f5c722..ea7b6c1 100644 --- a/src/styles/header.scss +++ b/src/styles/header.scss @@ -57,7 +57,11 @@ text-transform: uppercase; li { - padding: 7px; list-style: none; + + a { + padding: 7px; + display: block; + } } } diff --git a/translations/en.yaml b/translations/en.yaml new file mode 100644 index 0000000..cfec6f8 --- /dev/null +++ b/translations/en.yaml @@ -0,0 +1,17 @@ +--- +login: login +logout: logout +logged-in-as: logged in as <1>{{ name }} + +until: until {{ date }} + +sections: + my-internship: + header: "My internship" + +steps: + personal-data: + header: "Personal data" + internship-proposal: + header: "Internship proposal" + form: "Internship proposal form" diff --git a/translations/pl.yaml b/translations/pl.yaml new file mode 100644 index 0000000..7c1bcbf --- /dev/null +++ b/translations/pl.yaml @@ -0,0 +1,17 @@ +--- +login: zaloguj się +logout: wyloguj się +logged-in-as: zalogowany jako <1>{{ name }} + +until: do {{ date }} + +sections: + my-internship: + header: "Moja praktyka" + +steps: + personal-data: + header: "Uzupełnienie informacji" + internship-proposal: + header: "Zgłoszenie praktyki" + form: "Formularz zgłaszania praktyki" diff --git a/webpack.config.js b/webpack.config.js index 916d8ea..6aeedda 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -34,6 +34,10 @@ const config = { }, { test: /\.(woff|woff2|eot|ttf|otf)$/, use: 'file-loader' + }, { + test: /\.ya?ml$/, + type: 'json', + use: 'yaml-loader' }] }, plugins: [ diff --git a/yarn.lock b/yarn.lock index 48105c9..29848d6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -926,6 +926,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.10.1": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.4.tgz#a6724f1a6b8d2f6ea5236dbfe58c7d7ea9c5eb99" + integrity sha512-UpTN5yUJr9b4EX2CnGNWIvER7Ab83ibv0pcvvHc4UOdrBI5jb8bj+32cCwPX6xu0mt2daFNjYhoi+X7beH0RSw== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.10.1", "@babel/template@^7.8.6": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811" @@ -4388,6 +4395,13 @@ html-minifier-terser@^5.0.1: relateurl "^0.2.7" terser "^4.6.3" +html-parse-stringify2@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/html-parse-stringify2/-/html-parse-stringify2-2.0.1.tgz#dc5670b7292ca158b7bc916c9a6735ac8872834a" + integrity sha1-3FZwtyksoVi3vJFsmmc1rIhyg0o= + dependencies: + void-elements "^2.0.1" + html-webpack-plugin@4.0.0-beta.11: version "4.0.0-beta.11" resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.11.tgz#3059a69144b5aecef97708196ca32f9e68677715" @@ -4492,6 +4506,20 @@ hyphenate-style-name@^1.0.3: resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz#097bb7fa0b8f1a9cf0bd5c734cf95899981a9b48" integrity sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ== +i18next-browser-languagedetector@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-5.0.0.tgz#9e946ed2ea5514a636913fe020a32455e82946e3" + integrity sha512-ekeKbRvTOsSOABSEPHFqyb6Q37JagZXjkISgQKHP84t/VZRW/B3FMVz+tBNQDVdZLsEaOe8fuJpeZsw2TvWeVQ== + dependencies: + "@babel/runtime" "^7.5.5" + +i18next@^19.6.0: + version "19.6.0" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-19.6.0.tgz#3881b8e476e494dcdadcc8983e594080417fd82e" + integrity sha512-t+pA7iN2WtwS1UQc4PFKHDIO4HYZIl2Wo8UC8gqt70Q1qY50FflAF5vV4IbQEqy4DuK3I9wv3BL1PMvkk238WA== + dependencies: + "@babel/runtime" "^7.10.1" + iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -7323,6 +7351,14 @@ react-error-overlay@^6.0.7: resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108" integrity sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA== +react-i18next@^11.7.0: + version "11.7.0" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.7.0.tgz#f27c4c237a274e007a48ac1210db83e33719908b" + integrity sha512-8tvVkpuxQlubcszZON+jmoCgiA9gCZ74OAYli9KChPhETtq8pJsANBTe9KRLRLmX3ubumgvidURWr0VvKz1tww== + dependencies: + "@babel/runtime" "^7.3.1" + html-parse-stringify2 "2.0.1" + react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.9.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -8910,6 +8946,11 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== +void-elements@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" + integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w= + watchpack-chokidar2@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz#9948a1866cbbd6cb824dea13a7ed691f6c8ddff0" @@ -9290,7 +9331,15 @@ yallist@^3.0.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yaml@^1.7.2: +yaml-loader@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/yaml-loader/-/yaml-loader-0.6.0.tgz#fe1c48b9f4803dace55a59a1474e790ba6ab1b48" + integrity sha512-1bNiLelumURyj+zvVHOv8Y3dpCri0F2S+DCcmps0pA1zWRLjS+FhZQg4o3aUUDYESh73+pKZNI18bj7stpReow== + dependencies: + loader-utils "^1.4.0" + yaml "^1.8.3" + +yaml@^1.7.2, yaml@^1.8.3: version "1.10.0" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== -- 2.45.2 From e9d0f6d348952dee243823377ac4834909514dcc Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sat, 18 Jul 2020 16:42:19 +0200 Subject: [PATCH 07/12] Mark selected language as active --- src/app.tsx | 9 +++++++-- src/styles/header.scss | 6 +++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/app.tsx b/src/app.tsx index f078d76..c74e0b1 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -48,7 +48,7 @@ const UserMenu = (props: HTMLProps) => { return
    { student ? <> - logged in as {{ name: student.name }} + logged in as {{ name: `${student.name} ${student.surname}` }} {' '} ({ t('logout') }) : <> @@ -65,9 +65,14 @@ const LanguageSwitcher = ({ className, ...props }: HTMLProps) i18n.changeLanguage(language); } + const isActive = (language: string) => language.toLowerCase() === i18n.language.toLowerCase(); + return
      { ['pl', 'en'].map(language =>
    • - { language } + + { language } +
    • ) }
    } diff --git a/src/styles/header.scss b/src/styles/header.scss index ea7b6c1..fa40ca4 100644 --- a/src/styles/header.scss +++ b/src/styles/header.scss @@ -37,7 +37,7 @@ .header__divider { margin: 0 .5rem; - background: lighten($main-dark, 3%); + background: #274057; width: 1px; height: 60%; } @@ -65,3 +65,7 @@ } } } + +.language-switcher__language--active { + font-weight: bold; +} -- 2.45.2 From f450a8dc4e930b53da76d64590d9742856793b0f Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sat, 18 Jul 2020 16:53:48 +0200 Subject: [PATCH 08/12] Persist user in session storage --- package.json | 2 ++ src/app.tsx | 59 ++++++++++++++++++++------------------ src/state/reducer/index.ts | 1 + src/state/store.ts | 15 +++++++++- yarn.lock | 12 ++++++++ 5 files changed, 60 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index 8f9be5c..4557da7 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@types/react-redux": "^7.1.9", "@types/react-router-dom": "^5.1.5", "@types/redux": "^3.6.0", + "@types/redux-persist": "^4.3.1", "@typescript-eslint/eslint-plugin": "^2.10.0", "@typescript-eslint/parser": "^2.10.0", "babel-core": "^6.26.3", @@ -47,6 +48,7 @@ "react-router-dom": "^5.2.0", "redux": "^4.0.5", "redux-devtools-extension": "^2.13.8", + "redux-persist": "^6.0.0", "sass-loader": "8.0.2", "style-loader": "0.23.1", "ts-loader": "^7.0.5", diff --git a/src/app.tsx b/src/app.tsx index c74e0b1..0b44e75 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -7,7 +7,7 @@ import { BrowserRouter, Link, Route, Switch } from "react-router-dom" import moment, { Moment } from "moment"; import { route, routes } from "@/routing"; import { Provider, useDispatch, useSelector } from "react-redux"; -import store from "@/state/store"; +import store, { persistor } from "@/state/store"; import { AppState } from "@/state/reducer"; import { StudentAction, StudentActions } from "@/state/actions/student"; import { sampleStudent } from "@/provider/dummy/student"; @@ -20,6 +20,7 @@ import '@/styles/overrides.scss' import '@/styles/header.scss' import classNames from "classnames"; import { Button } from "@material-ui/core"; +import { PersistGate } from 'redux-persist/integration/react'; moment.locale("pl") @@ -80,33 +81,35 @@ const LanguageSwitcher = ({ className, ...props }: HTMLProps) function App() { return ( - - - - -
    - -
    - - -
    -
    - { routes.map(({ name, content, ...route }) => { content() }) } -
    -
    -
    -
    + + + + + +
    + +
    + + +
    +
    + { routes.map(({ name, content, ...route }) => { content() }) } +
    +
    +
    +
    +
    ); } diff --git a/src/state/reducer/index.ts b/src/state/reducer/index.ts index 5f2faad..0bd70e0 100644 --- a/src/state/reducer/index.ts +++ b/src/state/reducer/index.ts @@ -1,4 +1,5 @@ import { combineReducers } from "redux"; + import studentReducer from "./student" const rootReducer = combineReducers({ diff --git a/src/state/store.ts b/src/state/store.ts index 7e045a3..9fafb4b 100644 --- a/src/state/store.ts +++ b/src/state/store.ts @@ -1,7 +1,20 @@ import { createStore } from "redux"; import rootReducer from "@/state/reducer"; import { devToolsEnhancer } from "redux-devtools-extension"; +import { persistReducer, persistStore } from "redux-persist" +import sessionStorage from "redux-persist/lib/storage/session" -const store = createStore(rootReducer, devToolsEnhancer({ })); +const store = createStore( + persistReducer( + { + key: 'state', + storage: sessionStorage + }, + rootReducer + ), + devToolsEnhancer({}) +); + +export const persistor = persistStore(store) export default store; diff --git a/yarn.lock b/yarn.lock index 29848d6..225a9da 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1245,6 +1245,13 @@ "@types/prop-types" "*" csstype "^2.2.0" +"@types/redux-persist@^4.3.1": + version "4.3.1" + resolved "https://registry.yarnpkg.com/@types/redux-persist/-/redux-persist-4.3.1.tgz#aa4c876859e0bea5155e5f7980e5b8c4699dc2e6" + integrity sha1-qkyHaFngvqUVXl95gOW4xGmdwuY= + dependencies: + redux-persist "*" + "@types/redux@^3.6.0": version "3.6.0" resolved "https://registry.yarnpkg.com/@types/redux/-/redux-3.6.0.tgz#f1ebe1e5411518072e4fdfca5c76e16e74c1399a" @@ -7498,6 +7505,11 @@ redux-devtools-extension@^2.13.8: resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.8.tgz#37b982688626e5e4993ff87220c9bbb7cd2d96e1" integrity sha512-8qlpooP2QqPtZHQZRhx3x3OP5skEV1py/zUdMY28WNAocbafxdG2tRD1MWE7sp8obGMNYuLWanhhQ7EQvT1FBg== +redux-persist@*, redux-persist@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-6.0.0.tgz#b4d2972f9859597c130d40d4b146fecdab51b3a8" + integrity sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ== + redux@*, redux@^4.0.0, redux@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f" -- 2.45.2 From 16d2ef431531e81e3ff388ff2d7b9eaa7753b372 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sun, 19 Jul 2020 15:34:15 +0200 Subject: [PATCH 09/12] Set properly language on html element --- public/index.html | 3 +-- src/app.tsx | 1 + src/i18n.ts | 3 ++- src/styles/header.scss | 8 +++++++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/public/index.html b/public/index.html index 076a951..ecae82c 100644 --- a/public/index.html +++ b/public/index.html @@ -4,8 +4,7 @@ - - + Zgłoszenie praktyki studenckiej diff --git a/src/app.tsx b/src/app.tsx index 0b44e75..d8bc08c 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -64,6 +64,7 @@ const LanguageSwitcher = ({ className, ...props }: HTMLProps) const handleLanguageChange = (language: string) => () => { i18n.changeLanguage(language); + document.documentElement.lang = language; } const isActive = (language: string) => language.toLowerCase() === i18n.language.toLowerCase(); diff --git a/src/i18n.ts b/src/i18n.ts index a38e4f0..3dc605e 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -17,10 +17,11 @@ i18n .init({ resources, fallbackLng: "en", - debug: true, interpolation: { escapeValue: false } }) +document.documentElement.lang = i18n.language; + export default i18n; diff --git a/src/styles/header.scss b/src/styles/header.scss index fa40ca4..9750554 100644 --- a/src/styles/header.scss +++ b/src/styles/header.scss @@ -36,7 +36,7 @@ } .header__divider { - margin: 0 .5rem; + margin: 0 1rem; background: #274057; width: 1px; height: 60%; @@ -59,6 +59,10 @@ li { list-style: none; + &:not(:last-child) { + margin-right: .5rem; + } + a { padding: 7px; display: block; @@ -68,4 +72,6 @@ .language-switcher__language--active { font-weight: bold; + cursor: default; + text-decoration: none; } -- 2.45.2 From a3db797a0606eac58d3454b080c805b48c4810d5 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Tue, 21 Jul 2020 22:19:52 +0200 Subject: [PATCH 10/12] Add more steps --- package.json | 1 + src/app.tsx | 7 +-- src/data/internship.ts | 1 - src/i18n.ts | 6 ++ src/pages/errors/not-found.tsx | 22 ++++++++ src/pages/index.ts | 3 + src/pages/internship/proposal.tsx | 23 ++++++++ src/pages/main.tsx | 91 ++++++++++++++----------------- src/routing.tsx | 16 +++--- translations/en.yaml | 6 +- translations/pl.yaml | 10 ++++ yarn.lock | 7 +++ 12 files changed, 129 insertions(+), 64 deletions(-) create mode 100644 src/pages/errors/not-found.tsx create mode 100644 src/pages/index.ts create mode 100644 src/pages/internship/proposal.tsx diff --git a/package.json b/package.json index 4557da7..5fe314f 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "@babel/preset-typescript": "^7.10.1", "@date-io/moment": "^1.3.13", "@material-ui/core": "^4.10.1", + "@material-ui/icons": "^4.9.1", "@material-ui/lab": "^4.0.0-alpha.55", "@material-ui/pickers": "^3.2.10", "@types/classnames": "^2.2.10", diff --git a/src/app.tsx b/src/app.tsx index d8bc08c..a334733 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -12,18 +12,12 @@ import { AppState } from "@/state/reducer"; import { StudentAction, StudentActions } from "@/state/actions/student"; import { sampleStudent } from "@/provider/dummy/student"; import { Trans, useTranslation } from "react-i18next"; -import { StudentState } from "@/state/reducer/student"; import { Student } from "@/data"; - -import "moment/locale/pl" import '@/styles/overrides.scss' import '@/styles/header.scss' import classNames from "classnames"; -import { Button } from "@material-ui/core"; import { PersistGate } from 'redux-persist/integration/react'; -moment.locale("pl") - class LocalizedMomentUtils extends MomentUtils { getDatePickerHeaderText(date: Moment): string { return this.format(date, "d MMM yyyy"); @@ -65,6 +59,7 @@ const LanguageSwitcher = ({ className, ...props }: HTMLProps) const handleLanguageChange = (language: string) => () => { i18n.changeLanguage(language); document.documentElement.lang = language; + moment.locale(language) } const isActive = (language: string) => language.toLowerCase() === i18n.language.toLowerCase(); diff --git a/src/data/internship.ts b/src/data/internship.ts index b80258f..4961e27 100644 --- a/src/data/internship.ts +++ b/src/data/internship.ts @@ -1,6 +1,5 @@ import { Moment } from "moment"; import { Identifiable } from "./common"; -import { countWorkingWeeksInRange } from "@/utils/date"; import { Student } from "@/data/student"; import { Company } from "@/data/company"; diff --git a/src/i18n.ts b/src/i18n.ts index 3dc605e..4f8c6a2 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -2,6 +2,11 @@ import i18n from "i18next"; import { initReactI18next } from "react-i18next"; import I18nextBrowserLanguageDetector from "i18next-browser-languagedetector"; +import moment from "moment"; + +import "moment/locale/pl" +import "moment/locale/en-gb" + const resources = { en: { translation: require('../translations/en.yaml'), @@ -23,5 +28,6 @@ i18n }) document.documentElement.lang = i18n.language; +moment.locale(i18n.language) export default i18n; diff --git a/src/pages/errors/not-found.tsx b/src/pages/errors/not-found.tsx new file mode 100644 index 0000000..0f6456b --- /dev/null +++ b/src/pages/errors/not-found.tsx @@ -0,0 +1,22 @@ +import { Page } from "@/pages/base"; +import { Box, Button, Container, Divider, Typography } from "@material-ui/core"; +import { route } from "@/routing"; +import { Link as RouterLink } from "react-router-dom"; +import React from "react"; + +export const NotFoundPage = () => { + return + + 404 + Strona nie została znaleziona + + + + + + + + +} + +export default NotFoundPage; diff --git a/src/pages/index.ts b/src/pages/index.ts new file mode 100644 index 0000000..c5f71a3 --- /dev/null +++ b/src/pages/index.ts @@ -0,0 +1,3 @@ +export * from "./internship/proposal"; +export * from "./errors/not-found" +export * from "./main" diff --git a/src/pages/internship/proposal.tsx b/src/pages/internship/proposal.tsx new file mode 100644 index 0000000..00e233c --- /dev/null +++ b/src/pages/internship/proposal.tsx @@ -0,0 +1,23 @@ +import { Page } from "@/pages/base"; +import { Container, Link, Typography } from "@material-ui/core"; +import { Link as RouterLink } from "react-router-dom"; +import { route } from "@/routing"; +import { InternshipForm } from "@/forms/Internship"; +import React from "react"; + +export const InternshipProposalPage = () => { + return + + + Moja praktyka + Zgłoszenie praktyki + + Zgłoszenie praktyki + + + + + +} + +export default InternshipProposalPage; diff --git a/src/pages/main.tsx b/src/pages/main.tsx index 9bf4978..a2c371f 100644 --- a/src/pages/main.tsx +++ b/src/pages/main.tsx @@ -1,65 +1,58 @@ -import React from "react"; +import React, { useMemo } from "react"; import { Page } from "@/pages/base"; -import { Container, Typography, Button, Divider, Box, Stepper, Step, StepLabel, StepContent, Link } from "@material-ui/core"; -import { InternshipForm } from "@/forms/Internship"; +import { Box, Button, Container, Step as StepperStep, StepContent, StepLabel, Stepper, StepProps as StepperStepProps, Typography } from "@material-ui/core"; import { Link as RouterLink } from "react-router-dom"; import { route } from "@/routing"; import { useTranslation } from "react-i18next"; +import moment, { Moment } from "moment"; + +type StepProps = StepperStepProps & { + until?: Moment; + completedOn?: Moment; + label: string; +} + +const now = moment(); + +const Step = ({ until, label, completedOn, children, completed, ...props }: StepProps) => { + const { t } = useTranslation(); + + const isLate = useMemo(() => until?.isBefore(completedOn || now), [completedOn, until]); + const left = useMemo(() => moment.duration(now.diff(until)), [until]); + + return + + { label } + { until && + + { t('until', { date: until.format("DD MMMM YYYY") }) } + { isLate && - { t('late', { by: moment.duration(now.diff(until)).humanize() }) } } + { !isLate && !completed && - { t('left', { left: left.humanize() }) } } + + } + + { children && { children } } + +} export const MainPage = () => { const { t } = useTranslation(); - return + return { t("sections.my-internship.header") } - - - { t('steps.personal-data.header') } - - { t('until', { date: '05.07.2020' }) } - - - - - { t('steps.internship-proposal.header')} - - - + + + + + + + } - -export const InternshipFormPage = () => { - return - - - Moja praktyka - Zgłoszenie praktyki - - Zgłoszenie praktyki - - - - - -} - -export const NotFoundPage = () => { - return - - 404 - Strona nie została znaleziona - - - - - - - - -} diff --git a/src/routing.tsx b/src/routing.tsx index 2137bfa..d2a2274 100644 --- a/src/routing.tsx +++ b/src/routing.tsx @@ -1,6 +1,8 @@ import React, { ReactComponentElement } from "react"; -import { InternshipFormPage, MainPage, NotFoundPage } from "@/pages/main"; +import { MainPage } from "@/pages/main"; import { RouteProps } from "react-router-dom"; +import { InternshipProposalPage } from "@/pages/internship/proposal"; +import { NotFoundPage } from "@/pages/errors/not-found"; type Route = { name?: string; @@ -8,18 +10,18 @@ type Route = { } & RouteProps; export const routes: Route[] = [ - { name: "home", path: "/", exact: true, content: () => }, + { name: "home", path: "/", exact: true, content: () => }, - { name: "internship_proposal", path: "/internship/proposal", exact: true, content: () => }, + { name: "internship_proposal", path: "/internship/proposal", exact: true, content: () => }, // fallback route for 404 pages - { path: "*", content: () => } + { name: "fallback", path: "*", content: () => } ] -const routeNameMap = new Map(routes.filter(({name}) => !!name).map(({ name, path }) => [ name, path instanceof Array ? path[0] : path ])) as Map +const routeNameMap = new Map(routes.filter(({ name }) => !!name).map(({ name, path }) => [name, path instanceof Array ? path[0] : path])) as Map -export function route(name: string, params: { [param: string]: string } = { }) { +export function route(name: string, params: { [param: string]: string } = {}) { const url = routeNameMap.get(name) || ""; - return Object.entries(params).reduce((url, [name, value]) => url.replace(`:${name}`, value), url); + return Object.entries(params).reduce((url, [name, value]) => url.replace(`:${ name }`, value), url); } diff --git a/translations/en.yaml b/translations/en.yaml index cfec6f8..d0bc877 100644 --- a/translations/en.yaml +++ b/translations/en.yaml @@ -4,6 +4,8 @@ logout: logout logged-in-as: logged in as <1>{{ name }} until: until {{ date }} +late: late by {{ by }} +left: '{{ left }} left' sections: my-internship: @@ -11,7 +13,9 @@ sections: steps: personal-data: - header: "Personal data" + header: "Fill personal data" internship-proposal: header: "Internship proposal" form: "Internship proposal form" + plan: + header: "Individual Internship Plan" diff --git a/translations/pl.yaml b/translations/pl.yaml index 7c1bcbf..b931255 100644 --- a/translations/pl.yaml +++ b/translations/pl.yaml @@ -4,6 +4,8 @@ logout: wyloguj się logged-in-as: zalogowany jako <1>{{ name }} until: do {{ date }} +late: '{{ by }} spóźnienia' +left: jeszcze {{ left }} sections: my-internship: @@ -15,3 +17,11 @@ steps: internship-proposal: header: "Zgłoszenie praktyki" form: "Formularz zgłaszania praktyki" + plan: + header: "Indywidualny Program Praktyki" + report: + header: "Raport z praktyki" + grade: + header: "Ocena z praktyki" + insurance: + header: "Ubezpieczenie NWW" diff --git a/yarn.lock b/yarn.lock index 225a9da..a96ad63 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1043,6 +1043,13 @@ react-is "^16.8.0" react-transition-group "^4.4.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" + integrity sha512-GBitL3oBWO0hzBhvA9KxqcowRUsA0qzwKkURyC8nppnC3fw54KPKZ+d4V1Eeg/UnDRSzDaI9nGCdel/eh9AQMg== + dependencies: + "@babel/runtime" "^7.4.4" + "@material-ui/lab@^4.0.0-alpha.55": version "4.0.0-alpha.55" resolved "https://registry.yarnpkg.com/@material-ui/lab/-/lab-4.0.0-alpha.55.tgz#82a850dc59654e04ee3a31be1b34eb3bf64d5585" -- 2.45.2 From e012d015db3a2b2d06d5c5b39e667b8f632f5cb3 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Wed, 22 Jul 2020 00:00:27 +0200 Subject: [PATCH 11/12] Add step info --- src/app.tsx | 98 ++++++++++++++++------------------- src/data/edition.ts | 21 ++++++++ src/data/student.ts | 15 ++++++ src/index.tsx | 29 ++++++++++- src/pages/main.tsx | 47 ++++++++++++++--- src/provider/dummy/edition.ts | 8 +++ src/state/actions/edition.ts | 13 +++++ src/state/reducer/edition.ts | 17 ++++++ src/state/reducer/index.ts | 2 + src/state/store.ts | 3 +- translations/en.yaml | 21 ++++++++ translations/pl.yaml | 21 +++++++- 12 files changed, 231 insertions(+), 64 deletions(-) create mode 100644 src/data/edition.ts create mode 100644 src/provider/dummy/edition.ts create mode 100644 src/state/actions/edition.ts create mode 100644 src/state/reducer/edition.ts diff --git a/src/app.tsx b/src/app.tsx index a334733..0f42d36 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,13 +1,8 @@ -import React, { Dispatch, HTMLProps } from 'react'; -import { MuiThemeProvider as ThemeProvider, StylesProvider } from "@material-ui/core/styles"; -import { studentTheme } from "./ui/theme"; -import { MuiPickersUtilsProvider } from "@material-ui/pickers"; -import MomentUtils from "@date-io/moment"; -import { BrowserRouter, Link, Route, Switch } from "react-router-dom" -import moment, { Moment } from "moment"; +import React, { Dispatch, HTMLProps, useEffect } from 'react'; +import { Link, Route, Switch } from "react-router-dom" +import moment from "moment"; import { route, routes } from "@/routing"; -import { Provider, useDispatch, useSelector } from "react-redux"; -import store, { persistor } from "@/state/store"; +import { useDispatch, useSelector } from "react-redux"; import { AppState } from "@/state/reducer"; import { StudentAction, StudentActions } from "@/state/actions/student"; import { sampleStudent } from "@/provider/dummy/student"; @@ -16,13 +11,9 @@ import { Student } from "@/data"; import '@/styles/overrides.scss' import '@/styles/header.scss' import classNames from "classnames"; -import { PersistGate } from 'redux-persist/integration/react'; - -class LocalizedMomentUtils extends MomentUtils { - getDatePickerHeaderText(date: Moment): string { - return this.format(date, "d MMM yyyy"); - } -} +import { EditionAction, EditionActions } from "@/state/actions/edition"; +import { sampleEdition } from "@/provider/dummy/edition"; +import { Edition } from "@/data/edition"; const UserMenu = (props: HTMLProps) => { const student = useSelector(state => state.student as Student); @@ -40,14 +31,14 @@ const UserMenu = (props: HTMLProps) => { dispatch({ type: StudentActions.Logout }) } - return
      + return
        { student ? <> - logged in as {{ name: `${student.name} ${student.surname}` }} - {' '} - ({ t('logout') }) + logged in as { { name: `${ student.name } ${ student.surname }` } } + { ' ' } + ({ t('logout') }) : <> - { t('login') } + { t('login') } }
      ; @@ -75,39 +66,38 @@ const LanguageSwitcher = ({ className, ...props }: HTMLProps) } function App() { - return ( - - - - - - -
      - -
      - - -
      -
      - { routes.map(({ name, content, ...route }) => { content() }) } -
      -
      -
      -
      -
      -
      - ); + const dispatch = useDispatch>(); + const edition = useSelector(state => state.edition); + + useEffect(() => { + if (!edition) { + dispatch({ type: EditionActions.Set, edition: sampleEdition }); + } + }) + + const isReady = !!edition; + + return <> +
      + +
      + + +
      +
      + { isReady && { routes.map(({ name, content, ...route }) => { content() }) } } + ; } export default App; diff --git a/src/data/edition.ts b/src/data/edition.ts new file mode 100644 index 0000000..8a29c94 --- /dev/null +++ b/src/data/edition.ts @@ -0,0 +1,21 @@ +import { Moment } from "moment"; + +export type Edition = { + startDate: Moment; + endDate: Moment; + proposalDeadline: Moment; +} + +export type Deadlines = { + personalData?: Moment; + proposal?: Moment; + personalPlan?: Moment; + report?: Moment; +} + +export function getEditionDeadlines(edition: Edition): Deadlines { + return { + proposal: edition.proposalDeadline, + personalPlan: edition.proposalDeadline, + } +} diff --git a/src/data/student.ts b/src/data/student.ts index 5fb3221..b8eabfc 100644 --- a/src/data/student.ts +++ b/src/data/student.ts @@ -11,3 +11,18 @@ export interface Student extends Identifiable { semester: Semester; course: Course; } + +export function isStudentDataComplete(student: Student): boolean { + return getMissingStudentData(student).length === 0; +} + +export function getMissingStudentData(student: Student): (keyof Student)[] { + return [ + !!student.name || "name", + !!student.surname || "surname", + !!student.email || "email", + !!student.albumNumber || "albumNumber", + !!student.semester || "semester", + !!student.course || "course", + ].filter(x => x !== true) as (keyof Student)[]; +} diff --git a/src/index.tsx b/src/index.tsx index 4bbd47a..2c53768 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -2,10 +2,37 @@ import React from 'react'; import ReactDOM from 'react-dom'; import "./i18n" import App from './app'; +import { Provider } from "react-redux"; +import store, { persistor } from "@/state/store"; +import { PersistGate } from "redux-persist/integration/react"; +import { MuiThemeProvider as ThemeProvider, StylesProvider } from "@material-ui/core/styles"; +import { MuiPickersUtilsProvider } from "@material-ui/pickers"; +import moment, { Moment } from "moment"; +import { studentTheme } from "@/ui/theme"; +import { BrowserRouter } from "react-router-dom"; +import MomentUtils from "@date-io/moment"; + +class LocalizedMomentUtils extends MomentUtils { + getDatePickerHeaderText(date: Moment): string { + return this.format(date, "d MMM yyyy"); + } +} ReactDOM.render( - + + + + + + + + + + + + + , document.getElementById('root') ); diff --git a/src/pages/main.tsx b/src/pages/main.tsx index a2c371f..2e6bb4b 100644 --- a/src/pages/main.tsx +++ b/src/pages/main.tsx @@ -5,6 +5,10 @@ import { Link as RouterLink } from "react-router-dom"; import { route } from "@/routing"; import { useTranslation } from "react-i18next"; import moment, { Moment } from "moment"; +import { useSelector } from "react-redux"; +import { AppState } from "@/state/reducer"; +import { getMissingStudentData, Student } from "@/data"; +import { Deadlines, Edition, getEditionDeadlines } from "@/data/edition"; type StepProps = StepperStepProps & { until?: Moment; @@ -18,7 +22,7 @@ const Step = ({ until, label, completedOn, children, completed, ...props }: Step const { t } = useTranslation(); const isLate = useMemo(() => until?.isBefore(completedOn || now), [completedOn, until]); - const left = useMemo(() => moment.duration(now.diff(until)), [until]); + const left = useMemo(() => moment.duration(now.diff(until)), [until]); return @@ -26,7 +30,8 @@ const Step = ({ until, label, completedOn, children, completed, ...props }: Step { until && { t('until', { date: until.format("DD MMMM YYYY") }) } - { isLate && - { t('late', { by: moment.duration(now.diff(until)).humanize() }) } } + { isLate && - { t('late', { by: moment.duration(now.diff(until)).humanize() }) } } { !isLate && !completed && - { t('left', { left: left.humanize() }) } } } @@ -38,19 +43,47 @@ const Step = ({ until, label, completedOn, children, completed, ...props }: Step export const MainPage = () => { const { t } = useTranslation(); + const student = useSelector(state => state.student); + const deadlines = useSelector(state => getEditionDeadlines(state.edition as Edition)); // edition cannot be null at this point + + const missingStudentData = useMemo(() => student ? getMissingStudentData(student) : [], [student]); + return { t("sections.my-internship.header") } - - + + { missingStudentData.length > 0 && <> +

      { t('steps.personal-data.info') }

      + +
        + { missingStudentData.map(field =>
      • { t(`student.${field}`) }
      • ) } +
      + + + } +
      + +

      { t('steps.internship-proposal.info') }

      +
      - - - + +

      { t('steps.plan.info') }

      + + + +
      + +
      diff --git a/src/provider/dummy/edition.ts b/src/provider/dummy/edition.ts new file mode 100644 index 0000000..290a3ac --- /dev/null +++ b/src/provider/dummy/edition.ts @@ -0,0 +1,8 @@ +import { Edition } from "@/data/edition"; +import moment from "moment"; + +export const sampleEdition: Edition = { + startDate: moment("2020-07-01"), + endDate: moment("2020-09-30"), + proposalDeadline: moment("2020-07-31") +} diff --git a/src/state/actions/edition.ts b/src/state/actions/edition.ts new file mode 100644 index 0000000..142220d --- /dev/null +++ b/src/state/actions/edition.ts @@ -0,0 +1,13 @@ +import { Action } from "@/state/actions/base"; +import { Edition } from "@/data/edition"; + +export enum EditionActions { + Set = 'SET', +} + +export interface SetAction extends Action { + edition: Edition, +} + +export type EditionAction = SetAction; + diff --git a/src/state/reducer/edition.ts b/src/state/reducer/edition.ts new file mode 100644 index 0000000..ef696f9 --- /dev/null +++ b/src/state/reducer/edition.ts @@ -0,0 +1,17 @@ +import { Edition } from "@/data/edition"; +import { EditionAction, EditionActions } from "@/state/actions/edition"; + +export type EditionState = Edition | null; + +const initialEditionState: EditionState = null; + +const editionReducer = (state: EditionState = initialEditionState, action: EditionAction): EditionState => { + switch (action.type) { + case EditionActions.Set: + return action.edition; + } + + return state; +} + +export default editionReducer; diff --git a/src/state/reducer/index.ts b/src/state/reducer/index.ts index 0bd70e0..c839818 100644 --- a/src/state/reducer/index.ts +++ b/src/state/reducer/index.ts @@ -1,9 +1,11 @@ import { combineReducers } from "redux"; import studentReducer from "./student" +import editionReducer from "@/state/reducer/edition"; const rootReducer = combineReducers({ student: studentReducer, + edition: editionReducer, }) export type AppState = ReturnType; diff --git a/src/state/store.ts b/src/state/store.ts index 9fafb4b..f35ec6d 100644 --- a/src/state/store.ts +++ b/src/state/store.ts @@ -8,7 +8,8 @@ const store = createStore( persistReducer( { key: 'state', - storage: sessionStorage + storage: sessionStorage, + blacklist: ['edition'] }, rootReducer ), diff --git a/translations/en.yaml b/translations/en.yaml index d0bc877..fd6824a 100644 --- a/translations/en.yaml +++ b/translations/en.yaml @@ -7,6 +7,14 @@ until: until {{ date }} late: late by {{ by }} left: '{{ left }} left' +student: + name: first name + surname: last name + course: course + semester: semester + email: e-mail + albumNumber: album number + sections: my-internship: header: "My internship" @@ -14,8 +22,21 @@ sections: steps: personal-data: header: "Fill personal data" + info: > + Your profile is incomplete. In order to continue your internship you have to supply information given below. In + case of problem with providing those information - please contact with your internship coordinator of your course. internship-proposal: header: "Internship proposal" form: "Internship proposal form" + info: "" plan: header: "Individual Internship Plan" + info: "" + template: "Download template" + submit: "Submit Individual Internship Plan" + insurance: + header: "Insurance" + report: + header: "Internship report" + grade: + header: "Your grade" diff --git a/translations/pl.yaml b/translations/pl.yaml index b931255..a08a967 100644 --- a/translations/pl.yaml +++ b/translations/pl.yaml @@ -11,17 +11,36 @@ sections: my-internship: header: "Moja praktyka" +student: + name: imię + surname: mazwisko + course: kierunek + semester: semestr + email: adres e-mail + albumNumber: numer albumu + steps: personal-data: header: "Uzupełnienie informacji" + info: > + Twój profil jest niekompletny. W celu kontynuacji praktyki musisz uzupełnić informacje podane poniżej. Jeżeli masz + problem z uzupełnieniem tych informacji - skontaktuj się z koordynatorem praktyk dla Twojego kierunku. + form: "Uzupełnij dane" internship-proposal: header: "Zgłoszenie praktyki" + info: > + Przed podjęciem praktyki należy ją zgłosić. form: "Formularz zgłaszania praktyki" plan: header: "Indywidualny Program Praktyki" + info: "" + template: "Pobierz szablon" + submit: "Wyślij Indywidualny Plan Praktyki" report: header: "Raport z praktyki" grade: header: "Ocena z praktyki" insurance: - header: "Ubezpieczenie NWW" + header: "Ubezpieczenie NNW" + +contact-coordinator: "Skontaktuj się z koordynatorem" -- 2.45.2 From b9b63625f1b4e4f7de28a3fd9594de7502d3add2 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Wed, 22 Jul 2020 19:45:58 +0200 Subject: [PATCH 12/12] Add basic ipp submit form --- package.json | 1 + src/components/actions.tsx | 14 ++++++++++ src/pages/index.ts | 1 + src/pages/internship/plan.tsx | 52 +++++++++++++++++++++++++++++++++++ src/pages/main.tsx | 20 ++++++++------ src/routing.tsx | 2 ++ translations/en.yaml | 2 ++ translations/pl.yaml | 11 ++++++++ yarn.lock | 30 ++++++++++++++++++++ 9 files changed, 125 insertions(+), 8 deletions(-) create mode 100644 src/components/actions.tsx create mode 100644 src/pages/internship/plan.tsx diff --git a/package.json b/package.json index 5fe314f..f1d1f0f 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "html-webpack-plugin": "4.0.0-beta.11", "i18next": "^19.6.0", "i18next-browser-languagedetector": "^5.0.0", + "material-ui-dropzone": "^3.3.0", "moment": "^2.26.0", "node-sass": "^4.14.1", "optimize-css-assets-webpack-plugin": "5.0.3", diff --git a/src/components/actions.tsx b/src/components/actions.tsx new file mode 100644 index 0000000..5fdb03d --- /dev/null +++ b/src/components/actions.tsx @@ -0,0 +1,14 @@ +import React, { HTMLProps } from "react"; +import { makeStyles } from "@material-ui/core/styles"; + +export const Actions = (props: HTMLProps) => { + const classes = makeStyles(theme => ({ + root: { + "& > *": { + marginRight: theme.spacing(1) + } + } + }))(); + + return
      +} diff --git a/src/pages/index.ts b/src/pages/index.ts index c5f71a3..538a1bd 100644 --- a/src/pages/index.ts +++ b/src/pages/index.ts @@ -1,3 +1,4 @@ export * from "./internship/proposal"; export * from "./errors/not-found" export * from "./main" +export { Actions } from "@/components/actions"; diff --git a/src/pages/internship/plan.tsx b/src/pages/internship/plan.tsx new file mode 100644 index 0000000..992fd5c --- /dev/null +++ b/src/pages/internship/plan.tsx @@ -0,0 +1,52 @@ +import { Page } from "@/pages/base"; +import { Button, Container, FormHelperText, Grid, Link, Typography } from "@material-ui/core"; +import { Link as RouterLink } from "react-router-dom"; +import { route } from "@/routing"; +import React from "react"; +import { useTranslation } from "react-i18next"; +import { DropzoneArea } from "material-ui-dropzone"; +import { Description as DescriptionIcon } from "@material-ui/icons"; +import { Actions } from "@/components/actions"; + +export const SubmitPlanPage = () => { + const { t } = useTranslation(); + + return + + + { t('sections.my-internship.header') } + { t("steps.plan.submit") } + + { t("steps.plan.submit") } + + + + + { t('forms.plan.instructions') } + + + + + + + { t('forms.plan.dropzone-help') } + + + + + + + + + + + +} + +export default SubmitPlanPage; diff --git a/src/pages/main.tsx b/src/pages/main.tsx index 2e6bb4b..0b773d9 100644 --- a/src/pages/main.tsx +++ b/src/pages/main.tsx @@ -9,6 +9,8 @@ import { useSelector } from "react-redux"; import { AppState } from "@/state/reducer"; import { getMissingStudentData, Student } from "@/data"; import { Deadlines, Edition, getEditionDeadlines } from "@/data/edition"; +import { Description as DescriptionIcon } from "@material-ui/icons" +import { Actions } from "@/components/actions"; type StepProps = StepperStepProps & { until?: Moment; @@ -57,7 +59,7 @@ export const MainPage = () => {

      { t('steps.personal-data.info') }

        - { missingStudentData.map(field =>
      • { t(`student.${field}`) }
      • ) } + { missingStudentData.map(field =>
      • { t(`student.${ field }`) }
      • ) }
      - + + + + - + diff --git a/src/routing.tsx b/src/routing.tsx index d2a2274..adb533a 100644 --- a/src/routing.tsx +++ b/src/routing.tsx @@ -3,6 +3,7 @@ import { MainPage } from "@/pages/main"; import { RouteProps } from "react-router-dom"; import { InternshipProposalPage } from "@/pages/internship/proposal"; import { NotFoundPage } from "@/pages/errors/not-found"; +import SubmitPlanPage from "@/pages/internship/plan"; type Route = { name?: string; @@ -13,6 +14,7 @@ export const routes: Route[] = [ { name: "home", path: "/", exact: true, content: () => }, { name: "internship_proposal", path: "/internship/proposal", exact: true, content: () => }, + { name: "internship_plan", path: "/internship/plan", exact: true, content: () => }, // fallback route for 404 pages { name: "fallback", path: "*", content: () => } diff --git a/translations/en.yaml b/translations/en.yaml index fd6824a..ca1e2c6 100644 --- a/translations/en.yaml +++ b/translations/en.yaml @@ -7,6 +7,8 @@ until: until {{ date }} late: late by {{ by }} left: '{{ left }} left' +dropzone: "Drag and drop a file here or click to choose" + student: name: first name surname: last name diff --git a/translations/pl.yaml b/translations/pl.yaml index a08a967..8b9e395 100644 --- a/translations/pl.yaml +++ b/translations/pl.yaml @@ -7,10 +7,21 @@ until: do {{ date }} late: '{{ by }} spóźnienia' left: jeszcze {{ left }} +confirm: zatwierdź +go-back: wstecz + +dropzone: "Przeciągnij i upuść plik bądź kliknij, aby wybrać" + sections: my-internship: header: "Moja praktyka" +forms: + plan: + instructions: > + Wypełnij i zeskanuj Indywidualny Plan Praktyk a następnie wyślij go z pomocą tego formularza. + dropzone-help: Skan dokumentu w formacie PDF + student: name: imię surname: mazwisko diff --git a/yarn.lock b/yarn.lock index a96ad63..bad0fdf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1776,6 +1776,11 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +attr-accept@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.1.0.tgz#a231a854385d36ff7a99647bb77b33c8a5175aee" + integrity sha512-sLzVM3zCCmmDtDNhI0i96k6PUztkotSOXqE4kDGQt/6iDi5M+H0srjeF+QC6jN581l4X/Zq3Zu/tgcErEssavg== + autoprefixer@^9.6.1: version "9.8.0" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.0.tgz#68e2d2bef7ba4c3a65436f662d0a56a741e56511" @@ -3800,6 +3805,13 @@ file-loader@4.3.0: loader-utils "^1.2.3" schema-utils "^2.5.0" +file-selector@^0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.1.12.tgz#fe726547be219a787a9dcc640575a04a032b1fd0" + integrity sha512-Kx7RTzxyQipHuiqyZGf+Nz4vY9R1XGxuQl/hLoJwq+J4avk/9wxxgZyHKtbyIPJmbD4A66DWGYfyykWNpcYutQ== + dependencies: + tslib "^1.9.0" + file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" @@ -5491,6 +5503,15 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +material-ui-dropzone@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/material-ui-dropzone/-/material-ui-dropzone-3.3.0.tgz#5adb64e7ad71953eb29c7d0616b0b693904eb412" + integrity sha512-PBOj5IiGbLxEgNybO28xo381/qe0tMF55U715xGs8XLoyfjOFIoC29u4UL3+SWPYsaP/JIFwR4JW+h32xHugIg== + dependencies: + "@babel/runtime" "^7.4.4" + clsx "^1.0.2" + react-dropzone "^10.2.1" + md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -7360,6 +7381,15 @@ react-dom@^16.13.1: prop-types "^15.6.2" scheduler "^0.19.1" +react-dropzone@^10.2.1: + version "10.2.2" + resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-10.2.2.tgz#67b4db7459589a42c3b891a82eaf9ade7650b815" + integrity sha512-U5EKckXVt6IrEyhMMsgmHQiWTGLudhajPPG77KFSvgsMqNEHSyGpqWvOMc5+DhEah/vH4E1n+J5weBNLd5VtyA== + dependencies: + attr-accept "^2.0.0" + file-selector "^0.1.12" + prop-types "^15.7.2" + react-error-overlay@^6.0.7: version "6.0.7" resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108" -- 2.45.2