From 5ef38dad1adc20887f6f891edc0a1af2589e294e Mon Sep 17 00:00:00 2001 From: mbalsdon Date: Fri, 25 Mar 2022 00:07:02 -0700 Subject: [PATCH 01/18] add more mock meeting data --- src/api-bodies/MockData.tsx | 70 +++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/api-bodies/MockData.tsx b/src/api-bodies/MockData.tsx index e85a51e..2a77d54 100644 --- a/src/api-bodies/MockData.tsx +++ b/src/api-bodies/MockData.tsx @@ -74,6 +74,76 @@ const meetings = [ joinUrl: "", topic: "Back-end Meeting", }, + { + meetingId: "3", + liveParticipantIds: [], + registrantIds: ["0", "1"], + start: "2022-03-10T07:27:27", + duration: 727, + timezone: "", + joinUrl: "", + topic: "WHEN YOU", + }, + { + meetingId: "4", + liveParticipantIds: [], + registrantIds: ["0", "2", "3"], + start: "2022-03-10T12:30:00", + duration: 120, + timezone: "", + joinUrl: "", + topic: "Bathroom Break", + }, + { + meetingId: "5", + liveParticipantIds: ["0"], + registrantIds: ["0"], + start: "2022-03-24T23:11:11", + duration: 11, + timezone: "", + joinUrl: "", + topic: "Drink Coffee", + }, + { + meetingId: "6", + liveParticipantIds: ["0", "1"], + registrantIds: ["0", "1", "2", "3", "4", "5"], + start: "2022-03-25T09:00:00", + duration: 360, + timezone: "", + joinUrl: "", + topic: "Get grilled by Jerry", + }, + { + meetingId: "7", + liveParticipantIds: [], + registrantIds: ["0", "5"], + start: "2022-03-25T15:00:00", + duration: 150, + timezone: "", + joinUrl: "", + topic: "Get grilled by Arthur", + }, + { + meetingId: "8", + liveParticipantIds: ["2"], + registrantIds: ["0", "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: ["0", "4"], + start: "2022-03-25T18:15:30", + duration: 75, + timezone: "", + joinUrl: "", + topic: "Tag team!", + }, ]; const team = { From 9ce1314cae00dcc76af057e1c671e1dcfd4c46ca Mon Sep 17 00:00:00 2001 From: mbalsdon Date: Fri, 25 Mar 2022 00:07:29 -0700 Subject: [PATCH 02/18] remove old mock data --- src/components/calendar/Events.tsx | 185 ----------------------------- 1 file changed, 185 deletions(-) delete mode 100644 src/components/calendar/Events.tsx diff --git a/src/components/calendar/Events.tsx b/src/components/calendar/Events.tsx deleted file mode 100644 index 6b62d47..0000000 --- a/src/components/calendar/Events.tsx +++ /dev/null @@ -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), - }, -]; \ No newline at end of file From ccb5719e4b5786ac3adfe448c5d049be1d9ff65c Mon Sep 17 00:00:00 2001 From: mbalsdon Date: Fri, 25 Mar 2022 00:08:25 -0700 Subject: [PATCH 03/18] add selector for uuid of user currently using the app --- src/redux/slices/usersSlice.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/redux/slices/usersSlice.tsx b/src/redux/slices/usersSlice.tsx index a1b68ac..6df583d 100644 --- a/src/redux/slices/usersSlice.tsx +++ b/src/redux/slices/usersSlice.tsx @@ -66,4 +66,6 @@ export const selectTeam = (state: RootState) => { state.users.team.directReports.forEach((u) => team.push(u)); return team; }; -export default usersSlice.reducer; +export const selectMe = (state: RootState) => state.users.team.user; + +export default usersSlice.reducer; \ No newline at end of file From 3d0bd5c4d999481ab6539071c8f1c11ffe65d7be Mon Sep 17 00:00:00 2001 From: mbalsdon Date: Fri, 25 Mar 2022 00:12:12 -0700 Subject: [PATCH 04/18] allow selectUserUpcomingMeetings to take null uuid inputs due to TeamState in usersSlice.tsx supporting null user inputs --- src/redux/slices/meetingsAndUserStatusSlice.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/redux/slices/meetingsAndUserStatusSlice.tsx b/src/redux/slices/meetingsAndUserStatusSlice.tsx index 1298b62..93cf3b6 100644 --- a/src/redux/slices/meetingsAndUserStatusSlice.tsx +++ b/src/redux/slices/meetingsAndUserStatusSlice.tsx @@ -55,8 +55,8 @@ export const selectMeeting = (state: RootState, meetingID: string | null) => { ) : null; }; -export const selectUserUpcomingMeetings = (state: RootState, uuid: string) => { - return state.meetingsAndUserStatuses.meetings.filter((meeting) => +export const selectUserUpcomingMeetings = (state: RootState, uuid: string | null) => { + return uuid == null ? [] : state.meetingsAndUserStatuses.meetings.filter((meeting) => meeting.registrantIds.includes(uuid) ); }; From dcff2d70ef293428187662c4b2a99196c7da352b Mon Sep 17 00:00:00 2001 From: mbalsdon Date: Fri, 25 Mar 2022 00:14:53 -0700 Subject: [PATCH 05/18] integrate backend meeting structure with calendar, add ability to bring up meeting menu when clicking calendar events --- src/components/calendar/UserCalendar.tsx | 88 +++++++++++++++++++++--- 1 file changed, 77 insertions(+), 11 deletions(-) diff --git a/src/components/calendar/UserCalendar.tsx b/src/components/calendar/UserCalendar.tsx index d6a7797..26497c7 100644 --- a/src/components/calendar/UserCalendar.tsx +++ b/src/components/calendar/UserCalendar.tsx @@ -1,22 +1,88 @@ -import { Calendar, momentLocalizer } from "react-big-calendar"; +import { Calendar, momentLocalizer, Views } from "react-big-calendar"; import moment from "moment"; -import testEvents from "./Events"; import "react-big-calendar/lib/css/react-big-calendar.css"; +import { Divider } from "@mui/material"; +import { selectUserUpcomingMeetings } from "../../redux/slices/meetingsAndUserStatusSlice"; +import { useAppDispatch, useAppSelector } from "../../redux/hooks"; +import { selectMe } from "../../redux/slices/usersSlice"; +import { open } from "../../redux/slices/meetingDetailsOpenSlice"; +import DetailedMeeting from "../../api-bodies/DetailedMeeting"; -const localizer = momentLocalizer(moment); +// clicking meetings opens view + +// per user calendar +// full view (all teammates) +// match the styles and themes +// make an interface for events + +// 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; +} const UserCalendar: React.FC = () => { + const dispatch = useAppDispatch(); + const currentUserUuid = useAppSelector(selectMe); // TODO: per-user + const meetings = useAppSelector((state) => + selectUserUpcomingMeetings(state, currentUserUuid) + ).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 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)); + }; + return ( - + <> + + + + ); }; From 7b9ed5e313b8703a763a588ecfaf1a0b92d4cc44 Mon Sep 17 00:00:00 2001 From: mbalsdon Date: Fri, 25 Mar 2022 09:55:20 -0700 Subject: [PATCH 06/18] move everything into one file --- src/components/calendar/CalendarPage.tsx | 81 +++++++++++++++++++-- src/components/calendar/UserCalendar.tsx | 89 ------------------------ 2 files changed, 77 insertions(+), 93 deletions(-) delete mode 100644 src/components/calendar/UserCalendar.tsx diff --git a/src/components/calendar/CalendarPage.tsx b/src/components/calendar/CalendarPage.tsx index 340e0fe..f3723d2 100644 --- a/src/components/calendar/CalendarPage.tsx +++ b/src/components/calendar/CalendarPage.tsx @@ -1,14 +1,87 @@ -import { Toolbar } from "@mui/material"; -import UserCalendar from "./UserCalendar"; +import { Calendar, momentLocalizer, Views } from "react-big-calendar"; +import moment from "moment"; + +import "react-big-calendar/lib/css/react-big-calendar.css"; +import { Divider, Toolbar } from "@mui/material"; +import { selectUserUpcomingMeetings } from "../../redux/slices/meetingsAndUserStatusSlice"; +import { useAppDispatch, useAppSelector } from "../../redux/hooks"; +import { selectMe } from "../../redux/slices/usersSlice"; +import { open } from "../../redux/slices/meetingDetailsOpenSlice"; +import DetailedMeeting from "../../api-bodies/DetailedMeeting"; + +// TODO: per user calendar +// TODO: full view (all teammates) +// 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; +} const CalendarPage: React.FC = () => { + const dispatch = useAppDispatch(); + const currentUserUuid = useAppSelector(selectMe); // TODO: per-user + const meetings = useAppSelector((state) => + selectUserUpcomingMeetings(state, currentUserUuid) + ).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 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)); + }; + return ( <> - + + + ); }; -export default CalendarPage; +export default CalendarPage; \ No newline at end of file diff --git a/src/components/calendar/UserCalendar.tsx b/src/components/calendar/UserCalendar.tsx deleted file mode 100644 index 26497c7..0000000 --- a/src/components/calendar/UserCalendar.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { Calendar, momentLocalizer, Views } from "react-big-calendar"; -import moment from "moment"; - -import "react-big-calendar/lib/css/react-big-calendar.css"; -import { Divider } from "@mui/material"; -import { selectUserUpcomingMeetings } from "../../redux/slices/meetingsAndUserStatusSlice"; -import { useAppDispatch, useAppSelector } from "../../redux/hooks"; -import { selectMe } from "../../redux/slices/usersSlice"; -import { open } from "../../redux/slices/meetingDetailsOpenSlice"; -import DetailedMeeting from "../../api-bodies/DetailedMeeting"; - -// clicking meetings opens view - -// per user calendar -// full view (all teammates) -// match the styles and themes -// make an interface for events - -// 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; -} - -const UserCalendar: React.FC = () => { - - const dispatch = useAppDispatch(); - const currentUserUuid = useAppSelector(selectMe); // TODO: per-user - const meetings = useAppSelector((state) => - selectUserUpcomingMeetings(state, currentUserUuid) - ).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 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)); - }; - - return ( - <> - - - - - ); -}; - -export default UserCalendar; \ No newline at end of file From 8dc4fef0c435ad60a7ca16542f3476745874d4e5 Mon Sep 17 00:00:00 2001 From: Taehee Choi Date: Fri, 25 Mar 2022 10:59:05 -0700 Subject: [PATCH 07/18] fetch calls and websocket --- package-lock.json | 225 +++++++++++++++++- package.json | 3 +- src/api-bodies/UserFull.tsx | 3 +- src/api-bodies/UserStatus.tsx | 4 +- .../body-components/UpperBody.tsx | 19 +- src/components/home/Home.tsx | 4 +- src/components/login/Login.tsx | 25 +- src/components/sidebar/SettingsButton.tsx | 8 +- src/redux/middleware/socketMiddleware.tsx | 41 ++++ src/redux/slices/favoritesSlice.tsx | 23 +- .../slices/meetingsAndUserStatusSlice.tsx | 46 +++- src/redux/slices/usersSlice.tsx | 43 +++- src/redux/store.tsx | 4 + 13 files changed, 387 insertions(+), 61 deletions(-) create mode 100644 src/redux/middleware/socketMiddleware.tsx diff --git a/package-lock.json b/package-lock.json index df79fc0..9e49979 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,8 @@ "react-big-calendar": "^0.39.3", "react-dom": "^17.0.2", "react-redux": "^7.2.6", - "react-router-dom": "^6.2.1" + "react-router-dom": "^6.2.1", + "socket.io-client": "^4.4.1" }, "devDependencies": { "@svgr/webpack": "^5.5.0", @@ -2255,6 +2256,19 @@ "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": { "version": "5.4.0", "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" } }, + "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": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -4634,6 +4653,7 @@ "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", + "fsevents": "~2.3.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", @@ -5897,6 +5917,53 @@ "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": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", @@ -7430,6 +7497,11 @@ "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": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -7787,6 +7859,12 @@ "dev": true, "dependencies": { "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", "object-assign": "^4.1.1", "schema-utils": "^2.7.1" @@ -10017,6 +10095,16 @@ "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": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -11426,6 +11514,34 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "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": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -12971,6 +13087,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": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -13006,6 +13130,11 @@ "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": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -14491,6 +14620,16 @@ "dev": 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": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", @@ -15535,6 +15674,11 @@ "@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": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -17335,6 +17479,38 @@ "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": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", @@ -18505,6 +18681,11 @@ "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", "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": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -20404,6 +20585,16 @@ "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": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -21502,6 +21693,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": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -22643,6 +22856,11 @@ "dev": true, "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": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -22672,6 +22890,11 @@ "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": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index c1b049d..99ff8cc 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,8 @@ "react-big-calendar": "^0.39.3", "react-dom": "^17.0.2", "react-redux": "^7.2.6", - "react-router-dom": "^6.2.1" + "react-router-dom": "^6.2.1", + "socket.io-client": "^4.4.1" }, "scripts": { "start-local": "webpack serve --config webpack.config.ts", diff --git a/src/api-bodies/UserFull.tsx b/src/api-bodies/UserFull.tsx index c67dbc7..edd0d7b 100644 --- a/src/api-bodies/UserFull.tsx +++ b/src/api-bodies/UserFull.tsx @@ -3,7 +3,8 @@ import UserLite from "./UserLite"; interface UserFull { userInfo: UserLite; manager: UserLite; - directReports: UserLite[]; + managerDirectReports: UserLite[]; + userDirectReports: UserLite[]; } export default UserFull; diff --git a/src/api-bodies/UserStatus.tsx b/src/api-bodies/UserStatus.tsx index ddb4d18..d385aa5 100644 --- a/src/api-bodies/UserStatus.tsx +++ b/src/api-bodies/UserStatus.tsx @@ -1,7 +1,7 @@ interface UserStatus { - uuid: string; + userId: string; inMeeting: boolean; - meetingID: string | null; + meetingId: string | null; } export default UserStatus; diff --git a/src/components/contacts/contacts-components/body-components/UpperBody.tsx b/src/components/contacts/contacts-components/body-components/UpperBody.tsx index 64121a5..10b4560 100644 --- a/src/components/contacts/contacts-components/body-components/UpperBody.tsx +++ b/src/components/contacts/contacts-components/body-components/UpperBody.tsx @@ -18,6 +18,7 @@ import { } from "../../../../redux/slices/favoritesSlice"; import RemoveIcon from "@mui/icons-material/Remove"; import { MeetingStatus } from "../../../../utils"; +import { selectMe } from "../../../../redux/slices/usersSlice"; interface Props { contactInfo: UserLite; @@ -28,9 +29,10 @@ const UpperBody: React.FC = (props) => { const userStatus: UserStatus = useAppSelector((state) => selectUserStatus(state, props.contactInfo.uuid) ); + const me = useAppSelector(selectMe); const favoritesUuids = useAppSelector(selectFavorites); const detailedMeeting = useAppSelector((state) => - selectMeeting(state, userStatus.meetingID) + selectMeeting(state, userStatus.meetingId) ); const meetingStatus: MeetingStatus = userStatus && userStatus.inMeeting @@ -63,7 +65,11 @@ const UpperBody: React.FC = (props) => { variant="outlined" color="success" startIcon={} - onClick={() => dispatch(addFavorite(props.contactInfo.uuid))} + onClick={() => + dispatch( + addFavorite({ userId: me, toBeAdded: props.contactInfo.uuid }) + ) + } > favorites @@ -72,7 +78,14 @@ const UpperBody: React.FC = (props) => { variant="outlined" color="secondary" startIcon={} - onClick={() => dispatch(removeFavorite(props.contactInfo.uuid))} + onClick={() => + dispatch( + removeFavorite({ + userId: me, + toBeRemoved: props.contactInfo.uuid, + }) + ) + } > favorites diff --git a/src/components/home/Home.tsx b/src/components/home/Home.tsx index 7fa09d6..879d2a4 100644 --- a/src/components/home/Home.tsx +++ b/src/components/home/Home.tsx @@ -25,9 +25,9 @@ const Home: React.FC = () => { return ( - + {/* - + */} diff --git a/src/components/login/Login.tsx b/src/components/login/Login.tsx index 397c4a9..a61b4f8 100644 --- a/src/components/login/Login.tsx +++ b/src/components/login/Login.tsx @@ -12,7 +12,8 @@ import { store } from "../../redux/store"; import { fetchFavorites } from "../../redux/slices/favoritesSlice"; import { fetchMeetings } from "../../redux/slices/meetingsAndUserStatusSlice"; import { fetchUsers } from "../../redux/slices/usersSlice"; -import axios from "../../api/axios" +import axios from "../../api/axios"; +import { socketActions } from "../../redux/slices/meetingsAndUserStatusSlice"; interface LocationState { from: { pathname: string }; @@ -40,14 +41,14 @@ const Login: React.FC = () => { // setErrMsg(''); // }, [user, pwd]) - const handleLogin = async(e: React.SyntheticEvent) => { + const handleLogin = async (e: React.SyntheticEvent) => { e.preventDefault(); try { if (email === "" && password === "") { setAuth["email"] = email; setAuth["isLoggedIn"] = true; - + store.dispatch(fetchMeetings("")); store.dispatch(fetchUsers("")); store.dispatch(fetchFavorites("")); @@ -55,12 +56,12 @@ const Login: React.FC = () => { } const response = await axios.post( - '/login', + "/login", JSON.stringify({ email: email, password: password }) // { headers: { 'Content-Type': 'application/json' } } - ) + ); // const response = { data: { userid: "123456" }}; // delete later - + const logedInUserId = response?.data?.userId; console.log(email); @@ -68,18 +69,16 @@ const Login: React.FC = () => { console.log(response?.data); if (logedInUserId != undefined) { - setAuth["email"] = email; - setAuth["isLoggedIn"] = true; - + setAuth.email = email; + setAuth.isLoggedIn = true; + + store.dispatch(socketActions.startConnecting()); store.dispatch(fetchMeetings(logedInUserId)); store.dispatch(fetchUsers(logedInUserId)); store.dispatch(fetchFavorites(logedInUserId)); navigate(from, { replace: true }); } - - } catch (error) { - - } + } catch (error) {} setEmail(email); setPassword(""); }; diff --git a/src/components/sidebar/SettingsButton.tsx b/src/components/sidebar/SettingsButton.tsx index 8d970ac..2621a30 100644 --- a/src/components/sidebar/SettingsButton.tsx +++ b/src/components/sidebar/SettingsButton.tsx @@ -12,6 +12,7 @@ import { import { open as openMeetingDetails } from "../../redux/slices/meetingDetailsOpenSlice"; import { selectMeeting } from "../../redux/slices/meetingsAndUserStatusSlice"; import { useNavigate } from "react-router-dom"; +import { selectMe } from "../../redux/slices/usersSlice"; interface Props { user: UserLite; @@ -21,9 +22,10 @@ interface Props { const SettingsButton: React.FC = ({ user, status }: Props) => { const navigate = useNavigate(); const [anchorEl, setAnchorEl] = useState(null); + const me = useAppSelector(selectMe); const favoritesUuids = useAppSelector(selectFavorites); const meeting = useAppSelector((state) => - selectMeeting(state, status.meetingID) + selectMeeting(state, status.meetingId) ); const dispatch = useAppDispatch(); const open = Boolean(anchorEl); @@ -56,7 +58,7 @@ const SettingsButton: React.FC = ({ user, status }: Props) => { { handleClose(); - dispatch(removeFavorite(user.uuid)); + dispatch(removeFavorite({ userId: me, toBeRemoved: user.uuid })); }} > Remove from Favorites @@ -65,7 +67,7 @@ const SettingsButton: React.FC = ({ user, status }: Props) => { { handleClose(); - dispatch(addFavorite(user.uuid)); + dispatch(addFavorite({ userId: me, toBeAdded: user.uuid })); }} > Add to Favorites diff --git a/src/redux/middleware/socketMiddleware.tsx b/src/redux/middleware/socketMiddleware.tsx new file mode 100644 index 0000000..becdf30 --- /dev/null +++ b/src/redux/middleware/socketMiddleware.tsx @@ -0,0 +1,41 @@ +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)) { + 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; diff --git a/src/redux/slices/favoritesSlice.tsx b/src/redux/slices/favoritesSlice.tsx index 9d16211..0fbbbe3 100644 --- a/src/redux/slices/favoritesSlice.tsx +++ b/src/redux/slices/favoritesSlice.tsx @@ -1,6 +1,6 @@ import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; import { RootState } from "../store"; -import { favorites } from "../../api-bodies/MockData"; +import axios from "../../api/axios"; interface FavoritesState { favorites: string[]; @@ -32,24 +32,25 @@ export const favoritesSlice = createSlice({ export const fetchFavorites = createAsyncThunk( "favorites/fetchFavorites", async (uuid: string) => { - // const response = await client.post("/fakeApi/posts", initialPost); - // !!! - console.log(uuid); - return favorites; + const response = await axios.get(`/users/${uuid}/favourites`); + return response.data.userIds; } ); export const addFavorite = createAsyncThunk( "favorites/addFavorite", - async (uuid: string) => { - // const response = await client.post("/fakeApi/posts", initialPost); - return uuid; + async (ids: { userId: string; toBeAdded: string }) => { + await axios.post( + `/users/${ids.userId}/favourites`, + JSON.stringify({ userId: ids.toBeAdded }) + ); + return ids.toBeAdded; } ); export const removeFavorite = createAsyncThunk( "favorites/removeFavorite", - async (uuid: string) => { - // const response = await client.post("/fakeApi/posts", initialPost); - return uuid; + async (ids: { userId: string; toBeRemoved: string }) => { + await axios.delete(`/users/${ids.userId}/favourites/${ids.toBeRemoved}`); + return ids.toBeRemoved; } ); export const selectFavorites = (state: RootState) => state.favorites.favorites; diff --git a/src/redux/slices/meetingsAndUserStatusSlice.tsx b/src/redux/slices/meetingsAndUserStatusSlice.tsx index 1298b62..b449d48 100644 --- a/src/redux/slices/meetingsAndUserStatusSlice.tsx +++ b/src/redux/slices/meetingsAndUserStatusSlice.tsx @@ -1,23 +1,41 @@ import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; import DetailedMeeting from "../../api-bodies/DetailedMeeting"; import { RootState } from "../store"; -import { meetings } from "../../api-bodies/MockData"; import UserStatus from "../../api-bodies/UserStatus"; +import axios from "../../api/axios"; interface MeetingsAndUserStatusState { meetings: DetailedMeeting[]; userStatuses: Record; + isConnecting: boolean; + isConnected: boolean; } const initialState: MeetingsAndUserStatusState = { meetings: [], userStatuses: {}, + isConnecting: false, + isConnected: false, }; export const meetingsAndUserStatusSlice = createSlice({ name: "meetingsAndUserStatus", 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) { builder.addCase(fetchMeetings.fulfilled, (state, action) => { state.meetings = action.payload.meetings; @@ -29,16 +47,17 @@ export const meetingsAndUserStatusSlice = createSlice({ export const fetchMeetings = createAsyncThunk( "meetingsAndUserStatus/fetchMeetings", async (uuid: string) => { - // const response = await client.post("/fakeApi/posts", initialPost); - // !!! - console.log(uuid); + const response = await axios.get( + `/users/${uuid}/meetings?start=1900-01-01&end=2100-01-01` + ); + const meetings: DetailedMeeting[] = response.data.meetings; const userStatuses: Record = {}; meetings.forEach((meeting) => { meeting.liveParticipantIds.forEach((uuid) => { userStatuses[uuid] = { - uuid: uuid, + userId: uuid, inMeeting: true, - meetingID: meeting.meetingId, + meetingId: meeting.meetingId, }; }); }); @@ -51,8 +70,8 @@ export const selectMeetings = (state: RootState) => export const selectMeeting = (state: RootState, meetingID: string | null) => { return meetingID !== null ? state.meetingsAndUserStatuses.meetings.find( - (meeting) => meeting.meetingId === meetingID - ) + (meeting) => meeting.meetingId === meetingID + ) : null; }; export const selectUserUpcomingMeetings = (state: RootState, uuid: string) => { @@ -69,9 +88,9 @@ export const selectUserStatus = ( return userStatus; } else { return { - uuid: uuid, + userId: uuid, inMeeting: false, - meetingID: null, + meetingId: null, }; } }; @@ -86,12 +105,13 @@ export const selectUserStatuses = ( userStatuses.push(userStatus); } else { userStatuses.push({ - uuid: uuid, + userId: uuid, inMeeting: false, - meetingID: null, + meetingId: null, }); } }); return userStatuses; }; export default meetingsAndUserStatusSlice.reducer; +export const socketActions = meetingsAndUserStatusSlice.actions; diff --git a/src/redux/slices/usersSlice.tsx b/src/redux/slices/usersSlice.tsx index a1b68ac..c082184 100644 --- a/src/redux/slices/usersSlice.tsx +++ b/src/redux/slices/usersSlice.tsx @@ -1,11 +1,12 @@ import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; import { RootState } from "../store"; -import { userLites, team } from "../../api-bodies/MockData"; import UserLite from "../../api-bodies/UserLite"; +import axios from "../../api/axios"; +import UserFull from "../../api-bodies/UserFull"; interface TeamState { - user: string | null; - manager: string | null; + user: string; + manager: string; sameManager: string[]; directReports: string[]; } @@ -17,7 +18,7 @@ interface UsersState { const initialState: UsersState = { users: {}, - team: { user: null, manager: null, sameManager: [], directReports: [] }, + team: { user: "", manager: "", sameManager: [], directReports: [] }, }; export const usersSlice = createSlice({ @@ -35,19 +36,37 @@ export const usersSlice = createSlice({ export const fetchUsers = createAsyncThunk( "users/fetchUsers", async (uuid: string) => { - // fetch userfull - // fetch managerfull - // now you have yourself, co-workers, manager, and direct reports (your team basically!) - // call setTeam reducer in teamSlice - console.log(uuid); const users: Record = {}; - userLites.forEach((userLite) => { + const team: TeamState = { + user: "", + manager: "", + sameManager: [], + directReports: [], + }; + // fetch userfull + 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 }; } ); +export const selectMe = (state: RootState) => state.users.team.user; export const selectUser = ( state: RootState, uuid: string | undefined @@ -56,7 +75,9 @@ export const selectUser = ( }; export const selectUsers = (state: RootState, uuids: string[]): 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; }; export const selectTeam = (state: RootState) => { diff --git a/src/redux/store.tsx b/src/redux/store.tsx index 0d829d6..287a169 100644 --- a/src/redux/store.tsx +++ b/src/redux/store.tsx @@ -3,6 +3,7 @@ import meetingDetailsOpenReducer from "./slices/meetingDetailsOpenSlice"; import favoritesReducer from "./slices/favoritesSlice"; import meetingsAndUserStatusReducer from "./slices/meetingsAndUserStatusSlice"; import usersReducer from "./slices/usersSlice"; +import socketMiddleware from "./middleware/socketMiddleware"; export const store = configureStore({ reducer: { @@ -11,6 +12,9 @@ export const store = configureStore({ meetingsAndUserStatuses: meetingsAndUserStatusReducer, users: usersReducer, }, + middleware: (getDefaultMiddleware) => { + return getDefaultMiddleware().concat([socketMiddleware]); + }, }); export type RootState = ReturnType; From 04320c0ee539b6b6619eebb20c468a0c4e5889a4 Mon Sep 17 00:00:00 2001 From: Taehee Choi Date: Fri, 25 Mar 2022 11:08:49 -0700 Subject: [PATCH 08/18] linter fixes --- src/api/axios.tsx | 2 +- src/components/contacts/contacts-components/Sidebar.tsx | 4 ++-- src/components/home/CallFavouritesDialog.tsx | 1 + src/components/home/Home.tsx | 4 ++-- src/components/login/Login.tsx | 4 +++- src/redux/middleware/socketMiddleware.tsx | 5 ++++- src/redux/slices/meetingsAndUserStatusSlice.tsx | 4 ++-- src/utils.tsx | 3 ++- 8 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/api/axios.tsx b/src/api/axios.tsx index 0c91aaf..eda4bef 100644 --- a/src/api/axios.tsx +++ b/src/api/axios.tsx @@ -1,5 +1,5 @@ import axios from "axios"; 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" }); \ No newline at end of file diff --git a/src/components/contacts/contacts-components/Sidebar.tsx b/src/components/contacts/contacts-components/Sidebar.tsx index 47cf766..bf63195 100644 --- a/src/components/contacts/contacts-components/Sidebar.tsx +++ b/src/components/contacts/contacts-components/Sidebar.tsx @@ -27,13 +27,13 @@ const Sidebar: React.FC = () => { const favorites = useAppSelector((state) => selectUsers(state, favoritesUuids) ); - let filteredFavorites = favorites.filter((favorite) => + const filteredFavorites = favorites.filter((favorite) => favorite.name.toLowerCase().includes(inputText.toLowerCase()) ); const teamUuids = useAppSelector(selectTeam); const team = useAppSelector((state) => selectUsers(state, teamUuids)); - let filteredTeam = team.filter((member) => + const filteredTeam = team.filter((member) => member.name.toLowerCase().includes(inputText.toLowerCase()) ); diff --git a/src/components/home/CallFavouritesDialog.tsx b/src/components/home/CallFavouritesDialog.tsx index 63ee88a..58d12c1 100644 --- a/src/components/home/CallFavouritesDialog.tsx +++ b/src/components/home/CallFavouritesDialog.tsx @@ -33,6 +33,7 @@ const CallFavouritesDialog: React.FC = ({ const handleClose = () => { onClose(selectedValue); }; + console.log(users); const [group, setGroup] = useState("Favorites"); const [inputText, setInputText] = useState(""); diff --git a/src/components/home/Home.tsx b/src/components/home/Home.tsx index 879d2a4..7fa09d6 100644 --- a/src/components/home/Home.tsx +++ b/src/components/home/Home.tsx @@ -25,9 +25,9 @@ const Home: React.FC = () => { return ( - {/* + - */} + diff --git a/src/components/login/Login.tsx b/src/components/login/Login.tsx index a61b4f8..71eae7e 100644 --- a/src/components/login/Login.tsx +++ b/src/components/login/Login.tsx @@ -78,7 +78,9 @@ const Login: React.FC = () => { store.dispatch(fetchFavorites(logedInUserId)); navigate(from, { replace: true }); } - } catch (error) {} + } catch (error) { + console.log(error); + } setEmail(email); setPassword(""); }; diff --git a/src/redux/middleware/socketMiddleware.tsx b/src/redux/middleware/socketMiddleware.tsx index becdf30..4f47d2f 100644 --- a/src/redux/middleware/socketMiddleware.tsx +++ b/src/redux/middleware/socketMiddleware.tsx @@ -10,7 +10,10 @@ const socketMiddleware: Middleware = (store) => { const isConnectionEstablished = socket && store.getState().meetingsAndUserStatuses.isConnected; - if (socketActions.startConnecting.match(action)) { + if ( + socketActions.startConnecting.match(action) && + !isConnectionEstablished + ) { console.log("startConnecting called"); socket = io("wss://uo5wdcbn6l.execute-api.us-west-2.amazonaws.com/Prod/"); diff --git a/src/redux/slices/meetingsAndUserStatusSlice.tsx b/src/redux/slices/meetingsAndUserStatusSlice.tsx index b449d48..f43fe31 100644 --- a/src/redux/slices/meetingsAndUserStatusSlice.tsx +++ b/src/redux/slices/meetingsAndUserStatusSlice.tsx @@ -70,8 +70,8 @@ export const selectMeetings = (state: RootState) => export const selectMeeting = (state: RootState, meetingID: string | null) => { return meetingID !== null ? state.meetingsAndUserStatuses.meetings.find( - (meeting) => meeting.meetingId === meetingID - ) + (meeting) => meeting.meetingId === meetingID + ) : null; }; export const selectUserUpcomingMeetings = (state: RootState, uuid: string) => { diff --git a/src/utils.tsx b/src/utils.tsx index 6dbb3d5..3e2ad36 100644 --- a/src/utils.tsx +++ b/src/utils.tsx @@ -17,7 +17,8 @@ const getStatusColor = (ms: MeetingStatus): string => { const formatTimeFromDate = (date: Date): string => { let hour = date.getHours(); 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) { ampm = "am"; } else { From 62d120afb7edaf0c3983d621775dd5a82a4cc874 Mon Sep 17 00:00:00 2001 From: mbalsdon Date: Fri, 25 Mar 2022 11:19:40 -0700 Subject: [PATCH 09/18] add another mock meeting --- src/api-bodies/MockData.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/api-bodies/MockData.tsx b/src/api-bodies/MockData.tsx index 2a77d54..8bed897 100644 --- a/src/api-bodies/MockData.tsx +++ b/src/api-bodies/MockData.tsx @@ -144,6 +144,16 @@ const meetings = [ joinUrl: "", topic: "Tag team!", }, + { + meetingId: "10", + liveParticipantIds: [], + registrantIds: ["0", "1", "2", "3", "4", "5", "6"], + start: "2022-04-04T18:30:00", + duration: 90, + timezone: "", + joinUrl: "", + topic: "MVP Deadline", + } ]; const team = { From f282d0d96c335ca3606778702c5e3b0ee100de88 Mon Sep 17 00:00:00 2001 From: mbalsdon Date: Fri, 25 Mar 2022 11:21:19 -0700 Subject: [PATCH 10/18] add dropdown that allows viewing meetings for other team members, or all team members at once --- src/components/calendar/CalendarPage.tsx | 72 ++++++++++++++++++++---- 1 file changed, 62 insertions(+), 10 deletions(-) diff --git a/src/components/calendar/CalendarPage.tsx b/src/components/calendar/CalendarPage.tsx index f3723d2..e85a627 100644 --- a/src/components/calendar/CalendarPage.tsx +++ b/src/components/calendar/CalendarPage.tsx @@ -2,15 +2,15 @@ import { Calendar, momentLocalizer, Views } from "react-big-calendar"; import moment from "moment"; import "react-big-calendar/lib/css/react-big-calendar.css"; -import { Divider, Toolbar } from "@mui/material"; -import { selectUserUpcomingMeetings } from "../../redux/slices/meetingsAndUserStatusSlice"; +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 } from "../../redux/slices/usersSlice"; +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: per user calendar -// TODO: full view (all teammates) // TODO: match the styles and themes // A superset of DetailedMeeting; contains fields required for the calendar to work @@ -28,12 +28,25 @@ interface CalendarEvent { end: Date; } -const CalendarPage: React.FC = () => { +enum CalendarTaskView { + ViewAll = "VIEWALL" +} +const CalendarPage: React.FC = () => { const dispatch = useAppDispatch(); - const currentUserUuid = useAppSelector(selectMe); // TODO: per-user - const meetings = useAppSelector((state) => - selectUserUpcomingMeetings(state, currentUserUuid) + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const currentUserUuid: string = useAppSelector(selectMe)!; + const currentUser: UserLite = useAppSelector((state) => + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + 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, @@ -51,6 +64,9 @@ const CalendarPage: React.FC = () => { }) ); + 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, @@ -65,12 +81,48 @@ const CalendarPage: React.FC = () => { 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 ( <> + + + + User + + + + + Date: Fri, 25 Mar 2022 11:26:51 -0700 Subject: [PATCH 11/18] fix calendar height (was getting cut off) --- src/components/calendar/CalendarPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/calendar/CalendarPage.tsx b/src/components/calendar/CalendarPage.tsx index e85a627..a2a600c 100644 --- a/src/components/calendar/CalendarPage.tsx +++ b/src/components/calendar/CalendarPage.tsx @@ -129,7 +129,7 @@ const CalendarPage: React.FC = () => { step={60} showMultiDayTimes localizer={momentLocalizer(moment)} - style={{ height: "90%" }} + style={{ height: "83%" }} /> From 7f00d001986e64de4dfdfaeb4d4b05db881f6d5c Mon Sep 17 00:00:00 2001 From: mbalsdon Date: Fri, 25 Mar 2022 11:32:27 -0700 Subject: [PATCH 12/18] revert changes --- src/redux/slices/meetingsAndUserStatusSlice.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/redux/slices/meetingsAndUserStatusSlice.tsx b/src/redux/slices/meetingsAndUserStatusSlice.tsx index 93cf3b6..1298b62 100644 --- a/src/redux/slices/meetingsAndUserStatusSlice.tsx +++ b/src/redux/slices/meetingsAndUserStatusSlice.tsx @@ -55,8 +55,8 @@ export const selectMeeting = (state: RootState, meetingID: string | null) => { ) : null; }; -export const selectUserUpcomingMeetings = (state: RootState, uuid: string | null) => { - return uuid == null ? [] : state.meetingsAndUserStatuses.meetings.filter((meeting) => +export const selectUserUpcomingMeetings = (state: RootState, uuid: string) => { + return state.meetingsAndUserStatuses.meetings.filter((meeting) => meeting.registrantIds.includes(uuid) ); }; From 364c920f7783b93024282daf2d9dbe1b6eb861e1 Mon Sep 17 00:00:00 2001 From: Taehee Choi Date: Fri, 25 Mar 2022 11:51:33 -0700 Subject: [PATCH 13/18] currentUser now cannot be null --- src/components/calendar/CalendarPage.tsx | 89 ++++++++++++++---------- src/redux/slices/usersSlice.tsx | 10 +-- src/utils.tsx | 3 +- 3 files changed, 60 insertions(+), 42 deletions(-) diff --git a/src/components/calendar/CalendarPage.tsx b/src/components/calendar/CalendarPage.tsx index a2a600c..4e78494 100644 --- a/src/components/calendar/CalendarPage.tsx +++ b/src/components/calendar/CalendarPage.tsx @@ -1,11 +1,25 @@ -import { Calendar, momentLocalizer, Views } from "react-big-calendar"; +import { Calendar, momentLocalizer, Views } from "react-big-calendar"; 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 { + 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 { + selectMe, + selectTeam, + selectUser, + selectUsers, +} from "../../redux/slices/usersSlice"; import { open } from "../../redux/slices/meetingDetailsOpenSlice"; import DetailedMeeting from "../../api-bodies/DetailedMeeting"; import { useState } from "react"; @@ -29,40 +43,36 @@ interface CalendarEvent { } enum CalendarTaskView { - ViewAll = "VIEWALL" + ViewAll = "VIEWALL", } const CalendarPage: React.FC = () => { const dispatch = useAppDispatch(); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const currentUserUuid: string = useAppSelector(selectMe)!; - const currentUser: UserLite = useAppSelector((state) => - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - selectUser(state, currentUserUuid)! + 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) => + 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 - }) - ); + ).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); @@ -88,12 +98,12 @@ const CalendarPage: React.FC = () => { // filter the meetings based on the uuid const getUserMeetings = (uuid: string): CalendarEvent[] => { - if (uuid == CalendarTaskView.ViewAll) { // "View all team member meetings" option + if (uuid == CalendarTaskView.ViewAll) { + // "View all team member meetings" option return meetings; } else { - return meetings.filter(meeting => meeting.registrantIds.includes(uuid)); + return meetings.filter((meeting) => meeting.registrantIds.includes(uuid)); } - }; return ( @@ -111,16 +121,24 @@ const CalendarPage: React.FC = () => { label="User" onChange={handleUserChange} > - View all team member meetings - {currentUser.name + " (Me)"} - {team.map(user => ( - {user.name} + + View all team member meetings + + {currentUser ? ( + + {currentUser.name + " (Me)"} + + ) : null} + {team.map((user) => ( + + {user.name} + ))} - + { style={{ height: "83%" }} /> - ); }; -export default CalendarPage; \ No newline at end of file +export default CalendarPage; diff --git a/src/redux/slices/usersSlice.tsx b/src/redux/slices/usersSlice.tsx index 6df583d..c8ffd1d 100644 --- a/src/redux/slices/usersSlice.tsx +++ b/src/redux/slices/usersSlice.tsx @@ -4,8 +4,8 @@ import { userLites, team } from "../../api-bodies/MockData"; import UserLite from "../../api-bodies/UserLite"; interface TeamState { - user: string | null; - manager: string | null; + user: string; + manager: string; sameManager: string[]; directReports: string[]; } @@ -17,7 +17,7 @@ interface UsersState { const initialState: UsersState = { users: {}, - team: { user: null, manager: null, sameManager: [], directReports: [] }, + team: { user: "", manager: "", sameManager: [], directReports: [] }, }; export const usersSlice = createSlice({ @@ -61,11 +61,11 @@ export const selectUsers = (state: RootState, uuids: string[]): UserLite[] => { }; export const selectTeam = (state: RootState) => { 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.directReports.forEach((u) => team.push(u)); return team; }; export const selectMe = (state: RootState) => state.users.team.user; -export default usersSlice.reducer; \ No newline at end of file +export default usersSlice.reducer; diff --git a/src/utils.tsx b/src/utils.tsx index 6dbb3d5..3e2ad36 100644 --- a/src/utils.tsx +++ b/src/utils.tsx @@ -17,7 +17,8 @@ const getStatusColor = (ms: MeetingStatus): string => { const formatTimeFromDate = (date: Date): string => { let hour = date.getHours(); 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) { ampm = "am"; } else { From ff8cd26a532cb41474a4ffd9836f5f0d24e630f2 Mon Sep 17 00:00:00 2001 From: Taehee Choi Date: Fri, 25 Mar 2022 13:15:25 -0700 Subject: [PATCH 14/18] empty string for mock data --- src/api-bodies/MockData.tsx | 2 +- src/components/home/MeetingsPanel.tsx | 25 +++++--- src/components/login/Login.tsx | 2 - src/redux/slices/favoritesSlice.tsx | 31 +++++++--- .../slices/meetingsAndUserStatusSlice.tsx | 43 +++++++++----- src/redux/slices/usersSlice.tsx | 57 +++++++++++-------- 6 files changed, 101 insertions(+), 59 deletions(-) diff --git a/src/api-bodies/MockData.tsx b/src/api-bodies/MockData.tsx index e85a51e..8351c1b 100644 --- a/src/api-bodies/MockData.tsx +++ b/src/api-bodies/MockData.tsx @@ -77,7 +77,7 @@ const meetings = [ ]; const team = { - user: "0", + user: "", manager: "1", sameManager: ["2", "3", "4", "5", "6"], directReports: [], diff --git a/src/components/home/MeetingsPanel.tsx b/src/components/home/MeetingsPanel.tsx index 96c7dca..5125a5e 100644 --- a/src/components/home/MeetingsPanel.tsx +++ b/src/components/home/MeetingsPanel.tsx @@ -7,7 +7,6 @@ import { selectUsers } from "../../redux/slices/usersSlice"; import { formatTimeFromDate } from "../../utils"; const MeetingsPanel: React.FC = () => { - const meetings = useAppSelector(selectMeetings); return ( @@ -19,7 +18,9 @@ const MeetingsPanel: React.FC = () => { Sunday, 03 Feb 2022 */}
- Meetings in Progress + + Meetings in Progress +
{meetings.map((meeting) => { @@ -28,7 +29,7 @@ const MeetingsPanel: React.FC = () => { ); const startDate = new Date(meeting.start); const startDatemil = startDate.getTime(); - const endDatemil = startDatemil + meeting.duration*60000; + const endDatemil = startDatemil + meeting.duration * 60000; const endDate = new Date(endDatemil); const [currentDate, setCurrentDate] = useState(new Date()); useEffect(() => { @@ -39,10 +40,16 @@ const MeetingsPanel: React.FC = () => { if (currentDatemil >= startDatemil && currentDatemil <= endDatemil) { return ( - (" " + userLite.name + " ")).toString()} + " " + userLite.name + " ") + .toString()} /> ); } @@ -50,5 +57,5 @@ const MeetingsPanel: React.FC = () => { ); }; - -export default MeetingsPanel; \ No newline at end of file + +export default MeetingsPanel; diff --git a/src/components/login/Login.tsx b/src/components/login/Login.tsx index 71eae7e..faf90b3 100644 --- a/src/components/login/Login.tsx +++ b/src/components/login/Login.tsx @@ -13,7 +13,6 @@ import { fetchFavorites } from "../../redux/slices/favoritesSlice"; import { fetchMeetings } from "../../redux/slices/meetingsAndUserStatusSlice"; import { fetchUsers } from "../../redux/slices/usersSlice"; import axios from "../../api/axios"; -import { socketActions } from "../../redux/slices/meetingsAndUserStatusSlice"; interface LocationState { from: { pathname: string }; @@ -72,7 +71,6 @@ const Login: React.FC = () => { setAuth.email = email; setAuth.isLoggedIn = true; - store.dispatch(socketActions.startConnecting()); store.dispatch(fetchMeetings(logedInUserId)); store.dispatch(fetchUsers(logedInUserId)); store.dispatch(fetchFavorites(logedInUserId)); diff --git a/src/redux/slices/favoritesSlice.tsx b/src/redux/slices/favoritesSlice.tsx index 0fbbbe3..4f9d151 100644 --- a/src/redux/slices/favoritesSlice.tsx +++ b/src/redux/slices/favoritesSlice.tsx @@ -1,6 +1,7 @@ import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; import { RootState } from "../store"; import axios from "../../api/axios"; +import { favorites } from "../../api-bodies/MockData"; interface FavoritesState { favorites: string[]; @@ -32,25 +33,37 @@ export const favoritesSlice = createSlice({ export const fetchFavorites = createAsyncThunk( "favorites/fetchFavorites", async (uuid: string) => { - const response = await axios.get(`/users/${uuid}/favourites`); - return response.data.userIds; + if (uuid) { + const response = await axios.get(`/users/${uuid}/favourites`); + return response.data.userIds; + } else { + return favorites; + } } ); export const addFavorite = createAsyncThunk( "favorites/addFavorite", async (ids: { userId: string; toBeAdded: string }) => { - await axios.post( - `/users/${ids.userId}/favourites`, - JSON.stringify({ userId: ids.toBeAdded }) - ); - return ids.toBeAdded; + if (ids.userId) { + await axios.post( + `/users/${ids.userId}/favourites`, + JSON.stringify({ userId: ids.toBeAdded }) + ); + return ids.toBeAdded; + } else { + return ids.toBeAdded; + } } ); export const removeFavorite = createAsyncThunk( "favorites/removeFavorite", async (ids: { userId: string; toBeRemoved: string }) => { - await axios.delete(`/users/${ids.userId}/favourites/${ids.toBeRemoved}`); - return ids.toBeRemoved; + if (ids.userId) { + await axios.delete(`/users/${ids.userId}/favourites/${ids.toBeRemoved}`); + return ids.toBeRemoved; + } else { + return ids.toBeRemoved; + } } ); export const selectFavorites = (state: RootState) => state.favorites.favorites; diff --git a/src/redux/slices/meetingsAndUserStatusSlice.tsx b/src/redux/slices/meetingsAndUserStatusSlice.tsx index f43fe31..1b7a411 100644 --- a/src/redux/slices/meetingsAndUserStatusSlice.tsx +++ b/src/redux/slices/meetingsAndUserStatusSlice.tsx @@ -3,6 +3,7 @@ import DetailedMeeting from "../../api-bodies/DetailedMeeting"; import { RootState } from "../store"; import UserStatus from "../../api-bodies/UserStatus"; import axios from "../../api/axios"; +import { meetings } from "../../api-bodies/MockData"; interface MeetingsAndUserStatusState { meetings: DetailedMeeting[]; @@ -47,21 +48,35 @@ export const meetingsAndUserStatusSlice = createSlice({ export const fetchMeetings = createAsyncThunk( "meetingsAndUserStatus/fetchMeetings", async (uuid: string) => { - const response = await axios.get( - `/users/${uuid}/meetings?start=1900-01-01&end=2100-01-01` - ); - const meetings: DetailedMeeting[] = response.data.meetings; - const userStatuses: Record = {}; - meetings.forEach((meeting) => { - meeting.liveParticipantIds.forEach((uuid) => { - userStatuses[uuid] = { - userId: uuid, - inMeeting: true, - meetingId: meeting.meetingId, - }; + if (uuid) { + const response = await axios.get( + `/users/${uuid}/meetings?start=1900-01-01&end=2100-01-01` + ); + const meetings: DetailedMeeting[] = response.data.meetings; + const userStatuses: Record = {}; + meetings.forEach((meeting) => { + meeting.liveParticipantIds.forEach((uuid) => { + userStatuses[uuid] = { + userId: uuid, + inMeeting: true, + meetingId: meeting.meetingId, + }; + }); }); - }); - return { userStatuses: userStatuses, meetings: meetings }; + return { userStatuses: userStatuses, meetings: meetings }; + } else { + const userStatuses: Record = {}; + meetings.forEach((meeting) => { + meeting.liveParticipantIds.forEach((uuid) => { + userStatuses[uuid] = { + userId: uuid, + inMeeting: true, + meetingId: meeting.meetingId, + }; + }); + }); + return { userStatuses: userStatuses, meetings: meetings }; + } } ); diff --git a/src/redux/slices/usersSlice.tsx b/src/redux/slices/usersSlice.tsx index c082184..b3325da 100644 --- a/src/redux/slices/usersSlice.tsx +++ b/src/redux/slices/usersSlice.tsx @@ -3,6 +3,7 @@ import { RootState } from "../store"; 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 { user: string; @@ -36,33 +37,41 @@ export const usersSlice = createSlice({ export const fetchUsers = createAsyncThunk( "users/fetchUsers", async (uuid: string) => { - const users: Record = {}; - const team: TeamState = { - user: "", - manager: "", - sameManager: [], - directReports: [], - }; - // fetch userfull - const userResp = await axios.get(`/users/${uuid}`); - const user: UserFull = userResp.data.user; + if (uuid) { + const users: Record = {}; + const team: TeamState = { + user: "", + manager: "", + sameManager: [], + directReports: [], + }; + // fetch userfull + 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; + users[user.userInfo.uuid] = user.userInfo; + users[user.manager.uuid] = user.manager; - team.user = user.userInfo.uuid; - team.manager = user.manager.uuid; + 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 }; + 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 = {}; + userLites.forEach((userLite) => { + users[userLite.uuid] = userLite; + }); + return { users: users, team: team }; + } } ); From 468d61b130c46e57feded18ea9b054f581c6115b Mon Sep 17 00:00:00 2001 From: Taehee Choi Date: Fri, 25 Mar 2022 13:32:11 -0700 Subject: [PATCH 15/18] some fixes --- src/api-bodies/MockData.tsx | 30 +++++++++++++++--------------- src/redux/slices/usersSlice.tsx | 3 +-- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/api-bodies/MockData.tsx b/src/api-bodies/MockData.tsx index d9132d0..6c114b3 100644 --- a/src/api-bodies/MockData.tsx +++ b/src/api-bodies/MockData.tsx @@ -1,6 +1,6 @@ const userLites = [ { - uuid: "0", + uuid: "", emailAddress: "cth0604@gmail.com", name: "Taehee Choi", role: "Front-end Dev", @@ -45,9 +45,9 @@ const userLites = [ const meetings = [ { - meetingId: "0", + meetingId: "", liveParticipantIds: [], - registrantIds: ["0", "1", "2", "3", "4", "5", "6"], + registrantIds: ["", "1", "2", "3", "4", "5", "6"], start: "2022-03-13T17:00:00", duration: 15, timezone: "", @@ -57,7 +57,7 @@ const meetings = [ { meetingId: "1", liveParticipantIds: [], - registrantIds: ["0", "2", "4"], + registrantIds: ["", "2", "4"], start: "2022-03-16T17:30:00", duration: 30, timezone: "", @@ -77,7 +77,7 @@ const meetings = [ { meetingId: "3", liveParticipantIds: [], - registrantIds: ["0", "1"], + registrantIds: ["", "1"], start: "2022-03-10T07:27:27", duration: 727, timezone: "", @@ -87,7 +87,7 @@ const meetings = [ { meetingId: "4", liveParticipantIds: [], - registrantIds: ["0", "2", "3"], + registrantIds: ["", "2", "3"], start: "2022-03-10T12:30:00", duration: 120, timezone: "", @@ -96,8 +96,8 @@ const meetings = [ }, { meetingId: "5", - liveParticipantIds: ["0"], - registrantIds: ["0"], + liveParticipantIds: [""], + registrantIds: [""], start: "2022-03-24T23:11:11", duration: 11, timezone: "", @@ -106,8 +106,8 @@ const meetings = [ }, { meetingId: "6", - liveParticipantIds: ["0", "1"], - registrantIds: ["0", "1", "2", "3", "4", "5"], + liveParticipantIds: ["", "1"], + registrantIds: ["", "1", "2", "3", "4", "5"], start: "2022-03-25T09:00:00", duration: 360, timezone: "", @@ -117,7 +117,7 @@ const meetings = [ { meetingId: "7", liveParticipantIds: [], - registrantIds: ["0", "5"], + registrantIds: ["", "5"], start: "2022-03-25T15:00:00", duration: 150, timezone: "", @@ -127,7 +127,7 @@ const meetings = [ { meetingId: "8", liveParticipantIds: ["2"], - registrantIds: ["0", "5", "2", "3"], + registrantIds: ["", "5", "2", "3"], start: "2022-03-25T17:45:00", duration: 60, timezone: "", @@ -137,7 +137,7 @@ const meetings = [ { meetingId: "9", liveParticipantIds: ["2", "5"], - registrantIds: ["0", "4"], + registrantIds: ["", "4"], start: "2022-03-25T18:15:30", duration: 75, timezone: "", @@ -147,13 +147,13 @@ const meetings = [ { meetingId: "10", liveParticipantIds: [], - registrantIds: ["0", "1", "2", "3", "4", "5", "6"], + registrantIds: ["", "1", "2", "3", "4", "5", "6"], start: "2022-04-04T18:30:00", duration: 90, timezone: "", joinUrl: "", topic: "MVP Deadline", - } + }, ]; const team = { diff --git a/src/redux/slices/usersSlice.tsx b/src/redux/slices/usersSlice.tsx index c343ee1..8539c5f 100644 --- a/src/redux/slices/usersSlice.tsx +++ b/src/redux/slices/usersSlice.tsx @@ -80,7 +80,7 @@ export const selectUser = ( state: RootState, uuid: string | 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[] => { const users: UserLite[] = []; @@ -96,6 +96,5 @@ export const selectTeam = (state: RootState) => { state.users.team.directReports.forEach((u) => team.push(u)); return team; }; -export const selectMe = (state: RootState) => state.users.team.user; export default usersSlice.reducer; From d46f70895c2232ba4d7be0fed8d67d238103576e Mon Sep 17 00:00:00 2001 From: CodeServer Date: Fri, 25 Mar 2022 21:13:08 +0000 Subject: [PATCH 16/18] temp fix --- package-lock.json | 7 ------- src/components/login/Login.tsx | 8 ++++---- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9e49979..50b7abe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4653,7 +4653,6 @@ "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.3.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", @@ -7859,12 +7858,6 @@ "dev": true, "dependencies": { "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", "object-assign": "^4.1.1", "schema-utils": "^2.7.1" diff --git a/src/components/login/Login.tsx b/src/components/login/Login.tsx index 96fab47..b9282a3 100644 --- a/src/components/login/Login.tsx +++ b/src/components/login/Login.tsx @@ -41,9 +41,9 @@ const Login: React.FC = () => { // setErrMsg(''); // }, [user, pwd]) - store.dispatch(fetchMeetings(logedInUser)); - store.dispatch(fetchUsers(logedInUser)); - store.dispatch(fetchFavorites(logedInUser)); + store.dispatch(fetchMeetings("")); // temp fix + store.dispatch(fetchUsers("")); // temp fix + store.dispatch(fetchFavorites("")); // temp fix const handleLogin = async(e: React.SyntheticEvent) => { e.preventDefault(); @@ -71,7 +71,7 @@ const Login: React.FC = () => { if (logedInUserId != undefined) { setAuth["email"] = email; setAuth["isLoggedIn"] = true; - setLogedInUser(logedInUserId); + // setLogedInUser(logedInUserId); navigate(from, { replace: true }); } } catch (error) { From 9300a4ca70a71392f2bae001fbab69c1521533e8 Mon Sep 17 00:00:00 2001 From: CodeServer Date: Fri, 25 Mar 2022 21:24:43 +0000 Subject: [PATCH 17/18] temp fix with uuid 1 --- src/components/login/Login.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/login/Login.tsx b/src/components/login/Login.tsx index b9282a3..e286d13 100644 --- a/src/components/login/Login.tsx +++ b/src/components/login/Login.tsx @@ -41,9 +41,9 @@ const Login: React.FC = () => { // setErrMsg(''); // }, [user, pwd]) - store.dispatch(fetchMeetings("")); // temp fix - store.dispatch(fetchUsers("")); // temp fix - store.dispatch(fetchFavorites("")); // temp fix + store.dispatch(fetchMeetings("1")); // temp fix + store.dispatch(fetchUsers("1")); // temp fix + store.dispatch(fetchFavorites("1")); // temp fix const handleLogin = async(e: React.SyntheticEvent) => { e.preventDefault(); From 664a34302bfb678fa7ad4d57153d619711707ec6 Mon Sep 17 00:00:00 2001 From: CodeServer Date: Fri, 25 Mar 2022 22:28:53 +0000 Subject: [PATCH 18/18] fixed the meetings panel bug --- src/ProtectedRoute.tsx | 9 +++++++++ src/components/home/MeetingsPanel.tsx | 8 ++++---- src/components/login/Login.tsx | 14 ++------------ src/context/AuthProvider.tsx | 6 +++--- src/hooks/useAuth.tsx | 2 +- 5 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/ProtectedRoute.tsx b/src/ProtectedRoute.tsx index 55e2d26..5d8d12f 100644 --- a/src/ProtectedRoute.tsx +++ b/src/ProtectedRoute.tsx @@ -3,12 +3,21 @@ import useAuth from "./hooks/useAuth"; import Navbar from "./components/navbar/Navbar"; import Sidebar from "./components/sidebar/Sidebar"; 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 auth = useAuth(); const location = useLocation(); /* 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 ? ( <> diff --git a/src/components/home/MeetingsPanel.tsx b/src/components/home/MeetingsPanel.tsx index 0daadb7..bc3ad0e 100644 --- a/src/components/home/MeetingsPanel.tsx +++ b/src/components/home/MeetingsPanel.tsx @@ -29,7 +29,11 @@ const MeetingsPanel: React.FC = () => { // ); // participants.push(userLite); // }); + const [currentDate, setCurrentDate] = useState(new Date()); + useEffect(() => { + setInterval(() => setCurrentDate(new Date()), 1000); + }, []); return (
@@ -59,10 +63,6 @@ const MeetingsPanel: React.FC = () => { const startDatemil = startDate.getTime(); const endDatemil = startDatemil + meeting.duration * 60000; const endDate = new Date(endDatemil); - const [currentDate, setCurrentDate] = useState(new Date()); - useEffect(() => { - setInterval(() => setCurrentDate(new Date()), 1000); - }, []); const currentDatemil = currentDate.getTime(); diff --git a/src/components/login/Login.tsx b/src/components/login/Login.tsx index e286d13..36bfd79 100644 --- a/src/components/login/Login.tsx +++ b/src/components/login/Login.tsx @@ -8,10 +8,6 @@ import hsbcLogo from "../../assets/logo-png.png"; import zoomLogo from "../../assets/zoom.png"; import LoginIcon from "@mui/icons-material/Login"; import useAuth from "../../hooks/useAuth"; -import { store } from "../../redux/store"; -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 { @@ -27,7 +23,6 @@ const Login: React.FC = () => { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); - const [logedInUser, setLogedInUser] = useState(""); // const [errMsg, setErrMsg] = useState(''); // const userRef = useRef(); @@ -41,16 +36,12 @@ const Login: React.FC = () => { // setErrMsg(''); // }, [user, pwd]) - store.dispatch(fetchMeetings("1")); // temp fix - store.dispatch(fetchUsers("1")); // temp fix - store.dispatch(fetchFavorites("1")); // temp fix - const handleLogin = async(e: React.SyntheticEvent) => { e.preventDefault(); try { if (email === "" && password === "") { - setAuth["email"] = email; + setAuth["uuid"] = ""; setAuth["isLoggedIn"] = true; navigate(from, { replace: true }); } @@ -69,9 +60,8 @@ const Login: React.FC = () => { console.log(response?.data); if (logedInUserId != undefined) { - setAuth["email"] = email; + setAuth["uuid"] = logedInUserId; setAuth["isLoggedIn"] = true; - // setLogedInUser(logedInUserId); navigate(from, { replace: true }); } } catch (error) { diff --git a/src/context/AuthProvider.tsx b/src/context/AuthProvider.tsx index 6accd02..793946c 100644 --- a/src/context/AuthProvider.tsx +++ b/src/context/AuthProvider.tsx @@ -1,17 +1,17 @@ import { createContext, useState } from "react"; interface loginInfo { - email: string; + uuid: string; isLoggedIn: boolean; } const AuthContext = createContext({ - email: "", + uuid: "", isLoggedIn: false, }); export const AuthProvider = ({ children }: { children: React.ReactNode }) => { - const [auth] = useState({ email: "", isLoggedIn: false }); + const [auth] = useState({ uuid: "", isLoggedIn: false }); return {children}; }; diff --git a/src/hooks/useAuth.tsx b/src/hooks/useAuth.tsx index af7ffe0..17bf4f8 100644 --- a/src/hooks/useAuth.tsx +++ b/src/hooks/useAuth.tsx @@ -2,7 +2,7 @@ import { useContext } from "react"; import AuthContext from "../context/AuthProvider"; interface loginInfo { - email: string; + uuid: string; isLoggedIn: boolean; }