diff --git a/src/ProtectedRoute.tsx b/src/ProtectedRoute.tsx index 5d8d12f..0288f33 100644 --- a/src/ProtectedRoute.tsx +++ b/src/ProtectedRoute.tsx @@ -3,21 +3,18 @@ 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)); - } + // if (auth?.isLoggedIn != undefined && auth?.isLoggedIn == true) { + // store.dispatch(fetchMeetings(auth?.uuid)); + // store.dispatch(fetchUsers(auth?.uuid)); + // store.dispatch(fetchFavorites(auth?.uuid)); + // store.dispatch(socketActions.startConnecting()); + // } return auth?.isLoggedIn ? ( <> diff --git a/src/api-bodies/DetailedMeeting.tsx b/src/api-bodies/DetailedMeeting.tsx index eb64a19..91a7028 100644 --- a/src/api-bodies/DetailedMeeting.tsx +++ b/src/api-bodies/DetailedMeeting.tsx @@ -2,7 +2,7 @@ interface DetailedMeeting { meetingId: string; // primary key liveParticipantIds: string[]; registrantIds: string[]; - start: string; + startTime: string; duration: number; timezone: string; joinUrl: string; diff --git a/src/api-bodies/MockData.tsx b/src/api-bodies/MockData.tsx index 6c114b3..ff009b7 100644 --- a/src/api-bodies/MockData.tsx +++ b/src/api-bodies/MockData.tsx @@ -48,7 +48,7 @@ const meetings = [ meetingId: "", liveParticipantIds: [], registrantIds: ["", "1", "2", "3", "4", "5", "6"], - start: "2022-03-13T17:00:00", + startTime: "2022-03-13T17:00:00", duration: 15, timezone: "", joinUrl: "", @@ -58,7 +58,7 @@ const meetings = [ meetingId: "1", liveParticipantIds: [], registrantIds: ["", "2", "4"], - start: "2022-03-16T17:30:00", + startTime: "2022-03-16T17:30:00", duration: 30, timezone: "", joinUrl: "", @@ -68,7 +68,7 @@ const meetings = [ meetingId: "2", liveParticipantIds: ["3", "5"], registrantIds: ["3", "5", "6"], - start: "2022-03-13T17:30:00", + startTime: "2022-03-13T17:30:00", duration: 30, timezone: "", joinUrl: "", @@ -78,7 +78,7 @@ const meetings = [ meetingId: "3", liveParticipantIds: [], registrantIds: ["", "1"], - start: "2022-03-10T07:27:27", + startTime: "2022-03-10T07:27:27", duration: 727, timezone: "", joinUrl: "", @@ -88,7 +88,7 @@ const meetings = [ meetingId: "4", liveParticipantIds: [], registrantIds: ["", "2", "3"], - start: "2022-03-10T12:30:00", + startTime: "2022-03-10T12:30:00", duration: 120, timezone: "", joinUrl: "", @@ -98,7 +98,7 @@ const meetings = [ meetingId: "5", liveParticipantIds: [""], registrantIds: [""], - start: "2022-03-24T23:11:11", + startTime: "2022-03-24T23:11:11", duration: 11, timezone: "", joinUrl: "", @@ -108,7 +108,7 @@ const meetings = [ meetingId: "6", liveParticipantIds: ["", "1"], registrantIds: ["", "1", "2", "3", "4", "5"], - start: "2022-03-25T09:00:00", + startTime: "2022-03-25T09:00:00", duration: 360, timezone: "", joinUrl: "", @@ -118,7 +118,7 @@ const meetings = [ meetingId: "7", liveParticipantIds: [], registrantIds: ["", "5"], - start: "2022-03-25T15:00:00", + startTime: "2022-03-25T15:00:00", duration: 150, timezone: "", joinUrl: "", @@ -128,7 +128,7 @@ const meetings = [ meetingId: "8", liveParticipantIds: ["2"], registrantIds: ["", "5", "2", "3"], - start: "2022-03-25T17:45:00", + startTime: "2022-03-25T17:45:00", duration: 60, timezone: "", joinUrl: "", @@ -138,7 +138,7 @@ const meetings = [ meetingId: "9", liveParticipantIds: ["2", "5"], registrantIds: ["", "4"], - start: "2022-03-25T18:15:30", + startTime: "2022-03-25T18:15:30", duration: 75, timezone: "", joinUrl: "", @@ -148,7 +148,7 @@ const meetings = [ meetingId: "10", liveParticipantIds: [], registrantIds: ["", "1", "2", "3", "4", "5", "6"], - start: "2022-04-04T18:30:00", + startTime: "2022-04-04T18:30:00", duration: 90, timezone: "", joinUrl: "", diff --git a/src/api-bodies/NewMeeting.tsx b/src/api-bodies/NewMeeting.tsx index a36bc0b..e53d5b7 100644 --- a/src/api-bodies/NewMeeting.tsx +++ b/src/api-bodies/NewMeeting.tsx @@ -1,10 +1,8 @@ interface NewMeeting { - uuid: string; startTime: string; duration: number; - timezone: string; topic: string; - registrantsIds: string[]; + registrantIds: string[]; } export default NewMeeting; diff --git a/src/api-bodies/UserFull.tsx b/src/api-bodies/UserFull.tsx index edd0d7b..3508286 100644 --- a/src/api-bodies/UserFull.tsx +++ b/src/api-bodies/UserFull.tsx @@ -2,7 +2,7 @@ import UserLite from "./UserLite"; interface UserFull { userInfo: UserLite; - manager: UserLite; + manager?: UserLite; managerDirectReports: UserLite[]; userDirectReports: UserLite[]; } diff --git a/src/components/calendar/CalendarPage.tsx b/src/components/calendar/CalendarPage.tsx index 0929819..9ce4028 100644 --- a/src/components/calendar/CalendarPage.tsx +++ b/src/components/calendar/CalendarPage.tsx @@ -64,15 +64,15 @@ const CalendarPage: React.FC = () => { meetingId: m.meetingId, liveParticipantIds: m.liveParticipantIds, registrantIds: m.registrantIds, - startIso: m.start, + startIso: m.startTime, 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 + start: new Date(Date.parse(m.startTime)), // Turns the ISO String into a date object + end: new Date(Date.parse(m.startTime) + m.duration * 60000), // result of Date.parse() is milliseconds, and m.duration is given in minutes })); const [selectedUserUuid, setSelectedUserUuid] = useState(currentUserUuid); @@ -83,7 +83,7 @@ const CalendarPage: React.FC = () => { meetingId: event.meetingId, liveParticipantIds: event.liveParticipantIds, registrantIds: event.registrantIds, - start: event.startIso, + startTime: event.startIso, duration: event.duration, timezone: event.timezone, joinUrl: event.joinUrl, @@ -108,14 +108,16 @@ const CalendarPage: React.FC = () => { }; return ( - - + @@ -156,13 +158,12 @@ const CalendarPage: React.FC = () => { showMultiDayTimes localizer={momentLocalizer(moment)} style={{ height: "83%" }} - eventPropGetter = {() => { + eventPropGetter={() => { const backgroundColor = "IndianRed"; const borderColor = "White"; return { style: { backgroundColor, borderColor } }; }} /> - ); }; diff --git a/src/components/contacts/Contacts.tsx b/src/components/contacts/Contacts.tsx index 14c7844..c013595 100644 --- a/src/components/contacts/Contacts.tsx +++ b/src/components/contacts/Contacts.tsx @@ -7,6 +7,7 @@ import UserLite from "../../api-bodies/UserLite"; import { useParams } from "react-router-dom"; import { selectUser } from "../../redux/slices/usersSlice"; import { useAppSelector } from "../../redux/hooks"; +import EmptyBody from "./contacts-components/EmptyBody"; const Contacts: React.FC = () => { const { uuid } = useParams(); @@ -15,9 +16,14 @@ const Contacts: React.FC = () => { ); return ( - + - {uriContact ? : null} + {uriContact ? : } ); }; diff --git a/src/components/contacts/Utils.tsx b/src/components/contacts/Utils.tsx index 797a2f5..037f911 100644 --- a/src/components/contacts/Utils.tsx +++ b/src/components/contacts/Utils.tsx @@ -1,7 +1,7 @@ import DetailedMeeting from "../../api-bodies/DetailedMeeting"; const getUpcomingMeetingTime = (meeting: DetailedMeeting) => { - const startDate = new Date(meeting.start); + const startDate = new Date(meeting.startTime); const endDate = new Date(startDate.getTime() + meeting.duration * 60000); const startTime = startDate .toTimeString() diff --git a/src/components/contacts/contacts-components/EmptyBody.tsx b/src/components/contacts/contacts-components/EmptyBody.tsx new file mode 100644 index 0000000..a3fa3d6 --- /dev/null +++ b/src/components/contacts/contacts-components/EmptyBody.tsx @@ -0,0 +1,29 @@ +import { Box, Typography } from "@mui/material"; +import React from "react"; +import PermContactCalendarIcon from "@mui/icons-material/PermContactCalendar"; + +const EmptyBody: React.FC = () => { + return ( + + + + + + + View contact info by clicking a contact in the left panel + + + + ); +}; + +export default EmptyBody; diff --git a/src/components/contacts/contacts-components/body-components/LowerBody.tsx b/src/components/contacts/contacts-components/body-components/LowerBody.tsx index f7645b2..fcae8ea 100644 --- a/src/components/contacts/contacts-components/body-components/LowerBody.tsx +++ b/src/components/contacts/contacts-components/body-components/LowerBody.tsx @@ -32,32 +32,42 @@ const LowerBody: React.FC = (props) => { Upcoming meetings - - {meetings.map((meeting, i) => ( - - - - {getUpcomingMeetingTime(meeting)} - - - ))} - + + + {getUpcomingMeetingTime(meeting)} + + + ))} + + )} ); }; diff --git a/src/components/contacts/contacts-components/body-components/UpperBody.tsx b/src/components/contacts/contacts-components/body-components/UpperBody.tsx index 10b4560..1da27f8 100644 --- a/src/components/contacts/contacts-components/body-components/UpperBody.tsx +++ b/src/components/contacts/contacts-components/body-components/UpperBody.tsx @@ -1,8 +1,6 @@ import { Box, Button, IconButton, Typography } from "@mui/material"; import React from "react"; import PhoneIcon from "@mui/icons-material/Phone"; -import VideocamIcon from "@mui/icons-material/Videocam"; -import GroupsIcon from "@mui/icons-material/Groups"; import AddIcon from "@mui/icons-material/Add"; import UserLite from "../../../../api-bodies/UserLite"; import UserStatus from "../../../../api-bodies/UserStatus"; @@ -18,26 +16,50 @@ import { } from "../../../../redux/slices/favoritesSlice"; import RemoveIcon from "@mui/icons-material/Remove"; import { MeetingStatus } from "../../../../utils"; -import { selectMe } from "../../../../redux/slices/usersSlice"; +import { selectManager, selectMe } from "../../../../redux/slices/usersSlice"; +import axios from "../../../../api/axios"; +import NewMeeting from "../../../../api-bodies/NewMeeting"; interface Props { contactInfo: UserLite; } -const UpperBody: React.FC = (props) => { +const UpperBody: React.FC = ({ contactInfo }) => { const dispatch = useAppDispatch(); const userStatus: UserStatus = useAppSelector((state) => - selectUserStatus(state, props.contactInfo.uuid) + selectUserStatus(state, contactInfo.uuid) ); const me = useAppSelector(selectMe); const favoritesUuids = useAppSelector(selectFavorites); const detailedMeeting = useAppSelector((state) => - selectMeeting(state, userStatus.meetingId) + selectMeeting(state, userStatus.inMeeting ? userStatus.meetingId : null) ); + const managerId = useAppSelector(selectManager); const meetingStatus: MeetingStatus = - userStatus && userStatus.inMeeting - ? MeetingStatus.IN_MEETING - : MeetingStatus.NOT_IN_MEETING; + managerId === contactInfo.uuid + ? MeetingStatus.NOT_AVAILABLE + : userStatus && userStatus.inMeeting + ? MeetingStatus.IN_MEETING + : MeetingStatus.NOT_IN_MEETING; + + const startCall = async () => { + const newMeeting: NewMeeting = { + startTime: "2022-03-30T23:40:00Z", + duration: 30, + topic: `Meeting with ${contactInfo.name}`, + registrantIds: [contactInfo.uuid], + }; + try { + const response = await axios.post( + `/users/${me}/meetings`, + JSON.stringify(newMeeting) + ); + window.open(response.data.joinUrl, "_blank")?.focus(); + } catch (e) { + console.log(e); + } + }; + return ( = (props) => { mx: 4, }} > - {props.contactInfo.name} - {!favoritesUuids.includes(props.contactInfo.uuid) ? ( + {contactInfo.name} + {!favoritesUuids.includes(contactInfo.uuid) ? ( - = ({ user, status }: Props) => { setAnchorEl(null); }; + const startCall = async () => { + const newMeeting: NewMeeting = { + startTime: "2022-03-30T23:40:00Z", + duration: 30, + topic: `Meeting with ${user.name}`, + registrantIds: [user.uuid], + }; + try { + const response = await axios.post( + `/users/${me}/meetings`, + JSON.stringify(newMeeting) + ); + window.open(response.data.joinUrl, "_blank")?.focus(); + } catch (e) { + console.log(e); + } + }; + return (
= ({ user, status }: Props) => { > View upcoming meetings - Create meeting + { + handleClose(); + startCall(); + }} + > + Create meeting +
); diff --git a/src/redux/middleware/socketMiddleware.tsx b/src/redux/middleware/socketMiddleware.tsx index 4f47d2f..c843f0f 100644 --- a/src/redux/middleware/socketMiddleware.tsx +++ b/src/redux/middleware/socketMiddleware.tsx @@ -1,10 +1,8 @@ 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; + let socket: WebSocket; return (next) => (action) => { const isConnectionEstablished = @@ -14,28 +12,23 @@ const socketMiddleware: Middleware = (store) => { socketActions.startConnecting.match(action) && !isConnectionEstablished ) { - console.log("startConnecting called"); - socket = io("wss://uo5wdcbn6l.execute-api.us-west-2.amazonaws.com/Prod/"); - - socket.on("Connect", () => { - console.log("connected!!!"); - store.dispatch(socketActions.connectionEstablished()); - }); - - socket.on("MeetingCreated", (meeting: DetailedMeeting) => { - store.dispatch(socketActions.meetingCreated(meeting)); - }); - - socket.on( - "UserStatusChange", - (statusChange: { - userId: string; - inMeeting: boolean; - meetingId: string; - }) => { - store.dispatch(socketActions.userStatusChanged(statusChange)); - } + console.log("start connecting"); + socket = new WebSocket( + "wss://uo5wdcbn6l.execute-api.us-west-2.amazonaws.com/Prod/" ); + socket.onopen = () => { + console.log("connected"); + store.dispatch(socketActions.connectionEstablished()); + }; + socket.addEventListener("message", (event: MessageEvent) => { + const json = JSON.parse(event.data); + console.log(json); + if ("inMeeting" in json) { + store.dispatch(socketActions.userStatusChanged(json)); + } else { + store.dispatch(socketActions.meetingCreated(json)); + } + }); } next(action); }; diff --git a/src/redux/slices/meetingsAndUserStatusSlice.tsx b/src/redux/slices/meetingsAndUserStatusSlice.tsx index 1b7a411..6298a9d 100644 --- a/src/redux/slices/meetingsAndUserStatusSlice.tsx +++ b/src/redux/slices/meetingsAndUserStatusSlice.tsx @@ -31,10 +31,10 @@ export const meetingsAndUserStatusSlice = createSlice({ state.isConnected = true; }, meetingCreated: (state, action) => { - state.meetings.push(action.payload.meeting); + state.meetings.push(action.payload); }, userStatusChanged: (state, action) => { - state.userStatuses[action.payload.uuid] = action.payload; + state.userStatuses[action.payload.userId] = action.payload; }, }, extraReducers(builder) { @@ -90,9 +90,25 @@ export const selectMeeting = (state: RootState, meetingID: string | null) => { : null; }; export const selectUserUpcomingMeetings = (state: RootState, uuid: string) => { - return state.meetingsAndUserStatuses.meetings.filter((meeting) => - meeting.registrantIds.includes(uuid) - ); + const isToday = (date: Date) => { + const today = new Date(); + return ( + date.getDate() == today.getDate() && + date.getMonth() == today.getMonth() && + date.getFullYear() == today.getFullYear() + ); + }; + return state.meetingsAndUserStatuses.meetings + .filter( + (meeting) => + meeting.registrantIds.includes(uuid) && + meeting.startTime && + isToday(new Date(meeting.startTime)) + ) + .sort( + (a, b) => + new Date(a.startTime).getTime() - new Date(b.startTime).getTime() + ); }; export const selectUserStatus = ( state: RootState, diff --git a/src/redux/slices/usersSlice.tsx b/src/redux/slices/usersSlice.tsx index 8539c5f..35a0482 100644 --- a/src/redux/slices/usersSlice.tsx +++ b/src/redux/slices/usersSlice.tsx @@ -47,14 +47,14 @@ export const fetchUsers = createAsyncThunk( }; // fetch userfull const userResp = await axios.get(`/users/${uuid}`); - const user: UserFull = userResp.data.user; - + const user: UserFull = userResp.data["user"]; + console.log("1"); users[user.userInfo.uuid] = user.userInfo; - users[user.manager.uuid] = user.manager; + console.log("2"); + if (user.manager) users[user.manager.uuid] = user.manager; team.user = user.userInfo.uuid; - team.manager = user.manager.uuid; - + if (user.manager) team.manager = user.manager.uuid; user.userDirectReports.forEach((userLite) => { users[userLite.uuid] = userLite; team.directReports.push(userLite.uuid); @@ -76,6 +76,7 @@ export const fetchUsers = createAsyncThunk( ); export const selectMe = (state: RootState) => state.users.team.user; +export const selectManager = (state: RootState) => state.users.team.manager; export const selectUser = ( state: RootState, uuid: string | undefined diff --git a/src/utils.tsx b/src/utils.tsx index 3e2ad36..e537354 100644 --- a/src/utils.tsx +++ b/src/utils.tsx @@ -1,6 +1,7 @@ const enum MeetingStatus { NOT_IN_MEETING = "Not in meeting", IN_MEETING = "In meeting", + NOT_AVAILABLE = "Not available", } const getStatusColor = (ms: MeetingStatus): string => { @@ -11,6 +12,9 @@ const getStatusColor = (ms: MeetingStatus): string => { case MeetingStatus.IN_MEETING: { return "#ff7070"; } + case MeetingStatus.NOT_AVAILABLE: { + return "#808080"; + } } };