Merge pull request #19 from CPSC319-Winter-term-2/redux-setup

Redux setup
This commit is contained in:
cth0604 2022-03-13 18:26:21 -07:00 committed by GitHub
commit 20e8c79bb9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 647 additions and 116 deletions

180
package-lock.json generated
View File

@ -12,11 +12,13 @@
"@emotion/styled": "^11.8.1", "@emotion/styled": "^11.8.1",
"@mui/icons-material": "^5.4.2", "@mui/icons-material": "^5.4.2",
"@mui/material": "^5.4.3", "@mui/material": "^5.4.3",
"@reduxjs/toolkit": "^1.8.0",
"@types/react-big-calendar": "^0.36.2", "@types/react-big-calendar": "^0.36.2",
"moment": "^2.29.1", "moment": "^2.29.1",
"react": "^17.0.2", "react": "^17.0.2",
"react-big-calendar": "^0.39.3", "react-big-calendar": "^0.39.3",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-redux": "^7.2.6",
"react-router-dom": "^6.2.1" "react-router-dom": "^6.2.1"
}, },
"devDependencies": { "devDependencies": {
@ -2208,6 +2210,27 @@
"url": "https://opencollective.com/popperjs" "url": "https://opencollective.com/popperjs"
} }
}, },
"node_modules/@reduxjs/toolkit": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.8.0.tgz",
"integrity": "sha512-cdfHWfcvLyhBUDicoFwG1u32JqvwKDxLxDd7zSmSoFw/RhYLOygIRtmaMjPRUUHmVmmAGAvquLLsKKU/677kSQ==",
"dependencies": {
"immer": "^9.0.7",
"redux": "^4.1.2",
"redux-thunk": "^2.4.1",
"reselect": "^4.1.5"
},
"peerDependencies": {
"react": "^16.9.0 || ^17.0.0 || 18.0.0-beta",
"react-redux": "^7.2.1 || ^8.0.0-beta"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-redux": {
"optional": true
}
"node_modules/@restart/hooks": { "node_modules/@restart/hooks": {
"version": "0.3.27", "version": "0.3.27",
"resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.3.27.tgz", "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.3.27.tgz",
@ -2574,6 +2597,15 @@
"integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==",
"dev": true "dev": true
}, },
"node_modules/@types/hoist-non-react-statics": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
"integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
"dependencies": {
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0"
}
},
"node_modules/@types/html-minifier-terser": { "node_modules/@types/html-minifier-terser": {
"version": "6.1.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
@ -2677,6 +2709,17 @@
"@types/react": "*" "@types/react": "*"
} }
}, },
"node_modules/@types/react-redux": {
"version": "7.1.23",
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.23.tgz",
"integrity": "sha512-D02o3FPfqQlfu2WeEYwh3x2otYd2Dk1o8wAfsA0B1C2AJEFxE663Ozu7JzuWbznGgW248NaOF6wsqCGNq9d3qw==",
"dependencies": {
"@types/hoist-non-react-statics": "^3.3.0",
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0",
"redux": "^4.0.0"
}
},
"node_modules/@types/react-router": { "node_modules/@types/react-router": {
"version": "5.1.18", "version": "5.1.18",
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.18.tgz", "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.18.tgz",
@ -4580,6 +4623,7 @@
"dependencies": { "dependencies": {
"anymatch": "~3.1.2", "anymatch": "~3.1.2",
"braces": "~3.0.2", "braces": "~3.0.2",
"fsevents": "~2.3.2",
"glob-parent": "~5.1.2", "glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0", "is-binary-path": "~2.1.0",
"is-glob": "~4.0.1", "is-glob": "~4.0.1",
@ -7748,6 +7792,12 @@
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"imagemin": "^7.0.1", "imagemin": "^7.0.1",
"imagemin-gifsicle": "^7.0.0",
"imagemin-mozjpeg": "^9.0.0",
"imagemin-optipng": "^8.0.0",
"imagemin-pngquant": "^9.0.2",
"imagemin-svgo": "^9.0.0",
"imagemin-webp": "^7.0.0",
"loader-utils": "^2.0.0", "loader-utils": "^2.0.0",
"object-assign": "^4.1.1", "object-assign": "^4.1.1",
"schema-utils": "^2.7.1" "schema-utils": "^2.7.1"
@ -8204,6 +8254,15 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/immer": {
"version": "9.0.12",
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.12.tgz",
"integrity": "sha512-lk7UNmSbAukB5B6dh9fnh5D0bJTOFKxVg2cyJWTYrWRfhLrLMBquONcUs3aFq507hNoIZEDDh8lb8UtOizSMhA==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
}
},
"node_modules/import-fresh": { "node_modules/import-fresh": {
"version": "3.3.0", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@ -10558,6 +10617,35 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
}, },
"node_modules/react-redux": {
"version": "7.2.6",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz",
"integrity": "sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ==",
"dependencies": {
"@babel/runtime": "^7.15.4",
"@types/react-redux": "^7.1.20",
"hoist-non-react-statics": "^3.3.2",
"loose-envify": "^1.4.0",
"prop-types": "^15.7.2",
"react-is": "^17.0.2"
},
"peerDependencies": {
"react": "^16.8.3 || ^17"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
},
"react-native": {
"optional": true
}
}
},
"node_modules/react-redux/node_modules/react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
},
"node_modules/react-lifecycles-compat": { "node_modules/react-lifecycles-compat": {
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
@ -10660,6 +10748,22 @@
"node": ">= 0.10" "node": ">= 0.10"
} }
}, },
"node_modules/redux": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/redux/-/redux-4.1.2.tgz",
"integrity": "sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==",
"dependencies": {
"@babel/runtime": "^7.9.2"
}
},
"node_modules/redux-thunk": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz",
"integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==",
"peerDependencies": {
"redux": "^4"
}
},
"node_modules/regenerate": { "node_modules/regenerate": {
"version": "1.4.2", "version": "1.4.2",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
@ -10810,6 +10914,11 @@
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
"dev": true "dev": true
}, },
"node_modules/reselect": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.5.tgz",
"integrity": "sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ=="
},
"node_modules/resolve": { "node_modules/resolve": {
"version": "1.22.0", "version": "1.22.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
@ -14381,6 +14490,15 @@
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz",
"integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==" "integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA=="
}, },
"@reduxjs/toolkit": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.8.0.tgz",
"integrity": "sha512-cdfHWfcvLyhBUDicoFwG1u32JqvwKDxLxDd7zSmSoFw/RhYLOygIRtmaMjPRUUHmVmmAGAvquLLsKKU/677kSQ==",
"requires": {
"immer": "^9.0.7",
"redux": "^4.1.2",
"redux-thunk": "^2.4.1",
"reselect": "^4.1.5"
"@restart/hooks": { "@restart/hooks": {
"version": "0.3.27", "version": "0.3.27",
"resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.3.27.tgz", "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.3.27.tgz",
@ -14640,6 +14758,15 @@
"integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==",
"dev": true "dev": true
}, },
"@types/hoist-non-react-statics": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
"integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
"requires": {
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0"
}
},
"@types/html-minifier-terser": { "@types/html-minifier-terser": {
"version": "6.1.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
@ -14743,6 +14870,17 @@
"@types/react": "*" "@types/react": "*"
} }
}, },
"@types/react-redux": {
"version": "7.1.23",
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.23.tgz",
"integrity": "sha512-D02o3FPfqQlfu2WeEYwh3x2otYd2Dk1o8wAfsA0B1C2AJEFxE663Ozu7JzuWbznGgW248NaOF6wsqCGNq9d3qw==",
"requires": {
"@types/hoist-non-react-statics": "^3.3.0",
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0",
"redux": "^4.0.0"
}
},
"@types/react-router": { "@types/react-router": {
"version": "5.1.18", "version": "5.1.18",
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.18.tgz", "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.18.tgz",
@ -18993,6 +19131,11 @@
"is-cwebp-readable": "^3.0.0" "is-cwebp-readable": "^3.0.0"
} }
}, },
"immer": {
"version": "9.0.12",
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.12.tgz",
"integrity": "sha512-lk7UNmSbAukB5B6dh9fnh5D0bJTOFKxVg2cyJWTYrWRfhLrLMBquONcUs3aFq507hNoIZEDDh8lb8UtOizSMhA=="
},
"import-fresh": { "import-fresh": {
"version": "3.3.0", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@ -20727,6 +20870,24 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
}, },
"react-redux": {
"version": "7.2.6",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz",
"integrity": "sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ==",
"requires": {
"@babel/runtime": "^7.15.4",
"@types/react-redux": "^7.1.20",
"hoist-non-react-statics": "^3.3.2",
"loose-envify": "^1.4.0",
"prop-types": "^15.7.2",
"react-is": "^17.0.2"
},
"dependencies": {
"react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
}
"react-lifecycles-compat": { "react-lifecycles-compat": {
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
@ -20808,6 +20969,20 @@
"resolve": "^1.9.0" "resolve": "^1.9.0"
} }
}, },
"redux": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/redux/-/redux-4.1.2.tgz",
"integrity": "sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==",
"requires": {
"@babel/runtime": "^7.9.2"
}
},
"redux-thunk": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz",
"integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==",
"requires": {}
},
"regenerate": { "regenerate": {
"version": "1.4.2", "version": "1.4.2",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
@ -20927,6 +21102,11 @@
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
"dev": true "dev": true
}, },
"reselect": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.5.tgz",
"integrity": "sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ=="
},
"resolve": { "resolve": {
"version": "1.22.0", "version": "1.22.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",

View File

@ -12,11 +12,13 @@
"@emotion/styled": "^11.8.1", "@emotion/styled": "^11.8.1",
"@mui/icons-material": "^5.4.2", "@mui/icons-material": "^5.4.2",
"@mui/material": "^5.4.3", "@mui/material": "^5.4.3",
"@reduxjs/toolkit": "^1.8.0",
"@types/react-big-calendar": "^0.36.2", "@types/react-big-calendar": "^0.36.2",
"moment": "^2.29.1", "moment": "^2.29.1",
"react": "^17.0.2", "react": "^17.0.2",
"react-big-calendar": "^0.39.3", "react-big-calendar": "^0.39.3",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-redux": "^7.2.6",
"react-router-dom": "^6.2.1" "react-router-dom": "^6.2.1"
}, },
"scripts": { "scripts": {

View File

@ -1,4 +1,4 @@
import React, { useState } from "react"; import React from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import "./styles.css"; import "./styles.css";
@ -15,8 +15,6 @@ import Theme from "./Theme";
import "./style/App.css"; import "./style/App.css";
const App: React.FC = () => { const App: React.FC = () => {
const [meetingInfoOpen, setMeetingInfoOpen] = useState(false);
return ( return (
<ThemeProvider theme={Theme}> <ThemeProvider theme={Theme}>
<Router> <Router>
@ -24,15 +22,12 @@ const App: React.FC = () => {
<Route path="/login" element={<Login />} /> <Route path="/login" element={<Login />} />
<Route element={<ProtectedRoute />}> <Route element={<ProtectedRoute />}>
<Route path="/" element={<Home />} /> <Route path="/" element={<Home />} />
<Route <Route path="/contacts" element={<Contacts />} />
path="/contacts" <Route path="/calendar" element={<Calendar />} />
element={<Contacts setMeetingInfoOpen={setMeetingInfoOpen} />}
/>
<Route path="/calendar" element={<CalendarPage />} />
</Route> </Route>
</Routes> </Routes>
</Router> </Router>
<MeetingDetails open={meetingInfoOpen} setOpen={setMeetingInfoOpen} /> <MeetingDetails />
</ThemeProvider> </ThemeProvider>
); );
}; };

View File

@ -2,12 +2,12 @@ import {useLocation, Outlet, Navigate} from "react-router-dom";
import useAuth from "./hooks/useAuth"; import useAuth from "./hooks/useAuth";
import Navbar from "./components/navbar/Navbar"; import Navbar from "./components/navbar/Navbar";
import Sidebar from "./components/sidebar/Sidebar"; import Sidebar from "./components/sidebar/Sidebar";
import React, {useState} from "react"; import React, { useState } from "react";
import { import {
MeetingStatus, MeetingStatus,
SidebarUserObj, SidebarUserObj,
} from "./components/sidebar/SidebarUser"; } from "./components/sidebar/SidebarUser";
import {Box} from "@mui/material"; import { Box } from "@mui/material";
const ProtectedRoute = () => { const ProtectedRoute = () => {
const auth= useAuth(); const auth= useAuth();
@ -37,22 +37,21 @@ const ProtectedRoute = () => {
{ id: 19, name: "Bob Q.", meetingStatus: MeetingStatus.ONLINE }, { id: 19, name: "Bob Q.", meetingStatus: MeetingStatus.ONLINE },
{ id: 20, name: "Bob R.", meetingStatus: MeetingStatus.ONLINE }, { id: 20, name: "Bob R.", meetingStatus: MeetingStatus.ONLINE },
]); ]);
return ( return auth?.isLoggedIn ? (
auth?.isLoggedIn <>
? <Navbar />
<> <Box id="drawer-container" sx={{ display: "flex", height: "100%" }}>
<Navbar /> <Box sx={{ flexGrow: 1 }}>
<Box id="drawer-container" sx={{ display: "flex", height: "100%" }}> <Outlet />
<Box sx={{ flexGrow: 1 }}>
<Outlet />
</Box>
<Box>
<Sidebar users={sidebarUsers} />
</Box>
</Box> </Box>
</> <Box>
: <Navigate to="/login" state={{ from: location }} replace /> <Sidebar users={sidebarUsers} />
</Box>
</Box>
</>
) : (
<Navigate to="/login" state={{ from: location }} replace />
); );
}; };
export default ProtectedRoute; export default ProtectedRoute;

