Merge pull request #60 from CPSC319-Winter-term-2/various-fixes
linter fix
This commit is contained in:
commit
667886224c
@ -3,21 +3,18 @@ import useAuth from "./hooks/useAuth";
|
|||||||
import Navbar from "./components/navbar/Navbar";
|
import Navbar from "./components/navbar/Navbar";
|
||||||
import Sidebar from "./components/sidebar/Sidebar";
|
import Sidebar from "./components/sidebar/Sidebar";
|
||||||
import { Box } from "@mui/material";
|
import { Box } from "@mui/material";
|
||||||
import { store } from "./redux/store";
|
|
||||||
import { fetchFavorites } from "./redux/slices/favoritesSlice";
|
|
||||||
import { fetchMeetings } from "./redux/slices/meetingsAndUserStatusSlice";
|
|
||||||
import { fetchUsers } from "./redux/slices/usersSlice";
|
|
||||||
|
|
||||||
const ProtectedRoute = () => {
|
const ProtectedRoute = () => {
|
||||||
const auth = useAuth();
|
const auth = useAuth();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
/* Temporary data */
|
/* Temporary data */
|
||||||
if (auth?.isLoggedIn != undefined && auth?.isLoggedIn == true) {
|
// if (auth?.isLoggedIn != undefined && auth?.isLoggedIn == true) {
|
||||||
store.dispatch(fetchMeetings(auth?.uuid));
|
// store.dispatch(fetchMeetings(auth?.uuid));
|
||||||
store.dispatch(fetchUsers(auth?.uuid));
|
// store.dispatch(fetchUsers(auth?.uuid));
|
||||||
store.dispatch(fetchFavorites(auth?.uuid));
|
// store.dispatch(fetchFavorites(auth?.uuid));
|
||||||
}
|
// store.dispatch(socketActions.startConnecting());
|
||||||
|
// }
|
||||||
return auth?.isLoggedIn ? (
|
return auth?.isLoggedIn ? (
|
||||||
<>
|
<>
|
||||||
<Navbar />
|
<Navbar />
|
||||||
|
|||||||
@ -2,7 +2,7 @@ interface DetailedMeeting {
|
|||||||
meetingId: string; // primary key
|
meetingId: string; // primary key
|
||||||
liveParticipantIds: string[];
|
liveParticipantIds: string[];
|
||||||
registrantIds: string[];
|
registrantIds: string[];
|
||||||
start: string;
|
startTime: string;
|
||||||
duration: number;
|
duration: number;
|
||||||
timezone: string;
|
timezone: string;
|
||||||
joinUrl: string;
|
joinUrl: string;
|
||||||
|
|||||||
@ -48,7 +48,7 @@ const meetings = [
|
|||||||
meetingId: "",
|
meetingId: "",
|
||||||
liveParticipantIds: [],
|
liveParticipantIds: [],
|
||||||
registrantIds: ["", "1", "2", "3", "4", "5", "6"],
|
registrantIds: ["", "1", "2", "3", "4", "5", "6"],
|
||||||
start: "2022-03-13T17:00:00",
|
startTime: "2022-03-13T17:00:00",
|
||||||
duration: 15,
|
duration: 15,
|
||||||
timezone: "",
|
timezone: "",
|
||||||
joinUrl: "",
|
joinUrl: "",
|
||||||
@ -58,7 +58,7 @@ const meetings = [
|
|||||||
meetingId: "1",
|
meetingId: "1",
|
||||||
liveParticipantIds: [],
|
liveParticipantIds: [],
|
||||||
registrantIds: ["", "2", "4"],
|
registrantIds: ["", "2", "4"],
|
||||||
start: "2022-03-16T17:30:00",
|
startTime: "2022-03-16T17:30:00",
|
||||||
duration: 30,
|
duration: 30,
|
||||||
timezone: "",
|
timezone: "",
|
||||||
joinUrl: "",
|
joinUrl: "",
|
||||||
@ -68,7 +68,7 @@ const meetings = [
|
|||||||
meetingId: "2",
|
meetingId: "2",
|
||||||
liveParticipantIds: ["3", "5"],
|
liveParticipantIds: ["3", "5"],
|
||||||
registrantIds: ["3", "5", "6"],
|
registrantIds: ["3", "5", "6"],
|
||||||
start: "2022-03-13T17:30:00",
|
startTime: "2022-03-13T17:30:00",
|
||||||
duration: 30,
|
duration: 30,
|
||||||
timezone: "",
|
timezone: "",
|
||||||
joinUrl: "",
|
joinUrl: "",
|
||||||
@ -78,7 +78,7 @@ const meetings = [
|
|||||||
meetingId: "3",
|
meetingId: "3",
|
||||||
liveParticipantIds: [],
|
liveParticipantIds: [],
|
||||||
registrantIds: ["", "1"],
|
registrantIds: ["", "1"],
|
||||||
start: "2022-03-10T07:27:27",
|
startTime: "2022-03-10T07:27:27",
|
||||||
duration: 727,
|
duration: 727,
|
||||||
timezone: "",
|
timezone: "",
|
||||||
joinUrl: "",
|
joinUrl: "",
|
||||||
@ -88,7 +88,7 @@ const meetings = [
|
|||||||
meetingId: "4",
|
meetingId: "4",
|
||||||
liveParticipantIds: [],
|
liveParticipantIds: [],
|
||||||
registrantIds: ["", "2", "3"],
|
registrantIds: ["", "2", "3"],
|
||||||
start: "2022-03-10T12:30:00",
|
startTime: "2022-03-10T12:30:00",
|
||||||
duration: 120,
|
duration: 120,
|
||||||
timezone: "",
|
timezone: "",
|
||||||
joinUrl: "",
|
joinUrl: "",
|
||||||
@ -98,7 +98,7 @@ const meetings = [
|
|||||||
meetingId: "5",
|
meetingId: "5",
|
||||||
liveParticipantIds: [""],
|
liveParticipantIds: [""],
|
||||||
registrantIds: [""],
|
registrantIds: [""],
|
||||||
start: "2022-03-24T23:11:11",
|
startTime: "2022-03-24T23:11:11",
|
||||||
duration: 11,
|
duration: 11,
|
||||||
timezone: "",
|
timezone: "",
|
||||||
joinUrl: "",
|
joinUrl: "",
|
||||||
@ -108,7 +108,7 @@ const meetings = [
|
|||||||
meetingId: "6",
|
meetingId: "6",
|
||||||
liveParticipantIds: ["", "1"],
|
liveParticipantIds: ["", "1"],
|
||||||
registrantIds: ["", "1", "2", "3", "4", "5"],
|
registrantIds: ["", "1", "2", "3", "4", "5"],
|
||||||
start: "2022-03-25T09:00:00",
|
startTime: "2022-03-25T09:00:00",
|
||||||
duration: 360,
|
duration: 360,
|
||||||
timezone: "",
|
timezone: "",
|
||||||
joinUrl: "",
|
joinUrl: "",
|
||||||
@ -118,7 +118,7 @@ const meetings = [
|
|||||||
meetingId: "7",
|
meetingId: "7",
|
||||||
liveParticipantIds: [],
|
liveParticipantIds: [],
|
||||||
registrantIds: ["", "5"],
|
registrantIds: ["", "5"],
|
||||||
start: "2022-03-25T15:00:00",
|
startTime: "2022-03-25T15:00:00",
|
||||||
duration: 150,
|
duration: 150,
|
||||||
timezone: "",
|
timezone: "",
|
||||||
joinUrl: "",
|
joinUrl: "",
|
||||||
@ -128,7 +128,7 @@ const meetings = [
|
|||||||
meetingId: "8",
|
meetingId: "8",
|
||||||
liveParticipantIds: ["2"],
|
liveParticipantIds: ["2"],
|
||||||
registrantIds: ["", "5", "2", "3"],
|
registrantIds: ["", "5", "2", "3"],
|
||||||
start: "2022-03-25T17:45:00",
|
startTime: "2022-03-25T17:45:00",
|
||||||
duration: 60,
|
duration: 60,
|
||||||
timezone: "",
|
timezone: "",
|
||||||
joinUrl: "",
|
joinUrl: "",
|
||||||
@ -138,7 +138,7 @@ const meetings = [
|
|||||||
meetingId: "9",
|
meetingId: "9",
|
||||||
liveParticipantIds: ["2", "5"],
|
liveParticipantIds: ["2", "5"],
|
||||||
registrantIds: ["", "4"],
|
registrantIds: ["", "4"],
|
||||||
start: "2022-03-25T18:15:30",
|
startTime: "2022-03-25T18:15:30",
|
||||||
duration: 75,
|
duration: 75,
|
||||||
timezone: "",
|
timezone: "",
|
||||||
joinUrl: "",
|
joinUrl: "",
|
||||||
@ -148,7 +148,7 @@ const meetings = [
|
|||||||
meetingId: "10",
|
meetingId: "10",
|
||||||
liveParticipantIds: [],
|
liveParticipantIds: [],
|
||||||
registrantIds: ["", "1", "2", "3", "4", "5", "6"],
|
registrantIds: ["", "1", "2", "3", "4", "5", "6"],
|
||||||
start: "2022-04-04T18:30:00",
|
startTime: "2022-04-04T18:30:00",
|
||||||
duration: 90,
|
duration: 90,
|
||||||
timezone: "",
|
timezone: "",
|
||||||
joinUrl: "",
|
joinUrl: "",
|
||||||
|
|||||||
@ -1,10 +1,8 @@
|
|||||||
interface NewMeeting {
|
interface NewMeeting {
|
||||||
uuid: string;
|
|
||||||
startTime: string;
|
startTime: string;
|
||||||
duration: number;
|
duration: number;
|
||||||
timezone: string;
|
|
||||||
topic: string;
|
topic: string;
|
||||||
registrantsIds: string[];
|
registrantIds: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NewMeeting;
|
export default NewMeeting;
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import UserLite from "./UserLite";
|
|||||||
|
|
||||||
interface UserFull {
|
interface UserFull {
|
||||||
userInfo: UserLite;
|
userInfo: UserLite;
|
||||||
manager: UserLite;
|
manager?: UserLite;
|
||||||
managerDirectReports: UserLite[];
|
managerDirectReports: UserLite[];
|
||||||
userDirectReports: UserLite[];
|
userDirectReports: UserLite[];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,15 +64,15 @@ const CalendarPage: React.FC = () => {
|
|||||||
meetingId: m.meetingId,
|
meetingId: m.meetingId,
|
||||||
liveParticipantIds: m.liveParticipantIds,
|
liveParticipantIds: m.liveParticipantIds,
|
||||||
registrantIds: m.registrantIds,
|
registrantIds: m.registrantIds,
|
||||||
startIso: m.start,
|
startIso: m.startTime,
|
||||||
duration: m.duration,
|
duration: m.duration,
|
||||||
timezone: m.timezone,
|
timezone: m.timezone,
|
||||||
joinUrl: m.joinUrl,
|
joinUrl: m.joinUrl,
|
||||||
topic: m.topic,
|
topic: m.topic,
|
||||||
// fields needed by calendar
|
// fields needed by calendar
|
||||||
title: m.topic,
|
title: m.topic,
|
||||||
start: new Date(Date.parse(m.start)), // Turns the ISO String into a date object
|
start: new Date(Date.parse(m.startTime)), // 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
|
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);
|
const [selectedUserUuid, setSelectedUserUuid] = useState(currentUserUuid);
|
||||||
@ -83,7 +83,7 @@ const CalendarPage: React.FC = () => {
|
|||||||
meetingId: event.meetingId,
|
meetingId: event.meetingId,
|
||||||
liveParticipantIds: event.liveParticipantIds,
|
liveParticipantIds: event.liveParticipantIds,
|
||||||
registrantIds: event.registrantIds,
|
registrantIds: event.registrantIds,
|
||||||
start: event.startIso,
|
startTime: event.startIso,
|
||||||
duration: event.duration,
|
duration: event.duration,
|
||||||
timezone: event.timezone,
|
timezone: event.timezone,
|
||||||
joinUrl: event.joinUrl,
|
joinUrl: event.joinUrl,
|
||||||
@ -108,14 +108,16 @@ const CalendarPage: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{
|
<Box
|
||||||
display: "flex",
|
sx={{
|
||||||
flexDirection: "column",
|
display: "flex",
|
||||||
width: "98.5%",
|
flexDirection: "column",
|
||||||
height: "99.5%",
|
width: "98.5%",
|
||||||
ml: 1,
|
height: "99.5%",
|
||||||
mt: 1}}>
|
ml: 1,
|
||||||
|
mt: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Toolbar />
|
<Toolbar />
|
||||||
|
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
@ -156,13 +158,12 @@ const CalendarPage: React.FC = () => {
|
|||||||
showMultiDayTimes
|
showMultiDayTimes
|
||||||
localizer={momentLocalizer(moment)}
|
localizer={momentLocalizer(moment)}
|
||||||
style={{ height: "83%" }}
|
style={{ height: "83%" }}
|
||||||
eventPropGetter = {() => {
|
eventPropGetter={() => {
|
||||||
const backgroundColor = "IndianRed";
|
const backgroundColor = "IndianRed";
|
||||||
const borderColor = "White";
|
const borderColor = "White";
|
||||||
return { style: { backgroundColor, borderColor } };
|
return { style: { backgroundColor, borderColor } };
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import UserLite from "../../api-bodies/UserLite";
|
|||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { selectUser } from "../../redux/slices/usersSlice";
|
import { selectUser } from "../../redux/slices/usersSlice";
|
||||||
import { useAppSelector } from "../../redux/hooks";
|
import { useAppSelector } from "../../redux/hooks";
|
||||||
|
import EmptyBody from "./contacts-components/EmptyBody";
|
||||||
|
|
||||||
const Contacts: React.FC = () => {
|
const Contacts: React.FC = () => {
|
||||||
const { uuid } = useParams();
|
const { uuid } = useParams();
|
||||||
@ -15,9 +16,14 @@ const Contacts: React.FC = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ display: "flex", height: "100%" }}>
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
height: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
{uriContact ? <Body contactSelected={uriContact} /> : null}
|
{uriContact ? <Body contactSelected={uriContact} /> : <EmptyBody />}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import DetailedMeeting from "../../api-bodies/DetailedMeeting";
|
import DetailedMeeting from "../../api-bodies/DetailedMeeting";
|
||||||
|
|
||||||
const getUpcomingMeetingTime = (meeting: 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 endDate = new Date(startDate.getTime() + meeting.duration * 60000);
|
||||||
const startTime = startDate
|
const startTime = startDate
|
||||||
.toTimeString()
|
.toTimeString()
|
||||||
|
|||||||
29
src/components/contacts/contacts-components/EmptyBody.tsx
Normal file
29
src/components/contacts/contacts-components/EmptyBody.tsx
Normal file
@ -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 (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box>
|
||||||
|
<PermContactCalendarIcon color="secondary" sx={{ fontSize: 100 }} />
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Typography>
|
||||||
|
View contact info by clicking a contact in the left panel
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EmptyBody;
|
||||||
@ -34,32 +34,42 @@ const LowerBody: React.FC<Props> = (props) => {
|
|||||||
<Typography sx={{ color: "white", my: 1 }} variant="h4" textAlign="center">
|
<Typography sx={{ color: "white", my: 1 }} variant="h4" textAlign="center">
|
||||||
Upcoming meetings
|
Upcoming meetings
|
||||||
</Typography>
|
</Typography>
|
||||||
{/* <Divider color="indianred" /> */}
|
{meetings.length === 0 ? (
|
||||||
<List sx={{ maxHeight: "100%", overflow: "auto" }}>
|
<Typography
|
||||||
{meetings.map((meeting, i) => (
|
variant="h5"
|
||||||
<Box
|
textAlign="center"
|
||||||
sx={{
|
sx={{ position: "relative", top: "30%" }}
|
||||||
display: "flex",
|
>
|
||||||
justifyContent: "space-between",
|
No upcoming meetings today
|
||||||
height: "50px",
|
</Typography>
|
||||||
px: "10px",
|
) : (
|
||||||
backgroundColor: i % 2 ? "mistyrose" : "white",
|
<List sx={{ maxHeight: "100%", overflow: "auto" }}>
|
||||||
}}
|
{meetings.map((meeting, i) => (
|
||||||
key={i}
|
<Box
|
||||||
>
|
sx={{
|
||||||
<Button
|
display: "flex",
|
||||||
variant="text"
|
justifyContent: "space-between",
|
||||||
color="info"
|
height: "50px",
|
||||||
onClick={() => dispatch(open(meeting))}
|
px: "10px",
|
||||||
|
borderTop: 1,
|
||||||
|
borderBottom: i === meetings.length - 1 ? 1 : 0,
|
||||||
|
}}
|
||||||
|
key={i}
|
||||||
>
|
>
|
||||||
{meeting.topic}
|
<Button
|
||||||
</Button>
|
variant="text"
|
||||||
<Typography sx={{ pt: 1.5 }}>
|
color="info"
|
||||||
{getUpcomingMeetingTime(meeting)}
|
onClick={() => dispatch(open(meeting))}
|
||||||
</Typography>
|
>
|
||||||
</Box>
|
{meeting.topic}
|
||||||
))}
|
</Button>
|
||||||
</List>
|
<Typography sx={{ pt: 1.5 }}>
|
||||||
|
{getUpcomingMeetingTime(meeting)}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
import { Box, Button, IconButton, Typography } from "@mui/material";
|
import { Box, Button, IconButton, Typography } from "@mui/material";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PhoneIcon from "@mui/icons-material/Phone";
|
import PhoneIcon from "@mui/icons-material/Phone";
|
||||||
import VideocamIcon from "@mui/icons-material/Videocam";
|
|
||||||
import GroupsIcon from "@mui/icons-material/Groups";
|
|
||||||
import AddIcon from "@mui/icons-material/Add";
|
import AddIcon from "@mui/icons-material/Add";
|
||||||
import UserLite from "../../../../api-bodies/UserLite";
|
import UserLite from "../../../../api-bodies/UserLite";
|
||||||
import UserStatus from "../../../../api-bodies/UserStatus";
|
import UserStatus from "../../../../api-bodies/UserStatus";
|
||||||
@ -18,26 +16,50 @@ import {
|
|||||||
} from "../../../../redux/slices/favoritesSlice";
|
} from "../../../../redux/slices/favoritesSlice";
|
||||||
import RemoveIcon from "@mui/icons-material/Remove";
|
import RemoveIcon from "@mui/icons-material/Remove";
|
||||||
import { MeetingStatus } from "../../../../utils";
|
import { MeetingStatus } from "../../../../utils";
|
||||||
import { selectMe } from "../../../../redux/slices/usersSlice";
|
import { selectManager, selectMe } from "../../../../redux/slices/usersSlice";
|
||||||
|
import axios from "../../../../api/axios";
|
||||||
|
import NewMeeting from "../../../../api-bodies/NewMeeting";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
contactInfo: UserLite;
|
contactInfo: UserLite;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UpperBody: React.FC<Props> = (props) => {
|
const UpperBody: React.FC<Props> = ({ contactInfo }) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const userStatus: UserStatus = useAppSelector((state) =>
|
const userStatus: UserStatus = useAppSelector((state) =>
|
||||||
selectUserStatus(state, props.contactInfo.uuid)
|
selectUserStatus(state, contactInfo.uuid)
|
||||||
);
|
);
|
||||||
const me = useAppSelector(selectMe);
|
const me = useAppSelector(selectMe);
|
||||||
const favoritesUuids = useAppSelector(selectFavorites);
|
const favoritesUuids = useAppSelector(selectFavorites);
|
||||||
const detailedMeeting = useAppSelector((state) =>
|
const detailedMeeting = useAppSelector((state) =>
|
||||||
selectMeeting(state, userStatus.meetingId)
|
selectMeeting(state, userStatus.inMeeting ? userStatus.meetingId : null)
|
||||||
);
|
);
|
||||||
|
const managerId = useAppSelector(selectManager);
|
||||||
const meetingStatus: MeetingStatus =
|
const meetingStatus: MeetingStatus =
|
||||||
userStatus && userStatus.inMeeting
|
managerId === contactInfo.uuid
|
||||||
? MeetingStatus.IN_MEETING
|
? MeetingStatus.NOT_AVAILABLE
|
||||||
: MeetingStatus.NOT_IN_MEETING;
|
: 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 (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -60,16 +82,14 @@ const UpperBody: React.FC<Props> = (props) => {
|
|||||||
mx: 4,
|
mx: 4,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography sx={{ pt: 2 }} variant="h3">{props.contactInfo.name}</Typography>
|
<Typography variant="h3">{contactInfo.name}</Typography>
|
||||||
{!favoritesUuids.includes(props.contactInfo.uuid) ? (
|
{!favoritesUuids.includes(contactInfo.uuid) ? (
|
||||||
<Button
|
<Button
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
color="success"
|
color="success"
|
||||||
startIcon={<AddIcon />}
|
startIcon={<AddIcon />}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
dispatch(
|
dispatch(addFavorite({ userId: me, toBeAdded: contactInfo.uuid }))
|
||||||
addFavorite({ userId: me, toBeAdded: props.contactInfo.uuid })
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
favorites
|
favorites
|
||||||
@ -83,7 +103,7 @@ const UpperBody: React.FC<Props> = (props) => {
|
|||||||
dispatch(
|
dispatch(
|
||||||
removeFavorite({
|
removeFavorite({
|
||||||
userId: me,
|
userId: me,
|
||||||
toBeRemoved: props.contactInfo.uuid,
|
toBeRemoved: contactInfo.uuid,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -107,15 +127,10 @@ const UpperBody: React.FC<Props> = (props) => {
|
|||||||
sx={{ border: 1, backgroundColor: "white", mr: 1 }}
|
sx={{ border: 1, backgroundColor: "white", mr: 1 }}
|
||||||
size="large"
|
size="large"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
|
onClick={startCall}
|
||||||
>
|
>
|
||||||
<PhoneIcon fontSize="large" />
|
<PhoneIcon fontSize="large" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton sx={{ border: 1, backgroundColor: "white", mr: 1 }} size="large" color="secondary">
|
|
||||||
<VideocamIcon fontSize="large" />
|
|
||||||
</IconButton>
|
|
||||||
<IconButton sx={{ border: 1, backgroundColor: "white", mr: 1 }} size="large" color="secondary">
|
|
||||||
<GroupsIcon fontSize="large" />
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ display: "flex", flexDirection: "column" }}>
|
<Box sx={{ display: "flex", flexDirection: "column" }}>
|
||||||
<Typography variant="button" sx={{ textAlign: "right" }}>{meetingStatus}</Typography>
|
<Typography variant="button" sx={{ textAlign: "right" }}>{meetingStatus}</Typography>
|
||||||
|
|||||||
@ -8,31 +8,35 @@ import { useAppSelector } from "../../../../redux/hooks";
|
|||||||
import { selectUserStatus } from "../../../../redux/slices/meetingsAndUserStatusSlice";
|
import { selectUserStatus } from "../../../../redux/slices/meetingsAndUserStatusSlice";
|
||||||
import UserStatus from "../../../../api-bodies/UserStatus";
|
import UserStatus from "../../../../api-bodies/UserStatus";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { selectManager } from "../../../../redux/slices/usersSlice";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
contactInfo: UserLite;
|
contactInfo: UserLite;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ContactItem: React.FC<Props> = (props) => {
|
const ContactItem: React.FC<Props> = ({ contactInfo }) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const userStatus: UserStatus | null = useAppSelector((state) =>
|
const userStatus: UserStatus | null = useAppSelector((state) =>
|
||||||
selectUserStatus(state, props.contactInfo.uuid)
|
selectUserStatus(state, contactInfo.uuid)
|
||||||
);
|
);
|
||||||
|
const managerId = useAppSelector(selectManager);
|
||||||
const status: MeetingStatus =
|
const status: MeetingStatus =
|
||||||
userStatus && userStatus.inMeeting
|
managerId === contactInfo.uuid
|
||||||
? MeetingStatus.IN_MEETING
|
? MeetingStatus.NOT_AVAILABLE
|
||||||
: MeetingStatus.NOT_IN_MEETING;
|
: userStatus && userStatus.inMeeting
|
||||||
|
? MeetingStatus.IN_MEETING
|
||||||
|
: MeetingStatus.NOT_IN_MEETING;
|
||||||
return (
|
return (
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate(`/contacts/${props.contactInfo.uuid}`);
|
navigate(`/contacts/${contactInfo.uuid}`);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<PersonOutlineIcon />
|
<PersonOutlineIcon />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={props.contactInfo.name}
|
primary={contactInfo.name}
|
||||||
secondary={status}
|
secondary={status}
|
||||||
sx={{ flexGrow: 1 }}
|
sx={{ flexGrow: 1 }}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -13,15 +13,13 @@ const MeetingsPanel: React.FC = () => {
|
|||||||
const uuids: string[] = [];
|
const uuids: string[] = [];
|
||||||
meetings.forEach((meeting) => {
|
meetings.forEach((meeting) => {
|
||||||
meeting.liveParticipantIds.forEach((uuid) => {
|
meeting.liveParticipantIds.forEach((uuid) => {
|
||||||
if(!uuids.includes(uuid)) {
|
if (!uuids.includes(uuid)) {
|
||||||
uuids.push(uuid);
|
uuids.push(uuid);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const participants = useAppSelector((state) =>
|
const participants = useAppSelector((state) => selectUsers(state, uuids));
|
||||||
selectUsers(state,uuids)
|
|
||||||
);
|
|
||||||
|
|
||||||
// const participants: (UserLite | undefined)[] = [];
|
// const participants: (UserLite | undefined)[] = [];
|
||||||
// uuids.forEach((uuid) => {
|
// uuids.forEach((uuid) => {
|
||||||
@ -47,17 +45,20 @@ const MeetingsPanel: React.FC = () => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<List style={{maxHeight: "100%", overflow: "auto"}} >
|
<List style={{ maxHeight: "100%", overflow: "auto" }}>
|
||||||
{meetings.map((meeting) => {
|
{meetings.map((meeting) => {
|
||||||
const meetingMembers: UserLite[] = [];
|
const meetingMembers: UserLite[] = [];
|
||||||
participants.forEach((userLite) => {
|
participants.forEach((userLite) => {
|
||||||
if (userLite != undefined && meeting.liveParticipantIds.includes(userLite.uuid)) {
|
if (
|
||||||
|
userLite != undefined &&
|
||||||
|
meeting.liveParticipantIds.includes(userLite.uuid)
|
||||||
|
) {
|
||||||
meetingMembers.push(userLite);
|
meetingMembers.push(userLite);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const startDate = new Date(meeting.start);
|
const startDate = new Date(meeting.startTime);
|
||||||
const startDatemil = startDate.getTime();
|
const startDatemil = startDate.getTime();
|
||||||
const endDatemil = startDatemil + meeting.duration*60000;
|
const endDatemil = startDatemil + meeting.duration * 60000;
|
||||||
const endDate = new Date(endDatemil);
|
const endDate = new Date(endDatemil);
|
||||||
|
|
||||||
const currentDatemil = currentDate.getTime();
|
const currentDatemil = currentDate.getTime();
|
||||||
@ -82,8 +83,14 @@ const MeetingsPanel: React.FC = () => {
|
|||||||
meeting={meeting}
|
meeting={meeting}
|
||||||
meetingClass={"meeting-" + (i - 1) + lastMeetingClass}
|
meetingClass={"meeting-" + (i - 1) + lastMeetingClass}
|
||||||
meetingName={meeting.topic}
|
meetingName={meeting.topic}
|
||||||
meetingTime={formatTimeFromDate(startDate) + " - " + formatTimeFromDate(endDate)}
|
meetingTime={
|
||||||
meetingMembers={meetingMembersString()}
|
formatTimeFromDate(startDate) +
|
||||||
|
" - " +
|
||||||
|
formatTimeFromDate(endDate)
|
||||||
|
}
|
||||||
|
meetingMembers={meetingMembers
|
||||||
|
.map((userLite) => " " + userLite.name + " ")
|
||||||
|
.toString()}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
import PeopleIcon from "@mui/icons-material/People";
|
import PeopleIcon from "@mui/icons-material/People";
|
||||||
// import AddIcon from "@mui/icons-material/Add";
|
|
||||||
import PhoneEnabledIcon from "@mui/icons-material/PhoneEnabled";
|
import PhoneEnabledIcon from "@mui/icons-material/PhoneEnabled";
|
||||||
// import CircleIcon from "@mui/icons-material/Circle";
|
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
import Grid from "@mui/material/Grid";
|
import Grid from "@mui/material/Grid";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
|
|||||||
@ -9,6 +9,13 @@ import zoomLogo from "../../assets/zoom.png";
|
|||||||
import LoginIcon from "@mui/icons-material/Login";
|
import LoginIcon from "@mui/icons-material/Login";
|
||||||
import useAuth from "../../hooks/useAuth";
|
import useAuth from "../../hooks/useAuth";
|
||||||
import axios from "../../api/axios";
|
import axios from "../../api/axios";
|
||||||
|
import { store } from "../../redux/store";
|
||||||
|
import { fetchFavorites } from "../../redux/slices/favoritesSlice";
|
||||||
|
import {
|
||||||
|
fetchMeetings,
|
||||||
|
socketActions,
|
||||||
|
} from "../../redux/slices/meetingsAndUserStatusSlice";
|
||||||
|
import { fetchUsers } from "../../redux/slices/usersSlice";
|
||||||
|
|
||||||
interface LocationState {
|
interface LocationState {
|
||||||
from: { pathname: string };
|
from: { pathname: string };
|
||||||
@ -36,13 +43,17 @@ const Login: React.FC = () => {
|
|||||||
// setErrMsg('');
|
// setErrMsg('');
|
||||||
// }, [user, pwd])
|
// }, [user, pwd])
|
||||||
|
|
||||||
const handleLogin = async(e: React.SyntheticEvent) => {
|
const handleLogin = async (e: React.SyntheticEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (email === "" && password === "") {
|
if (email === "" && password === "") {
|
||||||
setAuth["uuid"] = "";
|
setAuth["uuid"] = "";
|
||||||
setAuth["isLoggedIn"] = true;
|
setAuth["isLoggedIn"] = true;
|
||||||
|
store.dispatch(fetchMeetings(""));
|
||||||
|
store.dispatch(fetchUsers(""));
|
||||||
|
store.dispatch(fetchFavorites(""));
|
||||||
|
store.dispatch(socketActions.startConnecting());
|
||||||
navigate(from, { replace: true });
|
navigate(from, { replace: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,6 +73,10 @@ const Login: React.FC = () => {
|
|||||||
if (logedInUserId != undefined) {
|
if (logedInUserId != undefined) {
|
||||||
setAuth["uuid"] = logedInUserId;
|
setAuth["uuid"] = logedInUserId;
|
||||||
setAuth["isLoggedIn"] = true;
|
setAuth["isLoggedIn"] = true;
|
||||||
|
store.dispatch(fetchMeetings(logedInUserId));
|
||||||
|
store.dispatch(fetchUsers(logedInUserId));
|
||||||
|
store.dispatch(fetchFavorites(logedInUserId));
|
||||||
|
store.dispatch(socketActions.startConnecting());
|
||||||
navigate(from, { replace: true });
|
navigate(from, { replace: true });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -1,16 +1,7 @@
|
|||||||
import DetailedMeeting from "../../api-bodies/DetailedMeeting";
|
import DetailedMeeting from "../../api-bodies/DetailedMeeting";
|
||||||
|
|
||||||
const getMeetingStatus = (meeting: DetailedMeeting) => {
|
|
||||||
const startDate = new Date(meeting.start);
|
|
||||||
const endDate = new Date(startDate.getTime() + meeting.duration * 60000);
|
|
||||||
const currentDate = new Date();
|
|
||||||
if (currentDate > startDate && currentDate < endDate) return "Live";
|
|
||||||
else if (currentDate < startDate) return "Scheduled";
|
|
||||||
else return "Finished";
|
|
||||||
};
|
|
||||||
|
|
||||||
const getUpcomingMeetingTime = (meeting: 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 endDate = new Date(startDate.getTime() + meeting.duration * 60000);
|
||||||
const startTime = startDate
|
const startTime = startDate
|
||||||
.toTimeString()
|
.toTimeString()
|
||||||
@ -27,4 +18,22 @@ const getUpcomingMeetingTime = (meeting: DetailedMeeting) => {
|
|||||||
return `${startTime} - ${endTime}`;
|
return `${startTime} - ${endTime}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export { getMeetingStatus, getUpcomingMeetingTime };
|
const getMeetingDuration = (meeting: DetailedMeeting) => {
|
||||||
|
const startDate = new Date(meeting.startTime);
|
||||||
|
const endDate = new Date(startDate.getTime() + meeting.duration * 60000);
|
||||||
|
const startDateString = startDate.toDateString();
|
||||||
|
const startTimeString = startDate
|
||||||
|
.toTimeString()
|
||||||
|
.split(" ")[0]
|
||||||
|
.split(":")
|
||||||
|
.splice(0, 2)
|
||||||
|
.join(":");
|
||||||
|
const endTimeString = endDate
|
||||||
|
.toTimeString()
|
||||||
|
.split(" ")[0]
|
||||||
|
.split(":")
|
||||||
|
.slice(0, 2)
|
||||||
|
.join(":");
|
||||||
|
return `${startDateString} ${startTimeString} - ${endTimeString}`;
|
||||||
|
};
|
||||||
|
export { getUpcomingMeetingTime, getMeetingDuration };
|
||||||
|
|||||||
@ -13,14 +13,36 @@ import DetailedMeeting from "../../../api-bodies/DetailedMeeting";
|
|||||||
import { useAppSelector } from "../../../redux/hooks";
|
import { useAppSelector } from "../../../redux/hooks";
|
||||||
import { selectUsers } from "../../../redux/slices/usersSlice";
|
import { selectUsers } from "../../../redux/slices/usersSlice";
|
||||||
import UserLite from "../../../api-bodies/UserLite";
|
import UserLite from "../../../api-bodies/UserLite";
|
||||||
|
import { getMeetingDuration } from "../Utils";
|
||||||
|
|
||||||
|
enum MeetingStatus {
|
||||||
|
Live = "Live",
|
||||||
|
Scheduled = "Scheduled",
|
||||||
|
Finished = "Finished",
|
||||||
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
meeting: DetailedMeeting;
|
meeting: DetailedMeeting;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Body: React.FC<Props> = (props) => {
|
const getMeetingStatus = (meeting: DetailedMeeting) => {
|
||||||
|
const startDate = new Date(meeting.startTime);
|
||||||
|
const endDate = new Date(startDate.getTime() + meeting.duration * 60000);
|
||||||
|
const currentDate = new Date();
|
||||||
|
if (meeting.liveParticipantIds.length === 0) {
|
||||||
|
if (startDate.getTime() - currentDate.getTime() > 0)
|
||||||
|
return MeetingStatus.Scheduled;
|
||||||
|
else if (endDate.getTime() - currentDate.getTime() > 0)
|
||||||
|
return MeetingStatus.Live;
|
||||||
|
else return MeetingStatus.Finished;
|
||||||
|
} else {
|
||||||
|
return MeetingStatus.Live;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Body: React.FC<Props> = ({ meeting }) => {
|
||||||
const registrants: UserLite[] = useAppSelector((state) =>
|
const registrants: UserLite[] = useAppSelector((state) =>
|
||||||
selectUsers(state, props.meeting.registrantIds)
|
selectUsers(state, meeting.registrantIds)
|
||||||
);
|
);
|
||||||
const liveParticipants: UserLite[] = useAppSelector((state) =>
|
const liveParticipants: UserLite[] = useAppSelector((state) =>
|
||||||
selectUsers(state, props.meeting.liveParticipantIds)
|
selectUsers(state, props.meeting.liveParticipantIds)
|
||||||
@ -44,6 +66,7 @@ const Body: React.FC<Props> = (props) => {
|
|||||||
return s.join("").slice(0, -2);
|
return s.join("").slice(0, -2);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const meetingStatus = getMeetingStatus(meeting);
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -63,11 +86,11 @@ const Body: React.FC<Props> = (props) => {
|
|||||||
mt: 10
|
mt: 10
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography sx={{ pl: 5 }} variant="overline">Meeting ID: {props.meeting.meetingId}</Typography>
|
<Typography sx={{ pl: 5 }} variant="overline">Meeting ID: {meeting.meetingId}</Typography>
|
||||||
<Typography sx={{ pl: 5, color: "#af000d" }} variant="h2">{props.meeting.topic}</Typography>
|
<Typography sx={{ pl: 5, color: "#af000d" }} variant="h2">{meeting.topic}</Typography>
|
||||||
<Divider sx={{ mb: 3 }} />
|
<Divider sx={{ mb: 3 }} />
|
||||||
<Typography sx={{ pl: 5 }} variant="h4">Start: {startDateString}</Typography>
|
<Typography sx={{ pl: 5 }} variant="h4">Start: {startDateString}</Typography>
|
||||||
<Typography sx={{ pl: 5 }} variant="h4">Duration: {props.meeting.duration.toString() + " minutes"}</Typography>
|
<Typography sx={{ pl: 5 }} variant="h4">Duration: {meeting.duration.toString() + " minutes"}</Typography>
|
||||||
<Typography sx= {{ my: 1 }} />
|
<Typography sx= {{ my: 1 }} />
|
||||||
<Typography sx={{ pl: 5 }} variant="button">Currently inside: {listLiveParticipants()}</Typography>
|
<Typography sx={{ pl: 5 }} variant="button">Currently inside: {listLiveParticipants()}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -6,24 +6,27 @@ import { close } from "../../../redux/slices/meetingDetailsOpenSlice";
|
|||||||
import DetailedMeeting from "../../../api-bodies/DetailedMeeting";
|
import DetailedMeeting from "../../../api-bodies/DetailedMeeting";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
meeting: DetailedMeeting | null;
|
meeting: DetailedMeeting;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Header: React.FC<Props> = (props) => {
|
const Header: React.FC<Props> = ({ meeting }) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppBar sx={{ position: "relative" }}>
|
<AppBar sx={{ position: "relative" }}>
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<Typography variant="h6" sx={{ flexGrow: 1 }}>
|
<Typography variant="h6" sx={{ flexGrow: 1 }}>
|
||||||
{props.meeting !== null ? props.meeting.topic : null}
|
{meeting !== null ? meeting.topic : null}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Button variant="contained" color="secondary">
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="secondary"
|
||||||
|
onClick={() => {
|
||||||
|
window.open(meeting.joinUrl, "_blank")?.focus();
|
||||||
|
}}
|
||||||
|
>
|
||||||
Join
|
Join
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="contained" color="secondary" sx={{ ml: 1 }}>
|
|
||||||
Recording
|
|
||||||
</Button>
|
|
||||||
<IconButton
|
<IconButton
|
||||||
edge="start"
|
edge="start"
|
||||||
color="inherit"
|
color="inherit"
|
||||||
|
|||||||
@ -13,6 +13,8 @@ import { open as openMeetingDetails } from "../../redux/slices/meetingDetailsOpe
|
|||||||
import { selectMeeting } from "../../redux/slices/meetingsAndUserStatusSlice";
|
import { selectMeeting } from "../../redux/slices/meetingsAndUserStatusSlice";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { selectMe } from "../../redux/slices/usersSlice";
|
import { selectMe } from "../../redux/slices/usersSlice";
|
||||||
|
import NewMeeting from "../../api-bodies/NewMeeting";
|
||||||
|
import axios from "../../api/axios";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
user: UserLite;
|
user: UserLite;
|
||||||
@ -43,6 +45,24 @@ const SettingsButton: React.FC<Props> = ({ user, status }: Props) => {
|
|||||||
setSnackbarOpen(false);
|
setSnackbarOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<IconButton
|
<IconButton
|
||||||
@ -100,7 +120,14 @@ const SettingsButton: React.FC<Props> = ({ user, status }: Props) => {
|
|||||||
>
|
>
|
||||||
View upcoming meetings
|
View upcoming meetings
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem onClick={handleClose}>Create meeting</MenuItem>
|
<MenuItem
|
||||||
|
onClick={() => {
|
||||||
|
handleClose();
|
||||||
|
startCall();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Create meeting
|
||||||
|
</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
<Snackbar
|
<Snackbar
|
||||||
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
|
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
|
||||||
|
|||||||
@ -1,10 +1,8 @@
|
|||||||
import { Middleware } from "redux";
|
import { Middleware } from "redux";
|
||||||
import { io, Socket } from "socket.io-client";
|
|
||||||
import DetailedMeeting from "../../api-bodies/DetailedMeeting";
|
|
||||||
import { socketActions } from "../slices/meetingsAndUserStatusSlice";
|
import { socketActions } from "../slices/meetingsAndUserStatusSlice";
|
||||||
|
|
||||||
const socketMiddleware: Middleware = (store) => {
|
const socketMiddleware: Middleware = (store) => {
|
||||||
let socket: Socket;
|
let socket: WebSocket;
|
||||||
|
|
||||||
return (next) => (action) => {
|
return (next) => (action) => {
|
||||||
const isConnectionEstablished =
|
const isConnectionEstablished =
|
||||||
@ -14,28 +12,23 @@ const socketMiddleware: Middleware = (store) => {
|
|||||||
socketActions.startConnecting.match(action) &&
|
socketActions.startConnecting.match(action) &&
|
||||||
!isConnectionEstablished
|
!isConnectionEstablished
|
||||||
) {
|
) {
|
||||||
console.log("startConnecting called");
|
console.log("start connecting");
|
||||||
socket = io("wss://uo5wdcbn6l.execute-api.us-west-2.amazonaws.com/Prod/");
|
socket = new WebSocket(
|
||||||
|
"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));
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
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);
|
next(action);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -31,10 +31,10 @@ export const meetingsAndUserStatusSlice = createSlice({
|
|||||||
state.isConnected = true;
|
state.isConnected = true;
|
||||||
},
|
},
|
||||||
meetingCreated: (state, action) => {
|
meetingCreated: (state, action) => {
|
||||||
state.meetings.push(action.payload.meeting);
|
state.meetings.push(action.payload);
|
||||||
},
|
},
|
||||||
userStatusChanged: (state, action) => {
|
userStatusChanged: (state, action) => {
|
||||||
state.userStatuses[action.payload.uuid] = action.payload;
|
state.userStatuses[action.payload.userId] = action.payload;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
extraReducers(builder) {
|
extraReducers(builder) {
|
||||||
@ -90,9 +90,25 @@ export const selectMeeting = (state: RootState, meetingID: string | null) => {
|
|||||||
: null;
|
: null;
|
||||||
};
|
};
|
||||||
export const selectUserUpcomingMeetings = (state: RootState, uuid: string) => {
|
export const selectUserUpcomingMeetings = (state: RootState, uuid: string) => {
|
||||||
return state.meetingsAndUserStatuses.meetings.filter((meeting) =>
|
const isToday = (date: Date) => {
|
||||||
meeting.registrantIds.includes(uuid)
|
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 = (
|
export const selectUserStatus = (
|
||||||
state: RootState,
|
state: RootState,
|
||||||
|
|||||||
@ -47,14 +47,14 @@ export const fetchUsers = createAsyncThunk(
|
|||||||
};
|
};
|
||||||
// fetch userfull
|
// fetch userfull
|
||||||
const userResp = await axios.get(`/users/${uuid}`);
|
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.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.user = user.userInfo.uuid;
|
||||||
team.manager = user.manager.uuid;
|
if (user.manager) team.manager = user.manager.uuid;
|
||||||
|
|
||||||
user.userDirectReports.forEach((userLite) => {
|
user.userDirectReports.forEach((userLite) => {
|
||||||
users[userLite.uuid] = userLite;
|
users[userLite.uuid] = userLite;
|
||||||
team.directReports.push(userLite.uuid);
|
team.directReports.push(userLite.uuid);
|
||||||
@ -76,6 +76,7 @@ export const fetchUsers = createAsyncThunk(
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const selectMe = (state: RootState) => state.users.team.user;
|
export const selectMe = (state: RootState) => state.users.team.user;
|
||||||
|
export const selectManager = (state: RootState) => state.users.team.manager;
|
||||||
export const selectUser = (
|
export const selectUser = (
|
||||||
state: RootState,
|
state: RootState,
|
||||||
uuid: string | undefined
|
uuid: string | undefined
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
const enum MeetingStatus {
|
const enum MeetingStatus {
|
||||||
NOT_IN_MEETING = "Not in meeting",
|
NOT_IN_MEETING = "Not in meeting",
|
||||||
IN_MEETING = "In meeting",
|
IN_MEETING = "In meeting",
|
||||||
|
NOT_AVAILABLE = "Not available",
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStatusColor = (ms: MeetingStatus): string => {
|
const getStatusColor = (ms: MeetingStatus): string => {
|
||||||
@ -11,6 +12,9 @@ const getStatusColor = (ms: MeetingStatus): string => {
|
|||||||
case MeetingStatus.IN_MEETING: {
|
case MeetingStatus.IN_MEETING: {
|
||||||
return "#ff7070";
|
return "#ff7070";
|
||||||
}
|
}
|
||||||
|
case MeetingStatus.NOT_AVAILABLE: {
|
||||||
|
return "#808080";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user