frontend/src/teams/TeamList.tsx

304 lines
9.4 KiB
TypeScript

import React, { useEffect, useState } from "react";
import {
Theme,
ListItem,
ListItemAvatar,
Avatar,
ListItemText,
Typography,
ListItemButton,
Box,
IconButton,
Grid2,
} from "@mui/material";
import { pond } from "protobuf-ts/pond";
import { useGlobalState, useSnackbar, useTeamAPI, useUserAPI } from "providers";
import { Team, teamScope } from "models";
import { useNavigate } from "react-router";
import { cloneDeep } from "lodash";
import { getContextKeys, getContextTypes } from "pbHelpers/Context";
import { makeStyles } from "@mui/styles";
import TeamActions from "./TeamActions";
import ResponsiveTable, { Column } from "common/ResponsiveTable";
import { useMobile } from "hooks";
import { AddBox } from "@mui/icons-material";
import TeamSettings from "./TeamSettings";
const useStyles = makeStyles((theme: Theme) => ({
cardContent: {
padding: theme.spacing(1)
},
clickable: {
"&:hover": {
textDecoration: "underline",
cursor: "pointer"
}
},
input: {
marginLeft: "auto",
marginRight: theme.spacing(2),
marginTop: theme.spacing(1)
},
button: {
width: theme.spacing(6),
height: theme.spacing(6),
marginTop: "auto",
marginBottom: "auto",
},
titleBox: {
display: "flex",
alignItems: "center",
marginLeft: theme.spacing(1),
width: "100%"
}
}))
export default function TeamList() {
const classes = useStyles();
const [teams, setTeams] = useState<Team[]>([]);
const teamAPI = useTeamAPI();
const userAPI = useUserAPI();
const history = useNavigate();
const isMobile = useMobile()
const snackbar = useSnackbar();
const [{ user, as, team }] = useGlobalState();
const [teamPerms, setTeamPerms] = useState<Map<string | number, pond.Permission[]>>(new Map());
const [teamPrefs, setTeamPrefs] = useState<Map<string | number, pond.TeamPreferences> >(new Map());
const [total, setTotal] = useState<number>(0);
const [limit, setLimit] = useState<number>(5);
const [page, setPage] = useState<number>(0);
const [loading, setLoading] = useState<boolean>(true);
const [search, setSearch] = useState<string>("");
const [teamDialogOpen, setTeamDialogOpen] = useState<boolean>(false);
const loadTeams = () => {
setLoading(true);
let newTeamPerms: Map<string | number, pond.Permission[]> = new Map();
let newTeamPrefs: Map<string | number, pond.TeamPreferences> = new Map();
let viewAs: string | undefined = as
if (as === team.key()) {
viewAs = undefined
}
teamAPI
.listTeams(limit, limit * page, undefined, "name", undefined, undefined, " ", search)
.then(resp => {
setTotal(resp.data.total ? resp.data.total : 0);
let newTeams: Team[] = []
resp.data.teams.forEach(team => {
let u = viewAs ? viewAs : user.id();
let t = Team.create(team)
if (team.settings && u !== "") {
userAPI
.getUser(u, teamScope(team.settings.key))
.then(resp => {
newTeamPerms.set(t.key(), resp.permissions)
newTeamPrefs.set(t.key(), resp.preferences)
t.preferences = cloneDeep(resp.preferences)
t.permissions = cloneDeep(resp.permissions)
}).catch(() => {
snackbar.error("Error loading teams.")
})
.finally(() => {
setTeamPerms(newTeamPerms);
setTeamPrefs(newTeamPrefs);
});
}
newTeams.push(t)
});
setTeams(newTeams);
})
.finally(() => {
setLoading(false);
});
};
useEffect(() => {
loadTeams();
}, [limit, page, search, as]);
const columns = (): Column<Team>[] => {
return [
{
title: "",
render: team => {
let permissions = teamPerms.get(team.settings?.key);
if (!permissions) {
permissions = [];
}
let preferences = teamPrefs.get(team.id());
if (!preferences) {
preferences = pond.TeamPreferences.create();
}
return (
<ListItem
alignItems="flex-start"
disablePadding={!isMobile}
secondaryAction={
<TeamActions
team={team}
permissions={permissions}
refreshCallback={loadTeams}
removeSelfCallback={loadTeams}
preferences={preferences}
toggleNotificationPreference={() => {
if (preferences) {
let newTeamPrefs = cloneDeep(teamPrefs);
let prefs = newTeamPrefs.get(team.id()) ?? pond.TeamPreferences.create()
prefs.notify = !newTeamPrefs.get(team.id())?.notify
newTeamPrefs.set(team.id(), prefs)
teamAPI
.updatePreferences(
team.key(),
prefs,
getContextKeys(),
getContextTypes()
)
.then(() => {
setTeamPrefs(newTeamPrefs);
});
}
}}
/>
}
>
<ListItemButton
onClick={() => {
if (team.settings) history(team.settings.key, { state: {team: team} });
}}
>
<ListItemAvatar sx={{ marginLeft: isMobile ? -2 : 0 }}>
<Avatar
onClick={() => {
if (team.settings) history(team.settings.key);
}}
src={team.settings?.avatar}
alt=""
className={classes.clickable}
/>
</ListItemAvatar>
<ListItemText
primary={
<Typography fontWeight={"bold"}>
{team.settings?.name}
</Typography>
}
secondary={
!isMobile && <React.Fragment>
<Typography component="span" variant="body2" color="textSecondary">
{team.settings?.info}
</Typography>
</React.Fragment>
}
/>
</ListItemButton>
</ListItem>
)
}
}
]
}
const handleChange = (event: any) => {
setLimit(event.target.value);
};
const renderGutter = (team: Team) => {
return(
<Typography variant="body2" color="textSecondary" sx={{ padding: 1 }}>
{team.settings.info}
</Typography>
)
}
const renderTitle = () => {
return (
<Box className={classes.titleBox}>
<Avatar
src={user.settings.avatar}
/>
<Grid2 container direction="column" marginLeft={1}>
<Grid2 marginBottom={-0.25}>
<Typography variant="h6">
{user.name()}'s Teams
</Typography>
</Grid2>
<Grid2 marginTop={-0.25}>
<Typography variant="body2">
All teams you are a member of.
</Typography>
</Grid2>
</Grid2>
<IconButton onClick={() => setTeamDialogOpen(true)} color="primary" className={classes.button}>
<AddBox/>
</IconButton>
</Box>
)
}
return (
<>
<ResponsiveTable<Team>
title={renderTitle()}
rows={teams}
columns={columns()}
handleRowsPerPageChange={handleChange}
total={total}
pageSize={limit}
page={page}
setPage={setPage}
setSearchText={setSearch}
isLoading={loading}
renderGutter={isMobile ? renderGutter : undefined}
hideKeys
loadMore={() => {
let current = cloneDeep(teams)
setLoading(true);
let newTeamPerms: Map<string | number, pond.Permission[]> = new Map();
let newTeamPrefs: Map<string | number, pond.TeamPreferences> = new Map();
let viewAs: string | undefined = as
if (as === team.key()) {
viewAs = undefined
}
teamAPI
.listTeams(limit, current.length, undefined, "name", undefined, undefined, " ", search)
.then(resp => {
setTotal(resp.data.total ? resp.data.total : 0);
let newTeams: Team[] = []
resp.data.teams.forEach(team => {
let u = viewAs ? viewAs : user.id();
let t = Team.create(team)
if (team.settings && u !== "") {
userAPI
.getUser(u, teamScope(team.settings.key))
.then(resp => {
newTeamPerms.set(t.key(), resp.permissions)
newTeamPrefs.set(t.key(), resp.preferences)
t.preferences = cloneDeep(resp.preferences)
t.permissions = cloneDeep(resp.permissions)
}).catch(() => {
snackbar.error("Error loading teams.")
})
.finally(() => {
setTeamPerms(newTeamPerms);
setTeamPrefs(newTeamPrefs);
});
}
newTeams.push(t)
});
setTeams(current.concat(newTeams));
})
.finally(() => {
setLoading(false);
});
}}
/>
<TeamSettings
teamDialogOpen={teamDialogOpen}
closeTeamDialogCallback={() => setTeamDialogOpen(false)}
refreshCallback={loadTeams}
/>
</>
)
}