View File

@ -0,0 +1,86 @@
const people = [
{
uuid: "0",
emailAddress: "cth0604@gmail.com",
name: "Taehee Choi",
role: "Front-end Dev",
},
{
uuid: "1",
emailAddress: "cth0604@gmail.com",
name: "Arthur Marques",
role: "Team Lead",
},
{
uuid: "2",
emailAddress: "cth0604@gmail.com",
name: "Mathew Balsdon",
role: "Front-end Dev",
},
{
uuid: "3",
emailAddress: "cth0604@gmail.com",
name: "Benson Lin",
role: "Back-end Dev",
},
{
uuid: "4",
emailAddress: "cth0604@gmail.com",
name: "Jincheng Lu",
role: "Front-end Dev",
},
{
uuid: "5",
emailAddress: "cth0604@gmail.com",
name: "Esteban Margaron",
role: "Back-end Dev",
},
{
uuid: "6",
emailAddress: "cth0604@gmail.com",
name: "Leanna Resurreccion",
role: "Back-end Dev",
},
];
const meetings = [
{
meetingId: "0",
liveParticipantIds: [],
registrantIds: ["0", "1", "2", "3", "4", "5", "6"],
start: "2022-03-13T17:00:00",
duration: 15,
timezone: "",
joinUrl: "",
topic: "Daily Scrum Meeting",
},
{
meetingId: "1",
liveParticipantIds: [],
registrantIds: ["0", "2", "4"],
start: "2022-03-13T17:30:00",
duration: 30,
timezone: "",
joinUrl: "",
topic: "Front-end Meeting",
},
{
meetingId: "2",
liveParticipantIds: [],
registrantIds: ["3", "5", "6"],
start: "2022-03-13T17:30:00",
duration: 30,
timezone: "",
joinUrl: "",
topic: "Back-end Meeting",
},
];
const team = {
user: "0",
manager: "1",
sameManager: ["2", "3", "4", "5", "6"],
directReports: [],
};
export { people, meetings, team };

