Merge branch 'main' into home

This commit is contained in:
Jincheng Lu 2022-03-25 15:54:13 -07:00 committed by GitHub
commit 3ed74c31cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 692 additions and 309 deletions

218
package-lock.json generated
View File

@ -20,7 +20,8 @@
"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-redux": "^7.2.6",
"react-router-dom": "^6.2.1" "react-router-dom": "^6.2.1",
"socket.io-client": "^4.4.1"
}, },
"devDependencies": { "devDependencies": {
"@svgr/webpack": "^5.5.0", "@svgr/webpack": "^5.5.0",
@ -2255,6 +2256,19 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/@socket.io/base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/@socket.io/component-emitter": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz",
"integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q=="
},
"node_modules/@svgr/babel-plugin-add-jsx-attribute": { "node_modules/@svgr/babel-plugin-add-jsx-attribute": {
"version": "5.4.0", "version": "5.4.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz",
@ -3604,6 +3618,11 @@
"@babel/core": "^7.0.0-0" "@babel/core": "^7.0.0-0"
} }
}, },
"node_modules/backo2": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
},
"node_modules/balanced-match": { "node_modules/balanced-match": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -5897,6 +5916,53 @@
"once": "^1.4.0" "once": "^1.4.0"
} }
}, },
"node_modules/engine.io-client": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.1.tgz",
"integrity": "sha512-V05mmDo4gjimYW+FGujoGmmmxRaDsrVr7AXA3ZIfa04MWM1jOfZfUwou0oNqhNwy/votUDvGDt4JA4QF4e0b4g==",
"dependencies": {
"@socket.io/component-emitter": "~3.0.0",
"debug": "~4.3.1",
"engine.io-parser": "~5.0.0",
"has-cors": "1.1.0",
"parseqs": "0.0.6",
"parseuri": "0.0.6",
"ws": "~8.2.3",
"xmlhttprequest-ssl": "~2.0.0",
"yeast": "0.1.2"
}
},
"node_modules/engine.io-client/node_modules/ws": {
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/engine.io-parser": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz",
"integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==",
"dependencies": {
"@socket.io/base64-arraybuffer": "~1.0.2"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/enhanced-resolve": { "node_modules/enhanced-resolve": {
"version": "4.5.0", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz",
@ -7430,6 +7496,11 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/has-cors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
},
"node_modules/has-flag": { "node_modules/has-flag": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@ -10017,6 +10088,16 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/parseqs": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
},
"node_modules/parseuri": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
},
"node_modules/parseurl": { "node_modules/parseurl": {
"version": "1.3.3", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@ -11426,6 +11507,34 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true "dev": true
}, },
"node_modules/socket.io-client": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.4.1.tgz",
"integrity": "sha512-N5C/L5fLNha5Ojd7Yeb/puKcPWWcoB/A09fEjjNsg91EDVr5twk/OEyO6VT9dlLSUNY85NpW6KBhVMvaLKQ3vQ==",
"dependencies": {
"@socket.io/component-emitter": "~3.0.0",
"backo2": "~1.0.2",
"debug": "~4.3.2",
"engine.io-client": "~6.1.1",
"parseuri": "0.0.6",
"socket.io-parser": "~4.1.1"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-parser": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.2.tgz",
"integrity": "sha512-j3kk71QLJuyQ/hh5F/L2t1goqzdTL0gvDzuhTuNSwihfuFUrcSji0qFZmJJPtG6Rmug153eOPsUizeirf1IIog==",
"dependencies": {
"@socket.io/component-emitter": "~3.0.0",
"debug": "~4.3.1"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/sockjs": { "node_modules/sockjs": {
"version": "0.3.24", "version": "0.3.24",
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
@ -12971,6 +13080,14 @@
} }
} }
}, },
"node_modules/xmlhttprequest-ssl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/xtend": { "node_modules/xtend": {
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
@ -13006,6 +13123,11 @@
"fd-slicer": "~1.1.0" "fd-slicer": "~1.1.0"
} }
}, },
"node_modules/yeast": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
},
"node_modules/yn": { "node_modules/yn": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
@ -14491,6 +14613,16 @@
"dev": true, "dev": true,
"optional": true "optional": true
}, },
"@socket.io/base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ=="
},
"@socket.io/component-emitter": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz",
"integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q=="
},
"@svgr/babel-plugin-add-jsx-attribute": { "@svgr/babel-plugin-add-jsx-attribute": {
"version": "5.4.0", "version": "5.4.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz",
@ -15535,6 +15667,11 @@
"@babel/helper-define-polyfill-provider": "^0.3.1" "@babel/helper-define-polyfill-provider": "^0.3.1"
} }
}, },
"backo2": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
},
"balanced-match": { "balanced-match": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -17335,6 +17472,38 @@
"once": "^1.4.0" "once": "^1.4.0"
} }
}, },
"engine.io-client": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.1.tgz",
"integrity": "sha512-V05mmDo4gjimYW+FGujoGmmmxRaDsrVr7AXA3ZIfa04MWM1jOfZfUwou0oNqhNwy/votUDvGDt4JA4QF4e0b4g==",
"requires": {
"@socket.io/component-emitter": "~3.0.0",
"debug": "~4.3.1",
"engine.io-parser": "~5.0.0",
"has-cors": "1.1.0",
"parseqs": "0.0.6",
"parseuri": "0.0.6",
"ws": "~8.2.3",
"xmlhttprequest-ssl": "~2.0.0",
"yeast": "0.1.2"
},
"dependencies": {
"ws": {
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
"requires": {}
}
}
},
"engine.io-parser": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz",
"integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==",
"requires": {
"@socket.io/base64-arraybuffer": "~1.0.2"
}
},
"enhanced-resolve": { "enhanced-resolve": {
"version": "4.5.0", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz",
@ -18505,6 +18674,11 @@
"integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==",
"dev": true "dev": true
}, },
"has-cors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
},
"has-flag": { "has-flag": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@ -20404,6 +20578,16 @@
"lines-and-columns": "^1.1.6" "lines-and-columns": "^1.1.6"
} }
}, },
"parseqs": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
},
"parseuri": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
},
"parseurl": { "parseurl": {
"version": "1.3.3", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@ -21502,6 +21686,28 @@
} }
} }
}, },
"socket.io-client": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.4.1.tgz",
"integrity": "sha512-N5C/L5fLNha5Ojd7Yeb/puKcPWWcoB/A09fEjjNsg91EDVr5twk/OEyO6VT9dlLSUNY85NpW6KBhVMvaLKQ3vQ==",
"requires": {
"@socket.io/component-emitter": "~3.0.0",
"backo2": "~1.0.2",
"debug": "~4.3.2",
"engine.io-client": "~6.1.1",
"parseuri": "0.0.6",
"socket.io-parser": "~4.1.1"
}
},
"socket.io-parser": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.2.tgz",
"integrity": "sha512-j3kk71QLJuyQ/hh5F/L2t1goqzdTL0gvDzuhTuNSwihfuFUrcSji0qFZmJJPtG6Rmug153eOPsUizeirf1IIog==",
"requires": {
"@socket.io/component-emitter": "~3.0.0",
"debug": "~4.3.1"
}
},
"sockjs": { "sockjs": {
"version": "0.3.24", "version": "0.3.24",
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
@ -22643,6 +22849,11 @@
"dev": true, "dev": true,
"requires": {} "requires": {}
}, },
"xmlhttprequest-ssl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A=="
},
"xtend": { "xtend": {
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
@ -22672,6 +22883,11 @@
"fd-slicer": "~1.1.0" "fd-slicer": "~1.1.0"
} }
}, },
"yeast": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
},
"yn": { "yn": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",

View File

@ -20,7 +20,8 @@
"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-redux": "^7.2.6",
"react-router-dom": "^6.2.1" "react-router-dom": "^6.2.1",
"socket.io-client": "^4.4.1"
}, },
"scripts": { "scripts": {
"start-local": "webpack serve --config webpack.config.ts", "start-local": "webpack serve --config webpack.config.ts",

View File

@ -3,12 +3,21 @@ 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 { Box } from "@mui/material"; import { Box } from "@mui/material";
import { store } from "./redux/store";
import { fetchFavorites } from "./redux/slices/favoritesSlice";
import { fetchMeetings } from "./redux/slices/meetingsAndUserStatusSlice";
import { fetchUsers } from "./redux/slices/usersSlice";
const ProtectedRoute = () => { const ProtectedRoute = () => {
const auth = useAuth(); const auth = useAuth();
const location = useLocation(); const location = useLocation();
/* Temporary data */ /* Temporary data */
if (auth?.isLoggedIn != undefined && auth?.isLoggedIn == true) {
store.dispatch(fetchMeetings(auth?.uuid));
store.dispatch(fetchUsers(auth?.uuid));
store.dispatch(fetchFavorites(auth?.uuid));
}
return auth?.isLoggedIn ? ( return auth?.isLoggedIn ? (
<> <>
<Navbar /> <Navbar />

View File

@ -1,6 +1,6 @@
const userLites = [ const userLites = [
{ {
uuid: "0", uuid: "",
emailAddress: "cth0604@gmail.com", emailAddress: "cth0604@gmail.com",
name: "Taehee Choi", name: "Taehee Choi",
role: "Front-end Dev", role: "Front-end Dev",
@ -45,9 +45,9 @@ const userLites = [
const meetings = [ const meetings = [
{ {
meetingId: "0", meetingId: "",
liveParticipantIds: [], liveParticipantIds: [],
registrantIds: ["0", "1", "2", "3", "4", "5", "6"], registrantIds: ["", "1", "2", "3", "4", "5", "6"],
start: "2022-03-13T17:00:00", start: "2022-03-13T17:00:00",
duration: 15, duration: 15,
timezone: "", timezone: "",
@ -57,7 +57,7 @@ const meetings = [
{ {
meetingId: "1", meetingId: "1",
liveParticipantIds: [], liveParticipantIds: [],
registrantIds: ["0", "2", "4"], registrantIds: ["", "2", "4"],
start: "2022-03-16T17:30:00", start: "2022-03-16T17:30:00",
duration: 30, duration: 30,
timezone: "", timezone: "",
@ -74,10 +74,90 @@ const meetings = [
joinUrl: "", joinUrl: "",
topic: "Back-end Meeting", topic: "Back-end Meeting",
}, },
{
meetingId: "3",
liveParticipantIds: [],
registrantIds: ["", "1"],
start: "2022-03-10T07:27:27",
duration: 727,
timezone: "",
joinUrl: "",
topic: "WHEN YOU",
},
{
meetingId: "4",
liveParticipantIds: [],
registrantIds: ["", "2", "3"],
start: "2022-03-10T12:30:00",
duration: 120,
timezone: "",
joinUrl: "",
topic: "Bathroom Break",
},
{
meetingId: "5",
liveParticipantIds: [""],
registrantIds: [""],
start: "2022-03-24T23:11:11",
duration: 11,
timezone: "",
joinUrl: "",
topic: "Drink Coffee",
},
{
meetingId: "6",
liveParticipantIds: ["", "1"],
registrantIds: ["", "1", "2", "3", "4", "5"],
start: "2022-03-25T09:00:00",
duration: 360,
timezone: "",
joinUrl: "",
topic: "Get grilled by Jerry",
},
{
meetingId: "7",
liveParticipantIds: [],
registrantIds: ["", "5"],
start: "2022-03-25T15:00:00",
duration: 150,
timezone: "",
joinUrl: "",
topic: "Get grilled by Arthur",
},
{
meetingId: "8",
liveParticipantIds: ["2"],
registrantIds: ["", "5", "2", "3"],
start: "2022-03-25T17:45:00",
duration: 60,
timezone: "",
joinUrl: "",
topic: "Jerry comes back for round 2",
},
{
meetingId: "9",
liveParticipantIds: ["2", "5"],
registrantIds: ["", "4"],
start: "2022-03-25T18:15:30",
duration: 75,
timezone: "",
joinUrl: "",
topic: "Tag team!",
},
{
meetingId: "10",
liveParticipantIds: [],
registrantIds: ["", "1", "2", "3", "4", "5", "6"],
start: "2022-04-04T18:30:00",
duration: 90,
timezone: "",
joinUrl: "",
topic: "MVP Deadline",
},
]; ];
const team = { const team = {
user: "0", user: "",
manager: "1", manager: "1",
sameManager: ["2", "3", "4", "5", "6"], sameManager: ["2", "3", "4", "5", "6"],
directReports: [], directReports: [],

View File

@ -3,7 +3,8 @@ import UserLite from "./UserLite";
interface UserFull { interface UserFull {
userInfo: UserLite; userInfo: UserLite;
manager: UserLite; manager: UserLite;
directReports: UserLite[]; managerDirectReports: UserLite[];
userDirectReports: UserLite[];
} }
export default UserFull; export default UserFull;

View File

@ -1,7 +1,7 @@
interface UserStatus { interface UserStatus {
uuid: string; userId: string;
inMeeting: boolean; inMeeting: boolean;
meetingID: string | null; meetingId: string | null;
} }
export default UserStatus; export default UserStatus;

View File

@ -1,5 +1,5 @@
import axios from "axios"; import axios from "axios";
export default axios.create({ export default axios.create({
baseURL: "https://g17vmhsvwi.execute-api.us-west-2.amazonaws.com/Prod" baseURL: "https://g17vmhsvwi.execute-api.us-west-2.amazonaws.com/Prod"
}); });

View File

@ -1,12 +1,154 @@
import { Toolbar } from "@mui/material"; import { Calendar, momentLocalizer, Views } from "react-big-calendar";
import UserCalendar from "./UserCalendar"; import moment from "moment";
import "react-big-calendar/lib/css/react-big-calendar.css";
import {
Divider,
FormControl,
InputLabel,
MenuItem,
Select,
SelectChangeEvent,
Toolbar,
Typography,
} from "@mui/material";
import { selectMeetings } from "../../redux/slices/meetingsAndUserStatusSlice";
import { useAppDispatch, useAppSelector } from "../../redux/hooks";
import {
selectMe,
selectTeam,
selectUser,
selectUsers,
} from "../../redux/slices/usersSlice";
import { open } from "../../redux/slices/meetingDetailsOpenSlice";
import DetailedMeeting from "../../api-bodies/DetailedMeeting";
import { useState } from "react";
import UserLite from "../../api-bodies/UserLite";
// TODO: match the styles and themes
// A superset of DetailedMeeting; contains fields required for the calendar to work
interface CalendarEvent {
meetingId: string;
liveParticipantIds: string[];
registrantIds: string[];
startIso: string; // names overlap here; this is equivalent to "start" in DetailedMeeting
duration: number;
timezone: string;
joinUrl: string;
topic: string;
title: string;
start: Date;
end: Date;
}
enum CalendarTaskView {
ViewAll = "VIEWALL",
}
const CalendarPage: React.FC = () => { const CalendarPage: React.FC = () => {
const dispatch = useAppDispatch();
const currentUserUuid: string = useAppSelector(selectMe);
const currentUser: UserLite | undefined = useAppSelector((state) =>
selectUser(state, currentUserUuid)
);
const teamUuids: string[] = useAppSelector(selectTeam);
const team: UserLite[] = useAppSelector((state) =>
selectUsers(state, teamUuids)
);
const meetings: CalendarEvent[] = useAppSelector((state) =>
selectMeetings(state)
).map((m) => ({
meetingId: m.meetingId,
liveParticipantIds: m.liveParticipantIds,
registrantIds: m.registrantIds,
startIso: m.start,
duration: m.duration,
timezone: m.timezone,
joinUrl: m.joinUrl,
topic: m.topic,
// fields needed by calendar
title: m.topic,
start: new Date(Date.parse(m.start)), // Turns the ISO String into a date object
end: new Date(Date.parse(m.start) + m.duration * 60000), // result of Date.parse() is milliseconds, and m.duration is given in minutes
}));
const [selectedUserUuid, setSelectedUserUuid] = useState(currentUserUuid);
// turns calendar event back into DetailedMeeting then opens meeting view
const handleSelectEvent = (event: CalendarEvent) => {
const meeting: DetailedMeeting = {
meetingId: event.meetingId,
liveParticipantIds: event.liveParticipantIds,
registrantIds: event.registrantIds,
start: event.startIso,
duration: event.duration,
timezone: event.timezone,
joinUrl: event.joinUrl,
topic: event.topic,
};
dispatch(open(meeting));
};
// dropdown selection event
const handleUserChange = (event: SelectChangeEvent) => {
setSelectedUserUuid(event.target.value);
};
// filter the meetings based on the uuid
const getUserMeetings = (uuid: string): CalendarEvent[] => {
if (uuid == CalendarTaskView.ViewAll) {
// "View all team member meetings" option
return meetings;
} else {
return meetings.filter((meeting) => meeting.registrantIds.includes(uuid));
}
};
return ( return (
<> <>
<Toolbar /> <Toolbar />
<UserCalendar />
<FormControl variant="filled" fullWidth>
<InputLabel id="calendar-user-select-label">
<Typography color="black">User</Typography>
</InputLabel>
<Select
labelId="calendar-user-select-label"
id="calendar-user-select"
value={selectedUserUuid}
label="User"
onChange={handleUserChange}
>
<MenuItem value={CalendarTaskView.ViewAll}>
View all team member meetings
</MenuItem>
{currentUser ? (
<MenuItem value={currentUserUuid}>
{currentUser.name + " (Me)"}
</MenuItem>
) : null}
{team.map((user) => (
<MenuItem key={user.uuid} value={user.uuid}>
{user.name}
</MenuItem>
))}
</Select>
</FormControl>
<Divider sx={{ my: 1 }} />
<Calendar
events={getUserMeetings(selectedUserUuid)}
views={["month", "week", "day"]}
defaultView={Views.WEEK}
onSelectEvent={handleSelectEvent}
step={60}
showMultiDayTimes
localizer={momentLocalizer(moment)}
style={{ height: "83%" }}
/>
</> </>
); );
}; };

View File

@ -1,185 +0,0 @@
/*
Test data from
https://github.com/jquense/react-big-calendar/blob/master/examples/events.js
*/
const now = new Date();
export default [
{
id: 0,
title: "All Day Event very long title",
allDay: true,
start: new Date(2022, 2, 0),
end: new Date(2022, 2, 1),
},
{
id: 1,
title: "Long Event",
start: new Date(2022, 2, 7),
end: new Date(2022, 2, 10),
},
{
id: 2,
title: "DTS STARTS",
start: new Date(2022, 1, 13, 0, 0, 0),
end: new Date(2022, 1, 20, 0, 0, 0),
},
{
id: 3,
title: "DTS ENDS",
start: new Date(2022, 9, 6, 0, 0, 0),
end: new Date(2022, 9, 13, 0, 0, 0),
},
{
id: 4,
title: "Some Event",
start: new Date(2022, 2, 9, 0, 0, 0),
end: new Date(2022, 2, 10, 0, 0, 0),
},
{
id: 5,
title: "Conference",
start: new Date(2022, 2, 11),
end: new Date(2022, 2, 13),
desc: "Big conference for important people",
},
{
id: 6,
title: "Meeting",
start: new Date(2022, 2, 12, 10, 30, 0, 0),
end: new Date(2022, 2, 12, 12, 30, 0, 0),
desc: "Pre-meeting meeting, to prepare for the meeting",
},
{
id: 7,
title: "Lunch",
start: new Date(2022, 2, 12, 12, 0, 0, 0),
end: new Date(2022, 2, 12, 13, 0, 0, 0),
desc: "Power lunch",
},
{
id: 8,
title: "Meeting",
start: new Date(2022, 2, 12, 14, 0, 0, 0),
end: new Date(2022, 2, 12, 15, 0, 0, 0),
},
{
id: 9,
title: "Happy Hour",
start: new Date(2022, 2, 12, 17, 0, 0, 0),
end: new Date(2022, 2, 12, 17, 30, 0, 0),
desc: "Most important meal of the day",
},
{
id: 10,
title: "Dinner",
start: new Date(2022, 2, 12, 20, 0, 0, 0),
end: new Date(2022, 2, 12, 21, 0, 0, 0),
},
{
id: 11,
title: "Planning Meeting with Paige",
start: new Date(2022, 2, 13, 8, 0, 0),
end: new Date(2022, 2, 13, 10, 30, 0),
},
{
id: 11.1,
title: "Inconvenient multi-day Conference Call",
start: new Date(2022, 2, 13, 9, 30, 0),
end: new Date(2022, 2, 14, 1, 0, 0),
},
{
id: 11.2,
title: "Project Kickoff - Lou's Shoes",
start: new Date(2022, 2, 13, 11, 30, 0),
end: new Date(2022, 2, 13, 14, 0, 0),
},
{
id: 11.3,
title: "Quote Follow-up - Tea by Tina",
start: new Date(2022, 2, 13, 15, 30, 0),
end: new Date(2022, 2, 13, 16, 0, 0),
},
{
id: 12,
title: "Late Night Event",
start: new Date(2022, 2, 17, 19, 30, 0),
end: new Date(2022, 2, 18, 2, 0, 0),
},
{
id: 12.5,
title: "Late Same Night Event",
start: new Date(2022, 2, 17, 19, 30, 0),
end: new Date(2022, 2, 17, 23, 30, 0),
},
{
id: 13,
title: "Multi-day Event",
start: new Date(2022, 2, 20, 19, 30, 0),
end: new Date(2022, 2, 22, 2, 0, 0),
},
{
id: 14,
title: "Today",
start: new Date(new Date().setHours(new Date().getHours() - 3)),
end: new Date(new Date().setHours(new Date().getHours() + 3)),
},
{
id: 15,
title: "Point in Time Event",
start: now,
end: now,
},
{
id: 16,
title: "Video Record",
start: new Date(2022, 2, 14, 15, 30, 0),
end: new Date(2022, 2, 14, 19, 0, 0),
},
{
id: 17,
title: "Dutch Song Producing",
start: new Date(2022, 2, 14, 16, 30, 0),
end: new Date(2022, 2, 14, 20, 0, 0),
},
{
id: 18,
title: "Itaewon Halloween Meeting",
start: new Date(2022, 2, 14, 16, 30, 0),
end: new Date(2022, 2, 14, 17, 30, 0),
},
{
id: 19,
title: "Online Coding Test",
start: new Date(2022, 2, 14, 17, 30, 0),
end: new Date(2022, 2, 14, 20, 30, 0),
},
{
id: 20,
title: "An overlapped Event",
start: new Date(2022, 2, 14, 17, 0, 0),
end: new Date(2022, 2, 14, 18, 30, 0),
},
{
id: 21,
title: "Phone Interview",
start: new Date(2022, 2, 14, 17, 0, 0),
end: new Date(2022, 2, 14, 18, 30, 0),
},
{
id: 22,
title: "Cooking Class",
start: new Date(2022, 2, 14, 17, 30, 0),
end: new Date(2022, 2, 14, 19, 0, 0),
},
{
id: 23,
title: "Go to the gym",
start: new Date(2022, 2, 14, 18, 30, 0),
end: new Date(2022, 2, 14, 20, 0, 0),
},
];

View File

@ -1,23 +0,0 @@
import { Calendar, momentLocalizer } from "react-big-calendar";
import moment from "moment";
import testEvents from "./Events";
import "react-big-calendar/lib/css/react-big-calendar.css";
const localizer = momentLocalizer(moment);
const UserCalendar: React.FC = () => {
return (
<Calendar
events={testEvents}
views={["month", "week", "day"]}
step={60}
showMultiDayTimes
localizer={localizer}
style={{ height: "80%" }}
/>
);
};
export default UserCalendar;

View File

@ -27,13 +27,13 @@ const Sidebar: React.FC = () => {
const favorites = useAppSelector((state) => const favorites = useAppSelector((state) =>
selectUsers(state, favoritesUuids) selectUsers(state, favoritesUuids)
); );
let filteredFavorites = favorites.filter((favorite) => const filteredFavorites = favorites.filter((favorite) =>
favorite.name.toLowerCase().includes(inputText.toLowerCase()) favorite.name.toLowerCase().includes(inputText.toLowerCase())
); );
const teamUuids = useAppSelector(selectTeam); const teamUuids = useAppSelector(selectTeam);
const team = useAppSelector((state) => selectUsers(state, teamUuids)); const team = useAppSelector((state) => selectUsers(state, teamUuids));
let filteredTeam = team.filter((member) => const filteredTeam = team.filter((member) =>
member.name.toLowerCase().includes(inputText.toLowerCase()) member.name.toLowerCase().includes(inputText.toLowerCase())
); );

View File

@ -18,6 +18,7 @@ import {
} from "../../../../redux/slices/favoritesSlice"; } from "../../../../redux/slices/favoritesSlice";
import RemoveIcon from "@mui/icons-material/Remove"; import RemoveIcon from "@mui/icons-material/Remove";
import { MeetingStatus } from "../../../../utils"; import { MeetingStatus } from "../../../../utils";
import { selectMe } from "../../../../redux/slices/usersSlice";
interface Props { interface Props {
contactInfo: UserLite; contactInfo: UserLite;
@ -28,9 +29,10 @@ const UpperBody: React.FC<Props> = (props) => {
const userStatus: UserStatus = useAppSelector((state) => const userStatus: UserStatus = useAppSelector((state) =>
selectUserStatus(state, props.contactInfo.uuid) selectUserStatus(state, props.contactInfo.uuid)
); );
const me = useAppSelector(selectMe);
const favoritesUuids = useAppSelector(selectFavorites); const favoritesUuids = useAppSelector(selectFavorites);
const detailedMeeting = useAppSelector((state) => const detailedMeeting = useAppSelector((state) =>
selectMeeting(state, userStatus.meetingID) selectMeeting(state, userStatus.meetingId)
); );
const meetingStatus: MeetingStatus = const meetingStatus: MeetingStatus =
userStatus && userStatus.inMeeting userStatus && userStatus.inMeeting
@ -63,7 +65,11 @@ const UpperBody: React.FC<Props> = (props) => {
variant="outlined" variant="outlined"
color="success" color="success"
startIcon={<AddIcon />} startIcon={<AddIcon />}
onClick={() => dispatch(addFavorite(props.contactInfo.uuid))} onClick={() =>
dispatch(
addFavorite({ userId: me, toBeAdded: props.contactInfo.uuid })
)
}
> >
favorites favorites
</Button> </Button>
@ -72,7 +78,14 @@ const UpperBody: React.FC<Props> = (props) => {
variant="outlined" variant="outlined"
color="secondary" color="secondary"
startIcon={<RemoveIcon />} startIcon={<RemoveIcon />}
onClick={() => dispatch(removeFavorite(props.contactInfo.uuid))} onClick={() =>
dispatch(
removeFavorite({
userId: me,
toBeRemoved: props.contactInfo.uuid,
})
)
}
> >
favorites favorites
</Button> </Button>

View File

@ -33,6 +33,7 @@ const CallFavouritesDialog: React.FC<Props> = ({
const handleClose = () => { const handleClose = () => {
onClose(selectedValue); onClose(selectedValue);
}; };
console.log(users);
const [group, setGroup] = useState<string>("Favorites"); const [group, setGroup] = useState<string>("Favorites");
const [inputText, setInputText] = useState<string>(""); const [inputText, setInputText] = useState<string>("");

View File

@ -9,7 +9,6 @@ import { formatTimeFromDate } from "../../utils";
import UserLite from "../../api-bodies/UserLite"; import UserLite from "../../api-bodies/UserLite";
const MeetingsPanel: React.FC = () => { const MeetingsPanel: React.FC = () => {
const meetings = useAppSelector(selectMeetings); const meetings = useAppSelector(selectMeetings);
const uuids: string[] = []; const uuids: string[] = [];
meetings.forEach((meeting) => { meetings.forEach((meeting) => {
@ -24,10 +23,25 @@ const MeetingsPanel: React.FC = () => {
selectUsers(state,uuids) selectUsers(state,uuids)
); );
// const participants: (UserLite | undefined)[] = [];
// uuids.forEach((uuid) => {
// const userLite = useAppSelector((state) =>
// selectUser(state,uuid)
// );
// participants.push(userLite);
// });
const [currentDate, setCurrentDate] = useState<Date>(new Date());
useEffect(() => {
setInterval(() => setCurrentDate(new Date()), 1000);
}, []);
return ( return (
<div className="meetings-panel"> <div className="meetings-panel">
<div className="row panel-label"> <div className="row panel-label">
<Typography className="mylabel" sx={{ ml: 1 }}>Meetings in Progress</Typography> <Typography className="mylabel" sx={{ ml: 1 }}>
Meetings in Progress
</Typography>
</div> </div>
<List style={{maxHeight: '100%', overflow: 'auto'}} > <List style={{maxHeight: '100%', overflow: 'auto'}} >
@ -42,10 +56,6 @@ const MeetingsPanel: React.FC = () => {
const startDatemil = startDate.getTime(); const startDatemil = startDate.getTime();
const endDatemil = startDatemil + meeting.duration*60000; const endDatemil = startDatemil + meeting.duration*60000;
const endDate = new Date(endDatemil); const endDate = new Date(endDatemil);
const [currentDate, setCurrentDate] = useState<Date>(new Date());
useEffect(() => {
setInterval(() => setCurrentDate(new Date()), 1000);
}, []);
const currentDatemil = currentDate.getTime(); const currentDatemil = currentDate.getTime();

View File

@ -8,11 +8,7 @@ import hsbcLogo from "../../assets/logo-png.png";
import zoomLogo from "../../assets/zoom.png"; import zoomLogo from "../../assets/zoom.png";
import LoginIcon from "@mui/icons-material/Login"; import LoginIcon from "@mui/icons-material/Login";
import useAuth from "../../hooks/useAuth"; import useAuth from "../../hooks/useAuth";
import { store } from "../../redux/store"; import axios from "../../api/axios";
import { fetchFavorites } from "../../redux/slices/favoritesSlice";
import { fetchMeetings } from "../../redux/slices/meetingsAndUserStatusSlice";
import { fetchUsers } from "../../redux/slices/usersSlice";
import axios from "../../api/axios"
interface LocationState { interface LocationState {
from: { pathname: string }; from: { pathname: string };
@ -27,7 +23,6 @@ const Login: React.FC = () => {
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
const [logedInUser, setLogedInUser] = useState("");
// const [errMsg, setErrMsg] = useState(''); // const [errMsg, setErrMsg] = useState('');
// const userRef = useRef(); // const userRef = useRef();
@ -41,26 +36,21 @@ const Login: React.FC = () => {
// setErrMsg(''); // setErrMsg('');
// }, [user, pwd]) // }, [user, pwd])
store.dispatch(fetchMeetings(logedInUser));
store.dispatch(fetchUsers(logedInUser));
store.dispatch(fetchFavorites(logedInUser));
const handleLogin = async(e: React.SyntheticEvent) => { const handleLogin = async(e: React.SyntheticEvent) => {
e.preventDefault(); e.preventDefault();
try { try {
if (email === "" && password === "") { if (email === "" && password === "") {
setAuth["email"] = email; setAuth["uuid"] = "";
setAuth["isLoggedIn"] = true; setAuth["isLoggedIn"] = true;
navigate(from, { replace: true }); navigate(from, { replace: true });
} }
const response = await axios.post( const response = await axios.post(
'/login', "/login",
JSON.stringify({ email: email, password: password }) JSON.stringify({ email: email, password: password })
// { headers: { 'Content-Type': 'application/json' } } // { headers: { 'Content-Type': 'application/json' } }
) );
// const response = { data: { userid: "123456" }}; // delete later // const response = { data: { userid: "123456" }}; // delete later
const logedInUserId = response?.data?.userId; const logedInUserId = response?.data?.userId;
@ -70,15 +60,12 @@ const Login: React.FC = () => {
console.log(response?.data); console.log(response?.data);
if (logedInUserId != undefined) { if (logedInUserId != undefined) {
setAuth["email"] = email; setAuth["uuid"] = logedInUserId;
setAuth["isLoggedIn"] = true; setAuth["isLoggedIn"] = true;
setLogedInUser(logedInUserId);
navigate(from, { replace: true }); navigate(from, { replace: true });
} }
} catch (error) { } catch (error) {
console.log(error);
} }
setEmail(email); setEmail(email);
setPassword(""); setPassword("");

View File

@ -12,6 +12,7 @@ import {
import { open as openMeetingDetails } from "../../redux/slices/meetingDetailsOpenSlice"; import { open as openMeetingDetails } from "../../redux/slices/meetingDetailsOpenSlice";
import { selectMeeting } from "../../redux/slices/meetingsAndUserStatusSlice"; import { selectMeeting } from "../../redux/slices/meetingsAndUserStatusSlice";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { selectMe } from "../../redux/slices/usersSlice";
interface Props { interface Props {
user: UserLite; user: UserLite;
@ -21,9 +22,10 @@ interface Props {
const SettingsButton: React.FC<Props> = ({ user, status }: Props) => { const SettingsButton: React.FC<Props> = ({ user, status }: Props) => {
const navigate = useNavigate(); const navigate = useNavigate();
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null); const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const me = useAppSelector(selectMe);
const favoritesUuids = useAppSelector(selectFavorites); const favoritesUuids = useAppSelector(selectFavorites);
const meeting = useAppSelector((state) => const meeting = useAppSelector((state) =>
selectMeeting(state, status.meetingID) selectMeeting(state, status.meetingId)
); );
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const open = Boolean(anchorEl); const open = Boolean(anchorEl);
@ -56,7 +58,7 @@ const SettingsButton: React.FC<Props> = ({ user, status }: Props) => {
<MenuItem <MenuItem
onClick={() => { onClick={() => {
handleClose(); handleClose();
dispatch(removeFavorite(user.uuid)); dispatch(removeFavorite({ userId: me, toBeRemoved: user.uuid }));
}} }}
> >
Remove from Favorites Remove from Favorites
@ -65,7 +67,7 @@ const SettingsButton: React.FC<Props> = ({ user, status }: Props) => {
<MenuItem <MenuItem
onClick={() => { onClick={() => {
handleClose(); handleClose();
dispatch(addFavorite(user.uuid)); dispatch(addFavorite({ userId: me, toBeAdded: user.uuid }));
}} }}
> >
Add to Favorites Add to Favorites

View File

@ -1,17 +1,17 @@
import { createContext, useState } from "react"; import { createContext, useState } from "react";
interface loginInfo { interface loginInfo {
email: string; uuid: string;
isLoggedIn: boolean; isLoggedIn: boolean;
} }
const AuthContext = createContext<loginInfo>({ const AuthContext = createContext<loginInfo>({
email: "", uuid: "",
isLoggedIn: false, isLoggedIn: false,
}); });
export const AuthProvider = ({ children }: { children: React.ReactNode }) => { export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
const [auth] = useState<loginInfo>({ email: "", isLoggedIn: false }); const [auth] = useState<loginInfo>({ uuid: "", isLoggedIn: false });
return <AuthContext.Provider value={auth}>{children}</AuthContext.Provider>; return <AuthContext.Provider value={auth}>{children}</AuthContext.Provider>;
}; };

View File

@ -2,7 +2,7 @@ import { useContext } from "react";
import AuthContext from "../context/AuthProvider"; import AuthContext from "../context/AuthProvider";
interface loginInfo { interface loginInfo {
email: string; uuid: string;
isLoggedIn: boolean; isLoggedIn: boolean;
} }

View File

@ -0,0 +1,44 @@
import { Middleware } from "redux";
import { io, Socket } from "socket.io-client";
import DetailedMeeting from "../../api-bodies/DetailedMeeting";
import { socketActions } from "../slices/meetingsAndUserStatusSlice";
const socketMiddleware: Middleware = (store) => {
let socket: Socket;
return (next) => (action) => {
const isConnectionEstablished =
socket && store.getState().meetingsAndUserStatuses.isConnected;
if (
socketActions.startConnecting.match(action) &&
!isConnectionEstablished
) {
console.log("startConnecting called");
socket = io("wss://uo5wdcbn6l.execute-api.us-west-2.amazonaws.com/Prod/");
socket.on("Connect", () => {
console.log("connected!!!");
store.dispatch(socketActions.connectionEstablished());
});
socket.on("MeetingCreated", (meeting: DetailedMeeting) => {
store.dispatch(socketActions.meetingCreated(meeting));
});
socket.on(
"UserStatusChange",
(statusChange: {
userId: string;
inMeeting: boolean;
meetingId: string;
}) => {
store.dispatch(socketActions.userStatusChanged(statusChange));
}
);
}
next(action);
};
};
export default socketMiddleware;

View File

@ -1,5 +1,6 @@
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { RootState } from "../store"; import { RootState } from "../store";
import axios from "../../api/axios";
import { favorites } from "../../api-bodies/MockData"; import { favorites } from "../../api-bodies/MockData";
interface FavoritesState { interface FavoritesState {
@ -32,24 +33,37 @@ export const favoritesSlice = createSlice({
export const fetchFavorites = createAsyncThunk( export const fetchFavorites = createAsyncThunk(
"favorites/fetchFavorites", "favorites/fetchFavorites",
async (uuid: string) => { async (uuid: string) => {
// const response = await client.post("/fakeApi/posts", initialPost); if (uuid) {
// !!! const response = await axios.get(`/users/${uuid}/favourites`);
console.log(uuid); return response.data.userIds;
return favorites; } else {
return favorites;
}
} }
); );
export const addFavorite = createAsyncThunk( export const addFavorite = createAsyncThunk(
"favorites/addFavorite", "favorites/addFavorite",
async (uuid: string) => { async (ids: { userId: string; toBeAdded: string }) => {
// const response = await client.post("/fakeApi/posts", initialPost); if (ids.userId) {
return uuid; await axios.post(
`/users/${ids.userId}/favourites`,
JSON.stringify({ userId: ids.toBeAdded })
);
return ids.toBeAdded;
} else {
return ids.toBeAdded;
}
} }
); );
export const removeFavorite = createAsyncThunk( export const removeFavorite = createAsyncThunk(
"favorites/removeFavorite", "favorites/removeFavorite",
async (uuid: string) => { async (ids: { userId: string; toBeRemoved: string }) => {
// const response = await client.post("/fakeApi/posts", initialPost); if (ids.userId) {
return uuid; await axios.delete(`/users/${ids.userId}/favourites/${ids.toBeRemoved}`);
return ids.toBeRemoved;
} else {
return ids.toBeRemoved;
}
} }
); );
export const selectFavorites = (state: RootState) => state.favorites.favorites; export const selectFavorites = (state: RootState) => state.favorites.favorites;

View File

@ -1,23 +1,42 @@
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import DetailedMeeting from "../../api-bodies/DetailedMeeting"; import DetailedMeeting from "../../api-bodies/DetailedMeeting";
import { RootState } from "../store"; import { RootState } from "../store";
import { meetings } from "../../api-bodies/MockData";
import UserStatus from "../../api-bodies/UserStatus"; import UserStatus from "../../api-bodies/UserStatus";
import axios from "../../api/axios";
import { meetings } from "../../api-bodies/MockData";
interface MeetingsAndUserStatusState { interface MeetingsAndUserStatusState {
meetings: DetailedMeeting[]; meetings: DetailedMeeting[];
userStatuses: Record<string, UserStatus>; userStatuses: Record<string, UserStatus>;
isConnecting: boolean;
isConnected: boolean;
} }
const initialState: MeetingsAndUserStatusState = { const initialState: MeetingsAndUserStatusState = {
meetings: [], meetings: [],
userStatuses: {}, userStatuses: {},
isConnecting: false,
isConnected: false,
}; };
export const meetingsAndUserStatusSlice = createSlice({ export const meetingsAndUserStatusSlice = createSlice({
name: "meetingsAndUserStatus", name: "meetingsAndUserStatus",
initialState, initialState,
reducers: {}, reducers: {
startConnecting: (state) => {
state.isConnecting = true;
},
connectionEstablished: (state) => {
state.isConnecting = true;
state.isConnected = true;
},
meetingCreated: (state, action) => {
state.meetings.push(action.payload.meeting);
},
userStatusChanged: (state, action) => {
state.userStatuses[action.payload.uuid] = action.payload;
},
},
extraReducers(builder) { extraReducers(builder) {
builder.addCase(fetchMeetings.fulfilled, (state, action) => { builder.addCase(fetchMeetings.fulfilled, (state, action) => {
state.meetings = action.payload.meetings; state.meetings = action.payload.meetings;
@ -29,20 +48,35 @@ export const meetingsAndUserStatusSlice = createSlice({
export const fetchMeetings = createAsyncThunk( export const fetchMeetings = createAsyncThunk(
"meetingsAndUserStatus/fetchMeetings", "meetingsAndUserStatus/fetchMeetings",
async (uuid: string) => { async (uuid: string) => {
// const response = await client.post("/fakeApi/posts", initialPost); if (uuid) {
// !!! const response = await axios.get(
console.log(uuid); `/users/${uuid}/meetings?start=1900-01-01&end=2100-01-01`
const userStatuses: Record<string, UserStatus> = {}; );
meetings.forEach((meeting) => { const meetings: DetailedMeeting[] = response.data.meetings;
meeting.liveParticipantIds.forEach((uuid) => { const userStatuses: Record<string, UserStatus> = {};
userStatuses[uuid] = { meetings.forEach((meeting) => {
uuid: uuid, meeting.liveParticipantIds.forEach((uuid) => {
inMeeting: true, userStatuses[uuid] = {
meetingID: meeting.meetingId, userId: uuid,
}; inMeeting: true,
meetingId: meeting.meetingId,
};
});
}); });
}); return { userStatuses: userStatuses, meetings: meetings };
return { userStatuses: userStatuses, meetings: meetings }; } else {
const userStatuses: Record<string, UserStatus> = {};
meetings.forEach((meeting) => {
meeting.liveParticipantIds.forEach((uuid) => {
userStatuses[uuid] = {
userId: uuid,
inMeeting: true,
meetingId: meeting.meetingId,
};
});
});
return { userStatuses: userStatuses, meetings: meetings };
}
} }
); );
@ -69,9 +103,9 @@ export const selectUserStatus = (
return userStatus; return userStatus;
} else { } else {
return { return {
uuid: uuid, userId: uuid,
inMeeting: false, inMeeting: false,
meetingID: null, meetingId: null,
}; };
} }
}; };
@ -86,12 +120,13 @@ export const selectUserStatuses = (
userStatuses.push(userStatus); userStatuses.push(userStatus);
} else { } else {
userStatuses.push({ userStatuses.push({
uuid: uuid, userId: uuid,
inMeeting: false, inMeeting: false,
meetingID: null, meetingId: null,
}); });
} }
}); });
return userStatuses; return userStatuses;
}; };
export default meetingsAndUserStatusSlice.reducer; export default meetingsAndUserStatusSlice.reducer;
export const socketActions = meetingsAndUserStatusSlice.actions;

View File

@ -1,11 +1,13 @@
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { RootState } from "../store"; import { RootState } from "../store";
import { userLites, team } from "../../api-bodies/MockData";
import UserLite from "../../api-bodies/UserLite"; import UserLite from "../../api-bodies/UserLite";
import axios from "../../api/axios";
import UserFull from "../../api-bodies/UserFull";
import { userLites, team } from "../../api-bodies/MockData";
interface TeamState { interface TeamState {
user: string | null; user: string;
manager: string | null; manager: string;
sameManager: string[]; sameManager: string[];
directReports: string[]; directReports: string[];
} }
@ -17,7 +19,7 @@ interface UsersState {
const initialState: UsersState = { const initialState: UsersState = {
users: {}, users: {},
team: { user: null, manager: null, sameManager: [], directReports: [] }, team: { user: "", manager: "", sameManager: [], directReports: [] },
}; };
export const usersSlice = createSlice({ export const usersSlice = createSlice({
@ -35,35 +37,64 @@ export const usersSlice = createSlice({
export const fetchUsers = createAsyncThunk( export const fetchUsers = createAsyncThunk(
"users/fetchUsers", "users/fetchUsers",
async (uuid: string) => { async (uuid: string) => {
// fetch userfull if (uuid) {
// fetch managerfull const users: Record<string, UserLite> = {};
// now you have yourself, co-workers, manager, and direct reports (your team basically!) const team: TeamState = {
// call setTeam reducer in teamSlice user: "",
console.log(uuid); manager: "",
const users: Record<string, UserLite> = {}; sameManager: [],
userLites.forEach((userLite) => { directReports: [],
users[userLite.uuid] = userLite; };
}); // fetch userfull
return { users: users, team: team }; const userResp = await axios.get(`/users/${uuid}`);
const user: UserFull = userResp.data.user;
users[user.userInfo.uuid] = user.userInfo;
users[user.manager.uuid] = user.manager;
team.user = user.userInfo.uuid;
team.manager = user.manager.uuid;
user.userDirectReports.forEach((userLite) => {
users[userLite.uuid] = userLite;
team.directReports.push(userLite.uuid);
});
user.managerDirectReports.forEach((userLite) => {
users[userLite.uuid] = userLite;
team.sameManager.push(userLite.uuid);
});
console.log(users);
return { users: users, team: team };
} else {
const users: Record<string, UserLite> = {};
userLites.forEach((userLite) => {
users[userLite.uuid] = userLite;
});
return { users: users, team: team };
}
} }
); );
export const selectMe = (state: RootState) => state.users.team.user;
export const selectUser = ( export const selectUser = (
state: RootState, state: RootState,
uuid: string | undefined uuid: string | undefined
): UserLite | undefined => { ): UserLite | undefined => {
return uuid ? state.users.users[uuid] : undefined; return uuid !== undefined ? state.users.users[uuid] : undefined;
}; };
export const selectUsers = (state: RootState, uuids: string[]): UserLite[] => { export const selectUsers = (state: RootState, uuids: string[]): UserLite[] => {
const users: UserLite[] = []; const users: UserLite[] = [];
uuids.forEach((uuid) => users.push(state.users.users[uuid])); uuids.forEach((uuid) => {
if (state.users.users[uuid]) users.push(state.users.users[uuid]);
});
return users; return users;
}; };
export const selectTeam = (state: RootState) => { export const selectTeam = (state: RootState) => {
const team: string[] = []; const team: string[] = [];
if (state.users.team.manager !== null) team.push(state.users.team.manager); if (state.users.team.manager) team.push(state.users.team.manager);
state.users.team.sameManager.forEach((u) => team.push(u)); state.users.team.sameManager.forEach((u) => team.push(u));
state.users.team.directReports.forEach((u) => team.push(u)); state.users.team.directReports.forEach((u) => team.push(u));
return team; return team;
}; };
export default usersSlice.reducer; export default usersSlice.reducer;

View File

@ -3,6 +3,7 @@ import meetingDetailsOpenReducer from "./slices/meetingDetailsOpenSlice";
import favoritesReducer from "./slices/favoritesSlice"; import favoritesReducer from "./slices/favoritesSlice";
import meetingsAndUserStatusReducer from "./slices/meetingsAndUserStatusSlice"; import meetingsAndUserStatusReducer from "./slices/meetingsAndUserStatusSlice";
import usersReducer from "./slices/usersSlice"; import usersReducer from "./slices/usersSlice";
import socketMiddleware from "./middleware/socketMiddleware";
export const store = configureStore({ export const store = configureStore({
reducer: { reducer: {
@ -11,6 +12,9 @@ export const store = configureStore({
meetingsAndUserStatuses: meetingsAndUserStatusReducer, meetingsAndUserStatuses: meetingsAndUserStatusReducer,
users: usersReducer, users: usersReducer,
}, },
middleware: (getDefaultMiddleware) => {
return getDefaultMiddleware().concat([socketMiddleware]);
},
}); });
export type RootState = ReturnType<typeof store.getState>; export type RootState = ReturnType<typeof store.getState>;

View File

@ -17,7 +17,8 @@ const getStatusColor = (ms: MeetingStatus): string => {
const formatTimeFromDate = (date: Date): string => { const formatTimeFromDate = (date: Date): string => {
let hour = date.getHours(); let hour = date.getHours();
let ampm = ""; let ampm = "";
let minutes = date.getMinutes() < 10 ? "0" + date.getMinutes() : "" + date.getMinutes(); const minutes =
date.getMinutes() < 10 ? "0" + date.getMinutes() : "" + date.getMinutes();
if (hour < 12) { if (hour < 12) {
ampm = "am"; ampm = "am";
} else { } else {