From 5ef38dad1adc20887f6f891edc0a1af2589e294e Mon Sep 17 00:00:00 2001 From: mbalsdon Date: Fri, 25 Mar 2022 00:07:02 -0700 Subject: [PATCH 01/13] 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/13] 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/13] 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/13] 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/13] 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 5119cd52729221f6bcaa4f0432ec07498c8fa76f Mon Sep 17 00:00:00 2001 From: CodeServer Date: Fri, 25 Mar 2022 09:01:20 +0000 Subject: [PATCH 06/13] trying to fix meetings panel (still have bug) --- src/components/home/MeetingsPanel.tsx | 28 +++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/components/home/MeetingsPanel.tsx b/src/components/home/MeetingsPanel.tsx index 96c7dca..5fc25bf 100644 --- a/src/components/home/MeetingsPanel.tsx +++ b/src/components/home/MeetingsPanel.tsx @@ -5,10 +5,24 @@ import { useAppSelector } from "../../redux/hooks"; import { selectMeetings } from "../../redux/slices/meetingsAndUserStatusSlice"; import { selectUsers } from "../../redux/slices/usersSlice"; import { formatTimeFromDate } from "../../utils"; +import UserLite from "../../api-bodies/UserLite"; const MeetingsPanel: React.FC = () => { const meetings = useAppSelector(selectMeetings); + let uuids: string[] = []; + meetings.forEach((meeting) => { + meeting.liveParticipantIds.forEach((uuid) => { + if(!uuids.includes(uuid)) { + uuids.push(uuid); + } + }); + }); + + const participants: UserLite[] = useAppSelector((state) => + selectUsers(state, uuids) + ); + return (
@@ -23,16 +37,22 @@ const MeetingsPanel: React.FC = () => {
{meetings.map((meeting) => { - const meetingMembers = useAppSelector((state) => - selectUsers(state, meeting.liveParticipantIds) - ); + // const meetingMembers = useAppSelector((state) => + // selectUsers(state, meeting.liveParticipantIds) + // ); + const meetingMembers: UserLite[] = []; + participants.forEach((userLite) => { + if (meeting.liveParticipantIds.includes(userLite.uuid)) { + meetingMembers.push(userLite); + } + }); const startDate = new Date(meeting.start); 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); + setInterval(() => setCurrentDate(new Date()), 30000); }, []); const currentDatemil = currentDate.getTime(); From ee77e90aa279ccda7f01956a6f80fff65a4b929b Mon Sep 17 00:00:00 2001 From: CodeServer Date: Fri, 25 Mar 2022 10:43:05 +0000 Subject: [PATCH 07/13] fixed the but --- src/components/home/MeetingsPanel.tsx | 20 ++++++++++++++------ src/components/login/Login.tsx | 14 +++++++------- src/components/navbar/Clock.tsx | 2 +- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/components/home/MeetingsPanel.tsx b/src/components/home/MeetingsPanel.tsx index 5fc25bf..778b36a 100644 --- a/src/components/home/MeetingsPanel.tsx +++ b/src/components/home/MeetingsPanel.tsx @@ -10,7 +10,7 @@ import UserLite from "../../api-bodies/UserLite"; const MeetingsPanel: React.FC = () => { const meetings = useAppSelector(selectMeetings); - let uuids: string[] = []; + const uuids: string[] = []; meetings.forEach((meeting) => { meeting.liveParticipantIds.forEach((uuid) => { if(!uuids.includes(uuid)) { @@ -18,10 +18,18 @@ const MeetingsPanel: React.FC = () => { } }); }); - - const participants: UserLite[] = useAppSelector((state) => - selectUsers(state, uuids) + + const participants = useAppSelector((state) => + selectUsers(state,uuids) ); + + // const participants: (UserLite | undefined)[] = []; + // uuids.forEach((uuid) => { + // const userLite = useAppSelector((state) => + // selectUser(state,uuid) + // ); + // participants.push(userLite); + // }); return ( @@ -42,7 +50,7 @@ const MeetingsPanel: React.FC = () => { // ); const meetingMembers: UserLite[] = []; participants.forEach((userLite) => { - if (meeting.liveParticipantIds.includes(userLite.uuid)) { + if (userLite != undefined && meeting.liveParticipantIds.includes(userLite.uuid)) { meetingMembers.push(userLite); } }); @@ -52,7 +60,7 @@ const MeetingsPanel: React.FC = () => { const endDate = new Date(endDatemil); const [currentDate, setCurrentDate] = useState(new Date()); useEffect(() => { - setInterval(() => setCurrentDate(new Date()), 30000); + setInterval(() => setCurrentDate(new Date()), 1000); }, []); const currentDatemil = currentDate.getTime(); diff --git a/src/components/login/Login.tsx b/src/components/login/Login.tsx index 397c4a9..493ab33 100644 --- a/src/components/login/Login.tsx +++ b/src/components/login/Login.tsx @@ -27,6 +27,7 @@ const Login: React.FC = () => { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); + const [logedInUser, setLogedInUser] = useState(""); // const [errMsg, setErrMsg] = useState(''); // const userRef = useRef(); @@ -40,6 +41,10 @@ const Login: React.FC = () => { // setErrMsg(''); // }, [user, pwd]) + store.dispatch(fetchMeetings(logedInUser)); + store.dispatch(fetchUsers(logedInUser)); + store.dispatch(fetchFavorites(logedInUser)); + const handleLogin = async(e: React.SyntheticEvent) => { e.preventDefault(); @@ -47,10 +52,7 @@ const Login: React.FC = () => { if (email === "" && password === "") { setAuth["email"] = email; setAuth["isLoggedIn"] = true; - - store.dispatch(fetchMeetings("")); - store.dispatch(fetchUsers("")); - store.dispatch(fetchFavorites("")); + navigate(from, { replace: true }); } @@ -71,9 +73,7 @@ const Login: React.FC = () => { setAuth["email"] = email; setAuth["isLoggedIn"] = true; - store.dispatch(fetchMeetings(logedInUserId)); - store.dispatch(fetchUsers(logedInUserId)); - store.dispatch(fetchFavorites(logedInUserId)); + setLogedInUser(logedInUserId); navigate(from, { replace: true }); } diff --git a/src/components/navbar/Clock.tsx b/src/components/navbar/Clock.tsx index 2de6a3b..d42da13 100644 --- a/src/components/navbar/Clock.tsx +++ b/src/components/navbar/Clock.tsx @@ -13,7 +13,7 @@ const Clock: React.FC = () => { const [date, setDate] = useState(new Date()); useEffect(() => { - setInterval(() => setDate(new Date()), 30000); + setInterval(() => setDate(new Date()), 1000); }, []); return ( From 7b9ed5e313b8703a763a588ecfaf1a0b92d4cc44 Mon Sep 17 00:00:00 2001 From: mbalsdon Date: Fri, 25 Mar 2022 09:55:20 -0700 Subject: [PATCH 08/13] 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 62d120afb7edaf0c3983d621775dd5a82a4cc874 Mon Sep 17 00:00:00 2001 From: mbalsdon Date: Fri, 25 Mar 2022 11:19:40 -0700 Subject: [PATCH 09/13] 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/13] 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/13] 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/13] 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/13] 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 {