287 lines
8.8 KiB
TypeScript
287 lines
8.8 KiB
TypeScript
import { Avatar, AvatarGroup, Box, Button, Card, Divider, Grid2, List, ListItemButton, Theme, Typography } from "@mui/material";
|
|
import { makeStyles } from "@mui/styles";
|
|
import LoadingScreen from "app/LoadingScreen";
|
|
import Chat from "chat/Chat";
|
|
import SmartBreadcrumb from "common/SmartBreadcrumb";
|
|
// import SmartBreadcrumb from "common/SmartBreadcrumb";
|
|
import { useMobile } from "hooks";
|
|
import { cloneDeep } from "lodash";
|
|
import { Team, teamScope, User } from "models";
|
|
import { appendToUrl } from "navigation/Router";
|
|
// import { useSnackbar } from "notistack";
|
|
import PageContainer from "pages/PageContainer";
|
|
import { getContextKeys, getContextTypes } from "pbHelpers/Context";
|
|
import { pond } from "protobuf-ts/pond";
|
|
import { useGlobalState, useSnackbar, useTeamAPI, useUserAPI } from "providers";
|
|
import { useEffect, useState } from "react";
|
|
import { useLocation, useNavigate, useParams } from 'react-router-dom';
|
|
import { getSignatureAccentColour, IsAdaptiveAgriculture, IsStreamline } from "services/whiteLabel";
|
|
import TeamActions from "teams/TeamActions";
|
|
import TeamKeyManager from "teams/TeamKeyManager";
|
|
import ObjectUsers from "user/ObjectUsers";
|
|
import UserAvatar from "user/UserAvatar";
|
|
import { or } from "utils/types";
|
|
|
|
interface StylesProps {
|
|
isMobile: boolean;
|
|
}
|
|
|
|
const useStyles = makeStyles<Theme, StylesProps>((theme: Theme) => {
|
|
// const isMobile = useMobile()
|
|
return ({
|
|
avatar: {
|
|
// margin: theme.spacing(2),
|
|
width: theme.spacing(4),
|
|
height: theme.spacing(4)
|
|
},
|
|
button: {
|
|
whiteSpace: "nowrap",
|
|
overflow: "hidden",
|
|
textOverflow: "ellipsis",
|
|
textTransform: "capitalize",
|
|
color: getSignatureAccentColour(),
|
|
},
|
|
card: {
|
|
height: theme.spacing(72),
|
|
},
|
|
cardMobile: {
|
|
height: theme.spacing(48)
|
|
},
|
|
container: {
|
|
display: 'flex',
|
|
justifyContent: "space-between",
|
|
alignItems: 'center',
|
|
width: '100%',
|
|
position: 'relative',
|
|
},
|
|
leftBox: {
|
|
minWidth: 0, // Important for text overflow
|
|
overflow: 'hidden',
|
|
textOverflow: "ellipsis",
|
|
},
|
|
rightBox: {
|
|
flexShrink: 0,
|
|
zIndex: 1, // Ensure it sits above the left box
|
|
},
|
|
ellipsis: {
|
|
whiteSpace: 'nowrap',
|
|
overflow: 'hidden',
|
|
textOverflow: 'ellipsis',
|
|
},
|
|
});
|
|
});
|
|
|
|
export default function TeamPage() {
|
|
const teamAPI = useTeamAPI();
|
|
const userAPI = useUserAPI();
|
|
const snackbar = useSnackbar()
|
|
const isMobile = useMobile();
|
|
const classes = useStyles({isMobile});
|
|
const { state } = useLocation();
|
|
const navigate = useNavigate();
|
|
// const { teamID } = useParams<{ teamID: string }>() as { teamID: string };
|
|
const { teamID } = useParams<{ teamID: string }>();
|
|
// const [{ user }, dispatch] = useGlobalState();
|
|
const [team, setTeam] = useState<Team>(state?.team ? Team.create(state.team) : Team.create())
|
|
const [users, setUsers] = useState<User[]>([]);
|
|
const [loading, setLoading] = useState(false)
|
|
const [loadingUsers, setLoadingUsers] = useState(false)
|
|
const [userDialog, setUserDialog] = useState(false)
|
|
const teamKey = teamID ? teamID : "";
|
|
const [{ user }] = useGlobalState()
|
|
|
|
|
|
const getTeam = () => {
|
|
if (teamKey.length < 1) return
|
|
setLoading(true)
|
|
teamAPI.getTeam(teamKey).then(resp => {
|
|
setTeam(resp)
|
|
}).catch(() => {
|
|
snackbar.error("Error loading team")
|
|
}).finally(() => {
|
|
setLoading(false)
|
|
})
|
|
}
|
|
|
|
const listTeamUsers = () => {
|
|
setLoadingUsers(true)
|
|
userAPI.listObjectUsers(teamScope(teamKey)).then(resp => {
|
|
let rUsers: User[] = [];
|
|
or(resp.data, { users: [] }).users.forEach((user: any) => {
|
|
rUsers.push(User.any(user));
|
|
});
|
|
setUsers(rUsers);
|
|
}).finally(() => {
|
|
setLoadingUsers(false)
|
|
})
|
|
}
|
|
|
|
const toggleNotificationPreference = () => {
|
|
let updatedPreferences = cloneDeep(team.preferences);
|
|
updatedPreferences.notify = !team.preferences.notify;
|
|
teamAPI
|
|
.updatePreferences(team?.key(), updatedPreferences, getContextKeys(), getContextTypes())
|
|
.then(() => {
|
|
let newTeam = Team.create(team.protobuf())
|
|
newTeam.preferences = updatedPreferences;
|
|
newTeam.permissions = team.permissions;
|
|
setTeam(newTeam)
|
|
})
|
|
.catch(() => {
|
|
snackbar.error(
|
|
"Error occured while " +
|
|
(team.preferences.notify ? "enabling" : "disabling") +
|
|
" notifications"
|
|
);
|
|
});
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (state && state.team) return
|
|
getTeam();
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
listTeamUsers()
|
|
}, [teamKey]);
|
|
|
|
if (loading) return (
|
|
<LoadingScreen message="Loading team"/>
|
|
)
|
|
|
|
if (team.key().length < 1) return (
|
|
<PageContainer>
|
|
Error loading team
|
|
</PageContainer>
|
|
)
|
|
|
|
const title = () => {
|
|
return (
|
|
<Grid2 container direction="row" spacing={1} wrap="nowrap" >
|
|
<Grid2 sx={{ minWidth: "auto"}}>
|
|
<UserAvatar user={team} size={isMobile ? "medium" : "large"} />
|
|
</Grid2>
|
|
<Grid2 container direction={"column"} spacing={isMobile ? 0 : 0.5} >
|
|
<Grid2 >
|
|
<Typography variant={isMobile ? "h5" : "h4"} className={classes.ellipsis} >
|
|
{/* {team.name()} */}
|
|
<SmartBreadcrumb paddingBottom={1} teamName={team.name()} />
|
|
{/* <SmartBreadcrumb /> */}
|
|
</Typography>
|
|
</Grid2>
|
|
<Grid2>
|
|
<Typography variant={isMobile ? "caption" : "body2"} color="textSecondary" className={classes.ellipsis}>
|
|
{team.settings.info}
|
|
</Typography>
|
|
</Grid2>
|
|
</Grid2>
|
|
</Grid2>
|
|
)
|
|
}
|
|
|
|
const userAvatars = () => {
|
|
if (loadingUsers) return null
|
|
return (
|
|
<Grid2 container direction={"row"} justifyContent={"space-between"} paddingTop={1}>
|
|
<Button className={classes.button} onClick={() => setUserDialog(true)}>
|
|
<Grid2 container spacing={1}>
|
|
<Grid2>
|
|
<AvatarGroup max={3}>
|
|
{users.map((user, index) => {
|
|
return(
|
|
<Avatar key={index} className={classes.avatar} alt={user.name()} src={user.settings.avatar}/>
|
|
)
|
|
})}
|
|
</AvatarGroup>
|
|
</Grid2>
|
|
<Grid2 alignContent={"center"}>
|
|
<Typography color="textPrimary" variant="body2">
|
|
{users.length + " team members"}
|
|
</Typography>
|
|
</Grid2>
|
|
</Grid2>
|
|
</Button>
|
|
<Grid2></Grid2>
|
|
</Grid2>
|
|
)
|
|
}
|
|
|
|
const toDevices = () => {
|
|
navigate(appendToUrl("devices"))
|
|
}
|
|
|
|
const toGroups = () => {
|
|
navigate(appendToUrl("groups"))
|
|
}
|
|
|
|
const toBins = () => {
|
|
// navigate(appendToUrl("bins"))
|
|
navigate("bins")
|
|
}
|
|
|
|
const isAg = IsAdaptiveAgriculture()
|
|
const isStreamline = IsStreamline()
|
|
|
|
return (
|
|
<PageContainer spacing={isMobile ? 0 : 2}>
|
|
<Box className={classes.container}>
|
|
<Box className={classes.leftBox}>
|
|
{title()}
|
|
</Box>
|
|
<Box className={classes.rightBox}>
|
|
<TeamActions
|
|
team={team}
|
|
permissions={team.permissions}
|
|
preferences={team.preferences}
|
|
refreshCallback={getTeam}
|
|
toggleNotificationPreference={toggleNotificationPreference}
|
|
/>
|
|
</Box>
|
|
</Box>
|
|
{userAvatars()}
|
|
<ObjectUsers
|
|
scope={teamScope(teamKey)}
|
|
label={team.name()}
|
|
permissions={team.permissions}
|
|
isDialogOpen={userDialog}
|
|
closeDialogCallback={() => setUserDialog(false)}
|
|
refreshCallback={() => {}}
|
|
/>
|
|
<Grid2 container spacing={1}>
|
|
<Grid2 size={{ xs: 12, sm: 4 }}>
|
|
<Card className={ isMobile ? classes.cardMobile : classes.card }>
|
|
<Chat objectKey={team.key()} type={pond.NoteType.NOTE_TYPE_TEAM}/>
|
|
</Card>
|
|
</Grid2>
|
|
<Grid2 size={{ xs: 12, sm: 4 }}>
|
|
<Card className={ isMobile ? classes.cardMobile : classes.card }>
|
|
<List>
|
|
<Divider />
|
|
<ListItemButton onClick={toDevices}>
|
|
Devices
|
|
</ListItemButton>
|
|
<Divider />
|
|
<ListItemButton onClick={toGroups}>
|
|
Groups
|
|
</ListItemButton>
|
|
{((isAg || isStreamline) || user.hasFeature("admin")) &&
|
|
<>
|
|
<Divider />
|
|
<ListItemButton onClick={toBins}>
|
|
Bins
|
|
</ListItemButton>
|
|
</>
|
|
}
|
|
<Divider />
|
|
</List>
|
|
</Card>
|
|
</Grid2>
|
|
<Grid2 size={{ xs: 12, sm: 4 }}>
|
|
<Card className={ isMobile ? classes.cardMobile : classes.card }>
|
|
<TeamKeyManager teamId={teamKey} permissions={team.permissions ?? []} />
|
|
</Card>
|
|
</Grid2>
|
|
</Grid2>
|
|
</PageContainer>
|
|
);
|
|
}
|