View File

@ -1,8 +0,0 @@
import Status from "./Status";
interface ContactInfo {
name: string;
status: Status;
}
export default ContactInfo;

View File

@ -2,25 +2,19 @@ import { Box } from "@mui/material";
import React from "react"; import React from "react";
import Body from "./contacts-components/Body"; import Body from "./contacts-components/Body";
import ContactInfo from "./ContactInfo";
import Sidebar from "./contacts-components/Sidebar"; import Sidebar from "./contacts-components/Sidebar";
import UserLite from "../../api-bodies/UserLite";
interface Props { const Contacts: React.FC = () => {
setMeetingInfoOpen: (open: boolean) => void; const [contactSelected, setContactSelected] = React.useState<UserLite | null>(
} null
);
const Contacts: React.FC<Props> = (props) => {
const [contactSelected, setContactSelected] =
React.useState<ContactInfo | null>(null);
return ( return (
<Box sx={{ display: "flex", height: "100%" }}> <Box sx={{ display: "flex", height: "100%" }}>
<Sidebar setContactSelected={setContactSelected} /> <Sidebar setContactSelected={setContactSelected} />
{contactSelected !== null ? ( {contactSelected !== null ? (
<Body <Body contactSelected={contactSelected} />
contactSelected={contactSelected}
setMeetingInfoOpen={props.setMeetingInfoOpen}
/>
) : null} ) : null}
</Box> </Box>
); );

View File

@ -1,3 +1,4 @@
import DetailedMeeting from "../../api-bodies/DetailedMeeting";
import Status from "./Status"; import Status from "./Status";
const returnStatusColor = ( const returnStatusColor = (
@ -15,4 +16,10 @@ const returnStatusColor = (
} }
}; };
export { returnStatusColor }; const getMeetingDuration = (meeting: DetailedMeeting) => {
const startDate = new Date(meeting.start);
const endDate = new Date(startDate.getTime() + meeting.duration * 60000);
return `${startDate.toTimeString()} - ${endDate.toTimeString()}`;
};
export { returnStatusColor, getMeetingDuration };

View File

@ -2,11 +2,10 @@ import { Box, Toolbar } from "@mui/material";
import React from "react"; import React from "react";
import UpperBody from "./body-components/UpperBody"; import UpperBody from "./body-components/UpperBody";
import LowerBody from "./body-components/LowerBody"; import LowerBody from "./body-components/LowerBody";
import ContactInfo from "../ContactInfo"; import UserLite from "../../../api-bodies/UserLite";
interface Props { interface Props {
contactSelected: ContactInfo; contactSelected: UserLite;
setMeetingInfoOpen: (open: boolean) => void;
} }
const Body: React.FC<Props> = (props) => { const Body: React.FC<Props> = (props) => {
@ -21,10 +20,7 @@ const Body: React.FC<Props> = (props) => {
> >
<Toolbar /> <Toolbar />
<UpperBody contactInfo={props.contactSelected} /> <UpperBody contactInfo={props.contactSelected} />
<LowerBody <LowerBody contactInfo={props.contactSelected} />
contactInfo={props.contactSelected}
setMeetingInfoOpen={props.setMeetingInfoOpen}
/>
</Box> </Box>
); );
}; };

