sidebar changes

This commit is contained in:
Taehee Choi 2022-03-20 23:56:18 -07:00
parent 16587686d4
commit 19906db3da
19 changed files with 283 additions and 224 deletions

View File

@ -22,7 +22,8 @@ const App: React.FC = () => {
<Route path="/login" element={<Login />} />
<Route element={<ProtectedRoute />}>
<Route path="/" element={<Home />} />
<Route path="/contacts" element={<Contacts />} />
<Route path="/contacts/" element={<Contacts />} />
<Route path="/contacts/:uuid" element={<Contacts />} />
<Route path="/calendar" element={<CalendarPage />} />
</Route>
</Routes>

View File

@ -1,42 +1,14 @@
import {useLocation, Outlet, Navigate} from "react-router-dom";
import { useLocation, Outlet, Navigate } from "react-router-dom";
import useAuth from "./hooks/useAuth";
import Navbar from "./components/navbar/Navbar";
import Sidebar from "./components/sidebar/Sidebar";
import React, { useState } from "react";
import {
MeetingStatus,
SidebarUserObj,
} from "./components/sidebar/SidebarUser";
import { Box } from "@mui/material";
const ProtectedRoute = () => {
const auth= useAuth();
const auth = useAuth();
const location = useLocation();
/* Temporary data */
const [sidebarUsers] = useState<SidebarUserObj[]>([
{ id: 0, name: "Jincheng L.", meetingStatus: MeetingStatus.ONLINE },
{ id: 1, name: "Matt B.", meetingStatus: MeetingStatus.IN_MEETING },
{ id: 2, name: "Taehee C.", meetingStatus: MeetingStatus.OFFLINE },
{ id: 3, name: "Bob A.", meetingStatus: MeetingStatus.AWAY },
{ id: 4, name: "Bob B.", meetingStatus: MeetingStatus.IN_MEETING },
{ id: 5, name: "Bob C.", meetingStatus: MeetingStatus.OFFLINE },
{ id: 6, name: "Bob D.", meetingStatus: MeetingStatus.ONLINE },
{ id: 7, name: "Bob E.", meetingStatus: MeetingStatus.AWAY },
{ id: 8, name: "Bob F.", meetingStatus: MeetingStatus.OFFLINE },
{ id: 9, name: "Bob G.", meetingStatus: MeetingStatus.ONLINE },
{ id: 10, name: "Bob H.", meetingStatus: MeetingStatus.AWAY },
{ id: 11, name: "Bob I.", meetingStatus: MeetingStatus.OFFLINE },
{ id: 12, name: "Bob J.", meetingStatus: MeetingStatus.AWAY },
{ id: 13, name: "Bob K.", meetingStatus: MeetingStatus.IN_MEETING },
{ id: 14, name: "Bob L.", meetingStatus: MeetingStatus.IN_MEETING },
{ id: 15, name: "Bob M.", meetingStatus: MeetingStatus.OFFLINE },
{ id: 16, name: "Bob N.", meetingStatus: MeetingStatus.OFFLINE },
{ id: 17, name: "Bob O.", meetingStatus: MeetingStatus.AWAY },
{ id: 18, name: "Bob P.", meetingStatus: MeetingStatus.AWAY },
{ id: 19, name: "Bob Q.", meetingStatus: MeetingStatus.ONLINE },
{ id: 20, name: "Bob R.", meetingStatus: MeetingStatus.ONLINE },
]);
return auth?.isLoggedIn ? (
<>
<Navbar />
@ -45,7 +17,7 @@ const ProtectedRoute = () => {
<Outlet />
</Box>
<Box>
<Sidebar users={sidebarUsers} />
<Sidebar />
</Box>
</Box>
</>

View File

@ -4,18 +4,20 @@ import Body from "./contacts-components/Body";
import Sidebar from "./contacts-components/Sidebar";
import UserLite from "../../api-bodies/UserLite";
import { useParams } from "react-router-dom";
import { selectUser } from "../../redux/slices/usersSlice";
import { useAppSelector } from "../../redux/hooks";
const Contacts: React.FC = () => {
const [contactSelected, setContactSelected] = React.useState<UserLite | null>(
null
const { uuid } = useParams();
const uriContact: UserLite | undefined = useAppSelector((state) =>
selectUser(state, uuid)
);
return (
<Box sx={{ display: "flex", height: "100%" }}>
<Sidebar setContactSelected={setContactSelected} />
{contactSelected !== null ? (
<Body contactSelected={contactSelected} />
) : null}
<Sidebar />
{uriContact ? <Body contactSelected={uriContact} /> : null}
</Box>
);
};

View File

@ -1,8 +0,0 @@
enum Status {
Online = "Online",
Offline = "Offline",
Away = "Away",
InMeeting = "In Meeting",
}
export default Status;

View File

@ -1,20 +1,4 @@
import DetailedMeeting from "../../api-bodies/DetailedMeeting";
import Status from "./Status";
const returnStatusColor = (
status: Status
): "success" | "disabled" | "warning" | "error" => {
switch (status) {
case Status.Online:
return "success";
case Status.Offline:
return "disabled";
case Status.Away:
return "warning";
case Status.InMeeting:
return "error";
}
};
const getUpcomingMeetingTime = (meeting: DetailedMeeting) => {
const startDate = new Date(meeting.start);
@ -34,4 +18,4 @@ const getUpcomingMeetingTime = (meeting: DetailedMeeting) => {
return `${startTime} - ${endTime}`;
};
export { returnStatusColor, getUpcomingMeetingTime };
export { getUpcomingMeetingTime };

View File

@ -13,15 +13,10 @@ import ContactItem from "./sidebar-components/ContactItem";
import { useAppSelector } from "../../../redux/hooks";
import { selectUsers, selectTeam } from "../../../redux/slices/usersSlice";
import { selectFavorites } from "../../../redux/slices/favoritesSlice";
import UserLite from "../../../api-bodies/UserLite";
interface Props {
setContactSelected: (contactInfo: UserLite) => void;
}
const sidebarWidth = 240;
const Sidebar: React.FC<Props> = (props) => {
const Sidebar: React.FC = () => {
const [favoritesOpen, setFavoritesOpen] = React.useState<boolean>(true);
const [teamOpen, setTeamOpen] = React.useState<boolean>(false);
@ -29,7 +24,8 @@ const Sidebar: React.FC<Props> = (props) => {
const favorites = useAppSelector((state) =>
selectUsers(state, favoritesUuids)
);
const team = useAppSelector(selectTeam);
const teamUuids = useAppSelector(selectTeam);
const team = useAppSelector((state) => selectUsers(state, teamUuids));
return (
<Drawer
@ -58,11 +54,7 @@ const Sidebar: React.FC<Props> = (props) => {
<Collapse in={favoritesOpen} timeout="auto" unmountOnExit>
<List disablePadding>
{favorites.map((favorite, i) => (
<ContactItem
contactInfo={favorite}
key={i}
setContactSelected={props.setContactSelected}
/>
<ContactItem contactInfo={favorite} key={i} />
))}
</List>
</Collapse>
@ -74,11 +66,7 @@ const Sidebar: React.FC<Props> = (props) => {
<Collapse in={teamOpen} timeout="auto" unmountOnExit>
<List disablePadding>
{team.map((member, i) => (
<ContactItem
contactInfo={member}
key={i}
setContactSelected={props.setContactSelected}
/>
<ContactItem contactInfo={member} key={i} />
))}
</List>
</Collapse>

View File

@ -4,7 +4,6 @@ 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 Status from "../../Status";
import UserLite from "../../../../api-bodies/UserLite";
import UserStatus from "../../../../api-bodies/UserStatus";
import { useAppSelector, useAppDispatch } from "../../../../redux/hooks";
@ -18,6 +17,7 @@ import {
removeFavorite,
} from "../../../../redux/slices/favoritesSlice";
import RemoveIcon from "@mui/icons-material/Remove";
import { MeetingStatus } from "../../../../utils";
interface Props {
contactInfo: UserLite;
@ -25,15 +25,17 @@ interface Props {
const UpperBody: React.FC<Props> = (props) => {
const dispatch = useAppDispatch();
const userStatus: UserStatus | null = useAppSelector((state) =>
const userStatus: UserStatus = useAppSelector((state) =>
selectUserStatus(state, props.contactInfo.uuid)
);
const favoritesUuids = useAppSelector(selectFavorites);
const detailedMeeting = useAppSelector((state) =>
selectMeeting(state, userStatus.meetingID)
);
const status: Status =
userStatus && userStatus.inMeeting ? Status.Online : Status.Offline;
const meetingStatus: MeetingStatus =
userStatus && userStatus.inMeeting
? MeetingStatus.IN_MEETING
: MeetingStatus.NOT_IN_MEETING;
return (
<Box
sx={{
@ -102,7 +104,7 @@ const UpperBody: React.FC<Props> = (props) => {
</IconButton>
</Box>
<Box sx={{ display: "flex", flexDirection: "column" }}>
<Typography sx={{ textAlign: "right" }}>{status}</Typography>
<Typography sx={{ textAlign: "right" }}>{meetingStatus}</Typography>
{detailedMeeting ? (
<Typography>{detailedMeeting.topic}</Typography>
) : null}

View File

@ -2,28 +2,30 @@ import { ListItemButton, ListItemIcon, ListItemText } from "@mui/material";
import React from "react";
import PersonOutlineIcon from "@mui/icons-material/PersonOutline";
import CircleIcon from "@mui/icons-material/Circle";
import { returnStatusColor } from "../../Utils";
import UserLite from "../../../../api-bodies/UserLite";
import Status from "../../Status";
import { MeetingStatus, getStatusColor } from "../../../../utils";
import { useAppSelector } from "../../../../redux/hooks";
import { selectUserStatus } from "../../../../redux/slices/meetingsAndUserStatusSlice";
import UserStatus from "../../../../api-bodies/UserStatus";
import { useNavigate } from "react-router-dom";
interface Props {
contactInfo: UserLite;
setContactSelected: (contactInfo: UserLite) => void;
}
const ContactItem: React.FC<Props> = (props) => {
const navigate = useNavigate();
const userStatus: UserStatus | null = useAppSelector((state) =>
selectUserStatus(state, props.contactInfo.uuid)
);
const status: Status =
userStatus && userStatus.inMeeting ? Status.Online : Status.Offline;
const status: MeetingStatus =
userStatus && userStatus.inMeeting
? MeetingStatus.IN_MEETING
: MeetingStatus.NOT_IN_MEETING;
return (
<ListItemButton
onClick={() => {
props.setContactSelected(props.contactInfo);
navigate(`/contacts/${props.contactInfo.uuid}`);
}}
>
<ListItemIcon>
@ -34,7 +36,7 @@ const ContactItem: React.FC<Props> = (props) => {
secondary={status}
sx={{ flexGrow: 1 }}
/>
<CircleIcon color={returnStatusColor(status)} />
<CircleIcon sx={{ color: getStatusColor(status) }} />
</ListItemButton>
);
};

View File

@ -1,44 +1,63 @@
import { Button, Checkbox, Dialog, DialogActions, DialogContent, DialogTitle, FormControlLabel, FormGroup, Typography } from "@mui/material";
import { SidebarUserObj } from "../sidebar/SidebarUser";
import {
Button,
Checkbox,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
FormControlLabel,
FormGroup,
Typography,
} from "@mui/material";
import { SidebarUserObj } from "./Home";
interface Props {
open: boolean;
selectedValue: SidebarUserObj;
onClose: (value: SidebarUserObj) => void;
users: SidebarUserObj[];
open: boolean;
selectedValue: SidebarUserObj;
onClose: (value: SidebarUserObj) => void;
users: SidebarUserObj[];
}
const CallFavouritesDialog: React.FC<Props> = ({ open, selectedValue, onClose, users }: Props) => {
const CallFavouritesDialog: React.FC<Props> = ({
open,
selectedValue,
onClose,
users,
}: Props) => {
const handleClose = () => {
onClose(selectedValue);
};
return (
<Dialog
onClose={handleClose}
open={open}
fullWidth
maxWidth="sm"
>
<Dialog onClose={handleClose} open={open} fullWidth maxWidth="sm">
<DialogTitle>Select who to call:</DialogTitle>
<DialogContent>
<FormControlLabel control={<Checkbox color="success" />} label="Select everyone" />
<FormControlLabel
control={<Checkbox color="success" />}
label="Select everyone"
/>
</DialogContent>
<DialogContent sx={{ height: "40vh" }} dividers>
<FormGroup>
{users.map((user) => (
<FormControlLabel key={user.id} control={<Checkbox color="success" />} label={user.name} />
<FormControlLabel
key={user.id}
control={<Checkbox color="success" />}
label={user.name}
/>
))}
</FormGroup>
</DialogContent>
<DialogActions>
<Button color="success" onClick={handleClose}>
<Typography variant="button" color="black">Call</Typography>
<Typography variant="button" color="black">
Call
</Typography>
</Button>
</DialogActions>
</Dialog>
);
};
export default CallFavouritesDialog;
export default CallFavouritesDialog;

View File

@ -2,22 +2,26 @@ import MeetingsPanel from "./MeetingsPanel";
import ShortCuts from "./ShortCuts";
import Container from "@mui/material/Container";
import Grid from "@mui/material/Grid";
import { MeetingStatus, SidebarUserObj } from "../sidebar/SidebarUser";
import { MeetingStatus } from "../../utils";
import { useState } from "react";
const Home: React.FC = () => {
export interface SidebarUserObj {
id: number;
name: string;
meetingStatus: MeetingStatus;
}
const Home: React.FC = () => {
/* Temporary data - this is the same as what's in ProtectedRoute.tsx so it should not
be duplicated like this in the future (Route components were being weird when I tried
to pass it down from App.tsx) */
const [mockUsers] = useState<SidebarUserObj[]>([
{ id: 0, name: "Jincheng L.", meetingStatus: MeetingStatus.ONLINE },
{ id: 0, name: "Jincheng L.", meetingStatus: MeetingStatus.NOT_IN_MEETING },
{ id: 1, name: "Matt B.", meetingStatus: MeetingStatus.IN_MEETING },
{ id: 2, name: "Taehee C.", meetingStatus: MeetingStatus.OFFLINE },
{ id: 3, name: "Bob A.", meetingStatus: MeetingStatus.AWAY }
{ id: 2, name: "Taehee C.", meetingStatus: MeetingStatus.IN_MEETING },
{ id: 3, name: "Bob A.", meetingStatus: MeetingStatus.NOT_IN_MEETING },
]);
return (
<Container className="main-home">
<Grid container>
@ -25,7 +29,7 @@ const Home: React.FC = () => {
<MeetingsPanel />
</Grid>
<Grid item sm={4}>
<ShortCuts users={mockUsers}/>
<ShortCuts users={mockUsers} />
</Grid>
</Grid>
</Container>

View File

@ -5,7 +5,7 @@ import CircleIcon from "@mui/icons-material/Circle";
import Button from "@mui/material/Button";
import Grid from "@mui/material/Grid";
import Typography from "@mui/material/Typography";
import { SidebarUserObj } from "../sidebar/SidebarUser";
import { SidebarUserObj } from "./Home";
import CallFavouritesDialog from "./CallFavouritesDialog";
import { useState } from "react";
@ -27,19 +27,23 @@ const ShortCuts: React.FC<Props> = ({ users }: Props) => {
};
return (
<div className="short-cuts" >
<div className="short-cuts">
<Grid className="row-1" container spacing={1}>
<Grid item sm={6}>
<Button className="tile">
<PeopleIcon className="icon" />
</Button>
<Typography className="mylabel" sx={{ ml: 1 }}>New Meeting</Typography>
<Typography className="mylabel" sx={{ ml: 1 }}>
New Meeting
</Typography>
</Grid>
<Grid item sm={6}>
<Button className="tile">
<AddIcon className="icon" />
</Button>
<Typography className="mylabel" sx={{ ml: 1 }}>Join</Typography>
<Typography className="mylabel" sx={{ ml: 1 }}>
Join
</Typography>
</Grid>
</Grid>
<Grid className="row-2" container spacing={1}>
@ -47,22 +51,27 @@ const ShortCuts: React.FC<Props> = ({ users }: Props) => {
<Button className="tile" onClick={handleClickOpen}>
<PhoneEnabledIcon className="icon" />
</Button>
<CallFavouritesDialog
<CallFavouritesDialog
selectedValue={selectedValue}
open={open}
onClose={handleClose}
users={users}/>
<Typography className="mylabel" sx={{ ml: 1 }}>Call Favourites</Typography>
users={users}
/>
<Typography className="mylabel" sx={{ ml: 1 }}>
Call Favourites
</Typography>
</Grid>
<Grid item sm={6}>
<Button className="tile">
<CircleIcon className="icon" />
</Button>
<Typography className="mylabel" sx={{ ml: 1 }}>Recordings</Typography>
<Typography className="mylabel" sx={{ ml: 1 }}>
Recordings
</Typography>
</Grid>
</Grid>
</div>
);
};
export default ShortCuts;
export default ShortCuts;

View File

@ -1,14 +1,25 @@
import { FormControl, InputLabel, MenuItem, Select, SelectChangeEvent, Typography } from "@mui/material";
import { useState } from "react";
import {
FormControl,
InputLabel,
MenuItem,
Select,
SelectChangeEvent,
Typography,
} from "@mui/material";
import React from "react";
// TODO: change MenuItem values to something meaningful
const GroupSelect: React.FC = () => {
interface Props {
group: string;
setGroup: (group: string) => void;
}
const [group, setGroup] = useState("");
const groups = ["Favorites", "Team"];
const handleChange = (event: SelectChangeEvent) => {
setGroup(event.target.value);
const GroupSelect: React.FC<Props> = ({ group, setGroup }) => {
const handleChange = (e: SelectChangeEvent) => {
setGroup(e.target.value);
};
return (
@ -23,18 +34,14 @@ const GroupSelect: React.FC = () => {
label="Group"
onChange={handleChange}
>
<MenuItem value={0}>
<Typography color="black">Favourites</Typography>
</MenuItem>
<MenuItem value={1}>
<Typography color="black">Contacts</Typography>
</MenuItem>
<MenuItem value={2}>
<Typography color="black">Office</Typography>
</MenuItem>
{groups.map((group, i) => (
<MenuItem value={group} key={i}>
<Typography color="black">{group}</Typography>
</MenuItem>
))}
</Select>
</FormControl>
);
};
export default GroupSelect;
export default GroupSelect;

View File

@ -1,12 +1,31 @@
import { Settings } from "@mui/icons-material";
import { IconButton, Menu, MenuItem } from "@mui/material";
import { useState } from "react";
import UserLite from "../../api-bodies/UserLite";
import UserStatus from "../../api-bodies/UserStatus";
import { useAppDispatch, useAppSelector } from "../../redux/hooks";
import {
selectFavorites,
addFavorite,
removeFavorite,
} from "../../redux/slices/favoritesSlice";
import { open as openMeetingDetails } from "../../redux/slices/meetingDetailsOpenSlice";
import { selectMeeting } from "../../redux/slices/meetingsAndUserStatusSlice";
import { useNavigate } from "react-router-dom";
interface Props {
user: UserLite;
status: UserStatus;
}
const SettingsButton: React.FC = () => {
const SettingsButton: React.FC<Props> = ({ user, status }: Props) => {
const navigate = useNavigate();
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const favoritesUuids = useAppSelector(selectFavorites);
const meeting = useAppSelector((state) =>
selectMeeting(state, status.meetingID)
);
const dispatch = useAppDispatch();
const open = Boolean(anchorEl);
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
@ -14,7 +33,7 @@ const SettingsButton: React.FC = () => {
const handleClose = () => {
setAnchorEl(null);
};
return (
<div>
<IconButton
@ -22,7 +41,8 @@ const SettingsButton: React.FC = () => {
aria-controls={open ? "context-menu" : undefined}
aria-haspopup="true"
aria-expanded={open ? "true" : undefined}
onClick={handleClick}>
onClick={handleClick}
>
<Settings />
</IconButton>
<Menu
@ -32,13 +52,47 @@ const SettingsButton: React.FC = () => {
onClose={handleClose}
MenuListProps={{ "aria-labelledby": "basic-button" }}
>
<MenuItem onClick={handleClose}>Remove from Favourites</MenuItem>
<MenuItem onClick={handleClose}>View current meeting</MenuItem>
<MenuItem onClick={handleClose}>View upcoming meetings</MenuItem>
{favoritesUuids.includes(user.uuid) ? (
<MenuItem
onClick={() => {
handleClose();
dispatch(removeFavorite(user.uuid));
}}
>
Remove from Favorites
</MenuItem>
) : (
<MenuItem
onClick={() => {
handleClose();
dispatch(addFavorite(user.uuid));
}}
>
Add to Favorites
</MenuItem>
)}
{status.inMeeting ? (
<MenuItem
onClick={() => {
handleClose();
dispatch(openMeetingDetails(meeting));
}}
>
View current meeting
</MenuItem>
) : null}
<MenuItem
onClick={() => {
handleClose();
navigate(`../contacts/${user.uuid}`);
}}
>
View upcoming meetings
</MenuItem>
<MenuItem onClick={handleClose}>Create meeting</MenuItem>
</Menu>
</div>
);
};
export default SettingsButton;
export default SettingsButton;

View File

@ -6,18 +6,35 @@ import {
ListSubheader,
Toolbar,
} from "@mui/material";
import { useState } from "react";
import { useAppSelector } from "../../redux/hooks";
import { selectFavorites } from "../../redux/slices/favoritesSlice";
import { selectUserStatuses } from "../../redux/slices/meetingsAndUserStatusSlice";
import { selectTeam, selectUsers } from "../../redux/slices/usersSlice";
import GroupSelect from "./GroupSelect";
import SidebarUser, { SidebarUserObj } from "./SidebarUser";
import SidebarUser from "./SidebarUser";
// TODO: toolbar on top of sidebar since it goes under the navbar
const drawerWidth = 200;
interface Props {
users: SidebarUserObj[];
}
const Sidebar: React.FC = () => {
const [group, setGroup] = useState<string>("Favorites");
const favoritesUuids = useAppSelector(selectFavorites);
const teamUuids = useAppSelector(selectTeam);
const groupMembersUuids: string[] =
group === "Favorites" ? favoritesUuids : teamUuids;
const groupMembers = useAppSelector((state) =>
selectUsers(state, groupMembersUuids)
);
const groupMemberStatuses = useAppSelector((state) =>
selectUserStatuses(state, groupMembersUuids)
);
console.log(group);
const Sidebar: React.FC<Props> = ({ users }: Props) => {
return (
<Drawer
sx={{
@ -32,11 +49,11 @@ const Sidebar: React.FC<Props> = ({ users }: Props) => {
<Toolbar />
<List disablePadding sx={{ height: "80%", overflow: "auto" }}>
<ListSubheader disableGutters>
<GroupSelect />
<GroupSelect group={group} setGroup={setGroup} />
</ListSubheader>
{users.map((user) => (
<ListItem disablePadding key={user.id}>
<SidebarUser user={user} />
{groupMembers.map((user, i) => (
<ListItem disablePadding key={user.uuid}>
<SidebarUser user={user} status={groupMemberStatuses[i]} />
</ListItem>
))}
</List>

View File

@ -1,70 +1,36 @@
import { Box, Typography } from "@mui/material";
import UserLite from "../../api-bodies/UserLite";
import UserStatus from "../../api-bodies/UserStatus";
import SettingsButton from "./SettingsButton";
export interface SidebarUserObj {
id: number,
name: string,
meetingStatus: MeetingStatus;
}
export const enum MeetingStatus {
OFFLINE = 0,
ONLINE = 1,
AWAY = 2,
IN_MEETING = 3
}
const getMeetingStatusText = (ms: MeetingStatus): string => {
switch (ms) {
case MeetingStatus.OFFLINE: {
return "Offline";
}
case MeetingStatus.ONLINE: {
return "Online";
}
case MeetingStatus.AWAY: {
return "Away";
}
case MeetingStatus.IN_MEETING: {
return "In meeting";
}
}
};
const getStatusColor = (ms: MeetingStatus): string => {
switch (ms) {
case MeetingStatus.OFFLINE: {
return "#b8b8b8";
}
case MeetingStatus.ONLINE: {
return "#70ff70";
}
case MeetingStatus.AWAY: {
return "#ff7070";
}
case MeetingStatus.IN_MEETING: {
return "#e8da46";
}
}
};
import { MeetingStatus, getStatusColor } from "../../utils";
interface Props {
user: SidebarUserObj;
user: UserLite;
status: UserStatus;
}
const SidebarUser: React.FC<Props> = ({ user }: Props) => {
const SidebarUser: React.FC<Props> = ({ user, status }: Props) => {
return (
<Box sx={{ display: "flex", flexDirection: "row", p: 1 }}>
<Box
bgcolor={getStatusColor(user.meetingStatus)}
sx={{ p: 1 }} />
<Box
bgcolor={getStatusColor(
status.inMeeting
? MeetingStatus.IN_MEETING
: MeetingStatus.NOT_IN_MEETING
)}
sx={{ p: 1 }}
/>
<Box sx={{ display: "flex", flexDirection: "column", ml: 1 }}>
<Typography variant="h6">{user.name}</Typography>
<Typography variant="caption">{getMeetingStatusText(user.meetingStatus)}</Typography>
<Typography variant="caption">
{status.inMeeting
? MeetingStatus.IN_MEETING
: MeetingStatus.NOT_IN_MEETING}
</Typography>
</Box>
<SettingsButton />
<SettingsButton user={user} status={status} />
</Box>
);
};
export default SidebarUser;
export default SidebarUser;

View File

@ -19,6 +19,7 @@ export const meetingDetailsOpenSlice = createSlice({
open: (state, action) => {
state.open = true;
state.meeting = action.payload;
console.log(action.payload);
},
close: (state) => {
state.open = false;

View File

@ -75,4 +75,23 @@ export const selectUserStatus = (
};
}
};
export const selectUserStatuses = (
state: RootState,
uuids: string[]
): UserStatus[] => {
const userStatuses: UserStatus[] = [];
uuids.forEach((uuid) => {
const userStatus = state.meetingsAndUserStatuses.userStatuses[uuid];
if (userStatus) {
userStatuses.push(userStatus);
} else {
userStatuses.push({
uuid: uuid,
inMeeting: false,
meetingID: null,
});
}
});
return userStatuses;
};
export default meetingsAndUserStatusSlice.reducer;

View File

@ -48,19 +48,22 @@ export const fetchUsers = createAsyncThunk(
}
);
export const selectUser = (
state: RootState,
uuid: string | undefined
): UserLite | undefined => {
return uuid ? state.users.users[uuid] : undefined;
};
export const selectUsers = (state: RootState, uuids: string[]): UserLite[] => {
const users: UserLite[] = [];
uuids.forEach((uuid) => users.push(state.users.users[uuid]));
return users;
};
export const selectTeam = (state: RootState) => {
const team: UserLite[] = [];
if (state.users.team.manager !== null)
team.push(state.users.users[state.users.team.manager]);
state.users.team.sameManager.forEach((u) => team.push(state.users.users[u]));
state.users.team.directReports.forEach((u) =>
team.push(state.users.users[u])
);
const team: string[] = [];
if (state.users.team.manager !== null) 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 default usersSlice.reducer;

17
src/utils.tsx Normal file
View File

@ -0,0 +1,17 @@
const enum MeetingStatus {
NOT_IN_MEETING = "Not in meeting",
IN_MEETING = "In meeting",
}
const getStatusColor = (ms: MeetingStatus): string => {
switch (ms) {
case MeetingStatus.NOT_IN_MEETING: {
return "#70ff70";
}
case MeetingStatus.IN_MEETING: {
return "#ff7070";
}
}
};
export { MeetingStatus, getStatusColor };