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"