View File

@ -9,19 +9,17 @@ import {
} from "@mui/material"; } from "@mui/material";
import React from "react"; import React from "react";
import { ExpandLess, ExpandMore } from "@mui/icons-material"; import { ExpandLess, ExpandMore } from "@mui/icons-material";
import ContactInfo from "../ContactInfo";
import Status from "../Status";
import ContactItem from "./sidebar-components/ContactItem"; import ContactItem from "./sidebar-components/ContactItem";
import { useAppSelector } from "../../../redux/hooks";
const contacts: ContactInfo[] = [ import {
{ name: "Taehee ...", status: Status.Online }, selectFavoritesJSON,
{ name: "Jincheng ...", status: Status.Offline }, selectTeamJSON,
{ name: "Mathew ...", status: Status.InMeeting }, } from "../../../redux/slices/peopleSlice";
{ name: "Esteban ...", status: Status.Away }, import { selectFavorites } from "../../../redux/slices/favoritesSlice";
]; import UserLite from "../../../api-bodies/UserLite";
interface Props { interface Props {
setContactSelected: (contactInfo: ContactInfo) => void; setContactSelected: (contactInfo: UserLite) => void;
} }
const sidebarWidth = 240; const sidebarWidth = 240;
@ -30,6 +28,12 @@ const Sidebar: React.FC<Props> = (props) => {
const [favoritesOpen, setFavoritesOpen] = React.useState<boolean>(true); const [favoritesOpen, setFavoritesOpen] = React.useState<boolean>(true);
const [teamOpen, setTeamOpen] = React.useState<boolean>(false); const [teamOpen, setTeamOpen] = React.useState<boolean>(false);
const favorites = useAppSelector(selectFavorites);
const favoritesJSON = useAppSelector((state) =>
selectFavoritesJSON(state, favorites)
);
const teamJSON = useAppSelector(selectTeamJSON);
return ( return (
<Drawer <Drawer
variant="permanent" variant="permanent"
@ -49,13 +53,16 @@ const Sidebar: React.FC<Props> = (props) => {
<ListItemButton onClick={() => setFavoritesOpen(!favoritesOpen)}> <ListItemButton onClick={() => setFavoritesOpen(!favoritesOpen)}>
{favoritesOpen ? <ExpandLess /> : <ExpandMore />} {favoritesOpen ? <ExpandLess /> : <ExpandMore />}
<ListItemText primary="Favorites" sx={{ textAlign: "center" }} /> <ListItemText primary="Favorites" sx={{ textAlign: "center" }} />
<ListItemText primary="10" sx={{ textAlign: "right" }} /> <ListItemText
primary={favoritesJSON.length}
sx={{ textAlign: "right" }}
/>
</ListItemButton> </ListItemButton>
<Collapse in={favoritesOpen} timeout="auto" unmountOnExit> <Collapse in={favoritesOpen} timeout="auto" unmountOnExit>
<List disablePadding> <List disablePadding>
{contacts.map((contact, i) => ( {favoritesJSON.map((favorite, i) => (
<ContactItem <ContactItem
contactInfo={contact} contactInfo={favorite}
key={i} key={i}
setContactSelected={props.setContactSelected} setContactSelected={props.setContactSelected}
/> />
@ -65,13 +72,13 @@ const Sidebar: React.FC<Props> = (props) => {
<ListItemButton onClick={() => setTeamOpen(!teamOpen)}> <ListItemButton onClick={() => setTeamOpen(!teamOpen)}>
{teamOpen ? <ExpandLess /> : <ExpandMore />} {teamOpen ? <ExpandLess /> : <ExpandMore />}
<ListItemText primary="Team" sx={{ textAlign: "center" }} /> <ListItemText primary="Team" sx={{ textAlign: "center" }} />
<ListItemText primary="10" sx={{ textAlign: "right" }} /> <ListItemText primary={teamJSON.length} sx={{ textAlign: "right" }} />
</ListItemButton> </ListItemButton>
<Collapse in={teamOpen} timeout="auto" unmountOnExit> <Collapse in={teamOpen} timeout="auto" unmountOnExit>
<List disablePadding> <List disablePadding>
{contacts.map((contact, i) => ( {teamJSON.map((member, i) => (
<ContactItem <ContactItem
contactInfo={contact} contactInfo={member}
key={i} key={i}
setContactSelected={props.setContactSelected} setContactSelected={props.setContactSelected}
/> />

View File

@ -1,21 +1,22 @@
import { Box, Button, List, Typography } from "@mui/material"; import { Box, Button, List, Typography } from "@mui/material";
import React from "react"; import React from "react";
import ContactInfo from "../../ContactInfo"; import UserLite from "../../../../api-bodies/UserLite";
import { useAppSelector, useAppDispatch } from "../../../../redux/hooks";
const meetings: { name: string; duration: string }[] = [ import { open } from "../../../../redux/slices/meetingDetailsOpenSlice";
{ name: "Kanban Meeting", duration: "10:45 am - 11:45 am" }, import { selectUserUpcomingMeetings } from "../../../../redux/slices/meetingsSlice";
{ name: "Design Meeting", duration: "10:45 am - 11:45 am" }, import { getMeetingDuration } from "../../Utils";
{ name: "Customer Meeting", duration: "10:45 am - 11:45 am" },
{ name: "Some kind of Meeting", duration: "10:45 am - 11:45 am" },
{ name: "Some kind of Meeting", duration: "10:45 am - 11:45 am" },
];
interface Props { interface Props {
contactInfo: ContactInfo; contactInfo: UserLite;
setMeetingInfoOpen: (open: boolean) => void;
} }
const LowerBody: React.FC<Props> = (props) => { const LowerBody: React.FC<Props> = (props) => {
const dispatch = useAppDispatch();
const meetings = useAppSelector((state) =>
selectUserUpcomingMeetings(state, props.contactInfo.uuid)
);
return ( return (
<Box <Box
sx={{ sx={{
@ -47,11 +48,13 @@ const LowerBody: React.FC<Props> = (props) => {
<Button <Button
variant="text" variant="text"
color="info" color="info"
onClick={() => props.setMeetingInfoOpen(true)} onClick={() => dispatch(open(meeting))}
> >
{meeting.name} {meeting.topic}
</Button> </Button>
<Typography sx={{ pt: 1.5 }}>{meeting.duration}</Typography> <Typography sx={{ pt: 1.5 }}>
{getMeetingDuration(meeting)}
</Typography>
</Box> </Box>
))} ))}
</List> </List>

View File

@ -3,11 +3,12 @@ import React from "react";
import PhoneIcon from "@mui/icons-material/Phone"; import PhoneIcon from "@mui/icons-material/Phone";
import VideocamIcon from "@mui/icons-material/Videocam"; import VideocamIcon from "@mui/icons-material/Videocam";
import GroupsIcon from "@mui/icons-material/Groups"; import GroupsIcon from "@mui/icons-material/Groups";
import ContactInfo from "../../ContactInfo";
import AddIcon from "@mui/icons-material/Add"; import AddIcon from "@mui/icons-material/Add";
import Status from "../../Status";
import UserLite from "../../../../api-bodies/UserLite";
interface Props { interface Props {
contactInfo: ContactInfo; contactInfo: UserLite;
} }
const UpperBody: React.FC<Props> = (props) => { const UpperBody: React.FC<Props> = (props) => {
@ -63,9 +64,7 @@ const UpperBody: React.FC<Props> = (props) => {
</IconButton> </IconButton>
</Box> </Box>
<Box sx={{ display: "flex", flexDirection: "column" }}> <Box sx={{ display: "flex", flexDirection: "column" }}>
<Typography sx={{ textAlign: "right" }}> <Typography sx={{ textAlign: "right" }}>{Status.Online}</Typography>
{props.contactInfo.status}
</Typography>
<Typography>MeetingName-1372</Typography> <Typography>MeetingName-1372</Typography>
</Box> </Box>
</Box> </Box>

View File

@ -2,12 +2,13 @@ import { ListItemButton, ListItemIcon, ListItemText } from "@mui/material";
import React from "react"; import React from "react";
import PersonOutlineIcon from "@mui/icons-material/PersonOutline"; import PersonOutlineIcon from "@mui/icons-material/PersonOutline";
import CircleIcon from "@mui/icons-material/Circle"; import CircleIcon from "@mui/icons-material/Circle";
import ContactInfo from "../../ContactInfo";
import { returnStatusColor } from "../../Utils"; import { returnStatusColor } from "../../Utils";
import UserLite from "../../../../api-bodies/UserLite";
import Status from "../../Status";
interface Props { interface Props {
contactInfo: ContactInfo; contactInfo: UserLite;
setContactSelected: (contactInfo: ContactInfo) => void; setContactSelected: (contactInfo: UserLite) => void;
} }
const ContactItem: React.FC<Props> = (props) => { const ContactItem: React.FC<Props> = (props) => {
@ -22,10 +23,10 @@ const ContactItem: React.FC<Props> = (props) => {
</ListItemIcon> </ListItemIcon>
<ListItemText <ListItemText
primary={props.contactInfo.name} primary={props.contactInfo.name}
secondary={props.contactInfo.status} secondary={Status.Online}
sx={{ flexGrow: 1 }} sx={{ flexGrow: 1 }}
/> />
<CircleIcon color={returnStatusColor(props.contactInfo.status)} /> <CircleIcon color={returnStatusColor(Status.Online)} />
</ListItemButton> </ListItemButton>
); );
}; };

View File

@ -13,9 +13,6 @@ interface LocationState {
from: { pathname: string }; from: { pathname: string };
} }
const Login: React.FC = () => { const Login: React.FC = () => {
const setAuth = useAuth();
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const state = location.state as LocationState; const state = location.state as LocationState;
@ -50,7 +47,7 @@ const Login: React.FC = () => {
return ( return (
<Container className="login"> <Container className="login">
<Stack className="grid-container" spacing={2}> <Stack className="grid-container" spacing={2}>
<img className="login-logo" src={hsbcLogo} alt="HSBC Logo"/> <img className="login-logo" src={hsbcLogo} alt="HSBC Logo" />
{/* <p ref={errRef} className={errMsg ? "errmsg" : "offscreen"} aria-live="assertive">{errMsg}</p> */} {/* <p ref={errRef} className={errMsg ? "errmsg" : "offscreen"} aria-live="assertive">{errMsg}</p> */}
<TextField className="username-input" id="outlined-basic" label="Username" variant="outlined" placeholder="Username" onChange={(event) => {setUsername(event.target.value);}}/> <TextField className="username-input" id="outlined-basic" label="Username" variant="outlined" placeholder="Username" onChange={(event) => {setUsername(event.target.value);}}/>
<TextField className="password-input" id="outlined-basic" label="Password" variant="outlined" placeholder="Password" type="password" onChange={(event) => {setPassword(event.target.value);}}/> <TextField className="password-input" id="outlined-basic" label="Password" variant="outlined" placeholder="Password" type="password" onChange={(event) => {setPassword(event.target.value);}}/>
@ -58,17 +55,25 @@ const Login: React.FC = () => {
<a className="register-btn" href="#"> <a className="register-btn" href="#">
<Typography sx={{ ml: 1, mb: 1 }}>Forgot your password?</Typography> <Typography sx={{ ml: 1, mb: 1 }}>Forgot your password?</Typography>
</a> </a>
<Button endIcon={<LoginIcon />} className="login-btn" variant="contained" type="submit" onClick={handleLogin}>Login</Button> <Button
endIcon={<LoginIcon />}
className="login-btn"
variant="contained"
type="submit"
onClick={handleLogin}
>
Login
</Button>
</Stack> </Stack>
<Stack direction="row" justifyContent="space-between" spacing={2}> <Stack direction="row" justifyContent="space-between" spacing={2}>
<Typography sx={{ ml: 1 }}>Login with</Typography> <Typography sx={{ ml: 1 }}>Login with</Typography>
<a className="zoom-logo" href="#"> <a className="zoom-logo" href="#">
<img src={zoomLogo} alt="Zoom Logo"/> <img src={zoomLogo} alt="Zoom Logo" />
</a> </a>
</Stack> </Stack>
</Stack> </Stack>
</Container> </Container>
); );
}; };
export default Login; export default Login;

View File

@ -2,17 +2,24 @@ import { Dialog } from "@mui/material";
import React from "react"; import React from "react";
import Body from "./meeting-details-components/Body"; import Body from "./meeting-details-components/Body";
import Header from "./meeting-details-components/Header"; import Header from "./meeting-details-components/Header";
import { useAppSelector, useAppDispatch } from "../../redux/hooks";
import {
close,
selectMeetingDetailsOpen,
} from "../../redux/slices/meetingDetailsOpenSlice";
interface Props { const MeetingDetails: React.FC = () => {
open: boolean; const meetingDetailsOpen = useAppSelector(selectMeetingDetailsOpen);
setOpen: (open: boolean) => void; const dispatch = useAppDispatch();
}
const MeetingDetails: React.FC<Props> = (props) => {
return ( return (
<Dialog fullScreen open={props.open} onClose={() => props.setOpen(false)}> <Dialog
<Header setOpen={props.setOpen} /> fullScreen
<Body /> open={meetingDetailsOpen.open}
onClose={() => dispatch(close())}
>
<Header meeting={meetingDetailsOpen.meeting} />
<Body meeting={meetingDetailsOpen.meeting} />
</Dialog> </Dialog>
); );
}; };

View File

@ -8,8 +8,13 @@ import {
} from "@mui/material"; } from "@mui/material";
import React from "react"; import React from "react";
import PersonOutlineIcon from "@mui/icons-material/PersonOutline"; import PersonOutlineIcon from "@mui/icons-material/PersonOutline";
import DetailedMeeting from "../../../api-bodies/DetailedMeeting";
const Body: React.FC = () => { interface Props {
meeting: DetailedMeeting | null;
}
const Body: React.FC<Props> = () => {
return ( return (
<Box <Box
sx={{ sx={{

View File

@ -1,17 +1,22 @@
import { AppBar, Button, IconButton, Toolbar, Typography } from "@mui/material"; import { AppBar, Button, IconButton, Toolbar, Typography } from "@mui/material";
import React from "react"; import React from "react";
import CloseIcon from "@mui/icons-material/Close"; import CloseIcon from "@mui/icons-material/Close";
import { useAppDispatch } from "../../../redux/hooks";
import { close } from "../../../redux/slices/meetingDetailsOpenSlice";
import DetailedMeeting from "../../../api-bodies/DetailedMeeting";
interface Props { interface Props {
setOpen: (open: boolean) => void; meeting: DetailedMeeting | null;
} }
const Header: React.FC<Props> = (props) => { const Header: React.FC<Props> = (props) => {
const dispatch = useAppDispatch();
return ( return (
<AppBar sx={{ position: "relative" }}> <AppBar sx={{ position: "relative" }}>
<Toolbar> <Toolbar>
<Typography variant="h6" sx={{ flexGrow: 1 }}> <Typography variant="h6" sx={{ flexGrow: 1 }}>
Kanban Meeting {props.meeting !== null ? props.meeting.topic : null}
</Typography> </Typography>
<Button variant="contained" color="secondary"> <Button variant="contained" color="secondary">
Join Join
@ -23,7 +28,7 @@ const Header: React.FC<Props> = (props) => {
edge="start" edge="start"
color="inherit" color="inherit"
aria-label="close" aria-label="close"
onClick={() => props.setOpen(false)} onClick={() => dispatch(close())}
sx={{ ml: 1 }} sx={{ ml: 1 }}
> >
<CloseIcon /> <CloseIcon />

View File

@ -2,11 +2,24 @@ import ReactDOM from "react-dom";
import App from "./App"; import App from "./App";
import "./style/App.css"; import "./style/App.css";
import { AuthProvider } from "./context/AuthProvider"; import { AuthProvider } from "./context/AuthProvider";
import { Provider } from "react-redux";
import { store } from "./redux/store";
import { fetchFavorites } from "./redux/slices/favoritesSlice";
import { fetchTeam } from "./redux/slices/teamSlice";
import { fetchMeetings } from "./redux/slices/meetingsSlice";
import { fetchPeople } from "./redux/slices/peopleSlice";
const Index:React.FC = () => { store.dispatch(fetchPeople(""));
store.dispatch(fetchFavorites(""));
store.dispatch(fetchTeam(""));
store.dispatch(fetchMeetings(""));
const Index: React.FC = () => {
return ( return (
<AuthProvider> <AuthProvider>
<App /> <Provider store={store}>
<App />
</Provider>
</AuthProvider> </AuthProvider>
); );
}; };

6
src/redux/hooks.tsx Normal file
View File

@ -0,0 +1,6 @@
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { RootState, AppDispatch } from "./store";
// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

View File

@ -0,0 +1,55 @@
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { RootState } from "../store";
interface FavoritesState {
favorites: string[];
}
const initialState: FavoritesState = {
favorites: [],
};
export const favoritesSlice = createSlice({
name: "favorites",
initialState,
reducers: {},
extraReducers(builder) {
builder
.addCase(fetchFavorites.fulfilled, (state, action) => {
state.favorites = action.payload;
})
.addCase(addFavorite.fulfilled, (state, action) => {
state.favorites.push(action.payload);
})
.addCase(removeFavorite.fulfilled, (state, action) => {
const index = state.favorites.indexOf(action.payload);
state.favorites.splice(index, 1);
});
},
});
export const fetchFavorites = createAsyncThunk(
"favorites/fetchFavorites",
async (uuid: string) => {
// const response = await client.post("/fakeApi/posts", initialPost);
// !!!
console.log(uuid);
return ["2", "4"];
}
);
export const addFavorite = createAsyncThunk(
"favorites/addFavorite",
async (uuid: string) => {
// const response = await client.post("/fakeApi/posts", initialPost);
return uuid;
}
);
export const removeFavorite = createAsyncThunk(
"favorites/removeFavorite",
async (uuid: string) => {
// const response = await client.post("/fakeApi/posts", initialPost);
return uuid;
}
);
export const selectFavorites = (state: RootState) => state.favorites.favorites;
export default favoritesSlice.reducer;

View File

@ -0,0 +1,33 @@
import { createSlice } from "@reduxjs/toolkit";
import DetailedMeeting from "../../api-bodies/DetailedMeeting";
import { RootState } from "../store";
interface MeetingDetailsOpenState {
open: boolean;
meeting: DetailedMeeting | null;
}
const initialState: MeetingDetailsOpenState = {
open: false,
meeting: null,
};
export const meetingDetailsOpenSlice = createSlice({
name: "meetingDetailsOpen",
initialState,
reducers: {
open: (state, action) => {
state.open = true;
state.meeting = action.payload;
},
close: (state) => {
state.open = false;
state.meeting = null;
},
},
});
export const { open, close } = meetingDetailsOpenSlice.actions;
export const selectMeetingDetailsOpen = (state: RootState) =>
state.meetingDetailsOpen;
export default meetingDetailsOpenSlice.reducer;

View File

@ -0,0 +1,41 @@
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import DetailedMeeting from "../../api-bodies/DetailedMeeting";
import { RootState } from "../store";
import { meetings } from "../../api-bodies/MockData";
interface MeetingsState {
meetings: DetailedMeeting[];
}
const initialState: MeetingsState = {
meetings: [],
};
export const meetingsSlice = createSlice({
name: "meetings",
initialState,
reducers: {},
extraReducers(builder) {
builder.addCase(fetchMeetings.fulfilled, (state, action) => {
return action.payload;
});
},
});
export const fetchMeetings = createAsyncThunk(
"meetings/fetchMeetings",
async (uuid: string) => {
// const response = await client.post("/fakeApi/posts", initialPost);
// !!!
console.log(uuid);
return { meetings: meetings };
}
);
export const selectMeetings = (state: RootState) => state.meetings.meetings;
export const selectUserUpcomingMeetings = (state: RootState, uuid: string) => {
return state.meetings.meetings.filter((meeting) =>
meeting.registrantIds.includes(uuid)
);
};
export default meetingsSlice.reducer;

View File

@ -0,0 +1,40 @@
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import UserLite from "../../api-bodies/UserLite";
import { RootState } from "../store";
import { people } from "../../api-bodies/MockData";
interface PeopleState {
people: UserLite[];
}
const initialState: PeopleState = {
people: [],
};
export const peopleSlice = createSlice({
name: "people",
initialState,
reducers: {},
extraReducers(builder) {
builder.addCase(fetchPeople.fulfilled, (state, action) => {
console.log(action.payload);
state.people = action.payload;
});
},
});
export const fetchPeople = createAsyncThunk(
"people/fetchPeople",
async (uuid: string) => {
// const response = await client.post("/fakeApi/posts", initialPost);
// !!!
console.log(uuid);
return people;
}
);
export const selectFavoritesJSON = (state: RootState, favorites: string[]) => {
return state.people.people.filter((p) => favorites.includes(p.uuid));
};
export const selectTeamJSON = (state: RootState) => state.people.people;
export default peopleSlice.reducer;

View File

@ -0,0 +1,41 @@
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { RootState } from "../store";
import { team } from "../../api-bodies/MockData";
interface TeamState {
user: string | null;
manager: string | null;
sameManager: string[];
directReports: string[];
}
const initialState: TeamState = {
user: null,
manager: null,
sameManager: [],
directReports: [],
};
export const teamSlice = createSlice({
name: "team",
initialState,
reducers: {},
extraReducers(builder) {
builder.addCase(fetchTeam.fulfilled, (state, action) => {
return action.payload;
});
},
});
export const fetchTeam = createAsyncThunk(
"team/fetchTeam",
async (uuid: string) => {
// const response = await client.post("/fakeApi/posts", initialPost);
// !!!
console.log(uuid);
return team;
}
);
export const selectTeam = (state: RootState) => state.team;
export default teamSlice.reducer;

19
src/redux/store.tsx Normal file
View File

@ -0,0 +1,19 @@
import { configureStore } from "@reduxjs/toolkit";
import meetingDetailsOpenReducer from "./slices/meetingDetailsOpenSlice";
import favoritesReducer from "./slices/favoritesSlice";
import teamReducer from "./slices/teamSlice";
import meetingsReducer from "./slices/meetingsSlice";
import peopleReducer from "./slices/peopleSlice";
export const store = configureStore({
reducer: {
meetingDetailsOpen: meetingDetailsOpenReducer,
favorites: favoritesReducer,
team: teamReducer,
meetings: meetingsReducer,
people: peopleReducer,
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;