team page added, uses data from previous page so that the team isn't loaded again if it's clicked from the team list
This commit is contained in:
parent
091634161f
commit
0e98d7329f
7 changed files with 63 additions and 23 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { CircularProgress, Typography, useTheme } from "@mui/material";
|
||||
import { Box, CircularProgress, Typography, useTheme } from "@mui/material";
|
||||
|
||||
interface Props {
|
||||
message?: string;
|
||||
|
|
@ -9,7 +9,7 @@ export default function LoadingScreen(props: Props) {
|
|||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box sx={{ width: "100%", height: "100%", justifyContent: "center", alignContent: "center", textAlign: "center" }}>
|
||||
<CircularProgress
|
||||
size={150}
|
||||
style={{ marginBottom: theme.spacing(4) }}
|
||||
|
|
@ -18,6 +18,6 @@ export default function LoadingScreen(props: Props) {
|
|||
<Typography variant="h6">
|
||||
{message ? message : "Loading..."}
|
||||
</Typography>
|
||||
</>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
@ -22,6 +22,10 @@ const useStyles = makeStyles((theme: Theme) => {
|
|||
title: {
|
||||
padding: theme.spacing(1)
|
||||
},
|
||||
subtitle: {
|
||||
padding: theme.spacing(1),
|
||||
marginTop: theme.spacing(-2),
|
||||
},
|
||||
titleContainer: {
|
||||
border: "none",
|
||||
},
|
||||
|
|
@ -51,6 +55,7 @@ interface Props<T> {
|
|||
rows: T[],
|
||||
columns?: Column<T>[],
|
||||
title?: string | JSX.Element;
|
||||
subtitle?: string | JSX.Element;
|
||||
total: number,
|
||||
pageSize: number,
|
||||
page: number,
|
||||
|
|
@ -75,6 +80,7 @@ export default function ResponsiveTable<T extends Object>(props: Props<T>) {
|
|||
setPage,
|
||||
handleRowsPerPageChange,
|
||||
title,
|
||||
subtitle,
|
||||
renderGutter,
|
||||
setSearchText,
|
||||
isLoading,
|
||||
|
|
@ -177,6 +183,20 @@ export default function ResponsiveTable<T extends Object>(props: Props<T>) {
|
|||
return null
|
||||
}
|
||||
|
||||
const renderSubtitle = () => {
|
||||
if (typeof(subtitle) === "string" ) return (
|
||||
<Typography variant="body2" color="textSecondary" className={classes.subtitle}>
|
||||
{subtitle}
|
||||
</Typography>
|
||||
)
|
||||
if (subtitle) return (
|
||||
<Box className={classes.subtitle}>
|
||||
{subtitle} haha lol
|
||||
</Box>
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
function capitalize(val: any) {
|
||||
return String(val).charAt(0).toUpperCase() + String(val).slice(1);
|
||||
}
|
||||
|
|
@ -231,6 +251,7 @@ export default function ResponsiveTable<T extends Object>(props: Props<T>) {
|
|||
<Box>
|
||||
<Box className={classes.mobileTitleBox}>
|
||||
{renderTitle()}
|
||||
{renderSubtitle()}
|
||||
{setSearchText && searchBar()}
|
||||
</Box>
|
||||
{rows.map((row, index) => {
|
||||
|
|
@ -290,15 +311,10 @@ export default function ResponsiveTable<T extends Object>(props: Props<T>) {
|
|||
<TableCell colSpan={10000} sx={{ border: "none" }}>
|
||||
<Grid2 container justifyContent="space-between">
|
||||
<Grid2>
|
||||
{typeof(title) === "string" ?
|
||||
<Typography variant="h5" className={classes.title}>
|
||||
{title}
|
||||
</Typography>
|
||||
:
|
||||
title && <Box className={classes.title}>
|
||||
{title}
|
||||
<Box sx={{ marginTop: -1 }}>
|
||||
{renderTitle()}
|
||||
{renderSubtitle()}
|
||||
</Box>
|
||||
}
|
||||
</Grid2>
|
||||
<Grid2>
|
||||
{setSearchText &&<Box className={classes.searchContainer}>
|
||||
|
|
@ -308,7 +324,7 @@ export default function ResponsiveTable<T extends Object>(props: Props<T>) {
|
|||
</Grid2>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{ !hideKeys && <TableRow>
|
||||
<TableRow sx={ hideKeys ? { display: "none" } : undefined } >
|
||||
{ renderGutter && <TableCell></TableCell>}
|
||||
{ columns ?
|
||||
columns.map((column, index) => {
|
||||
|
|
@ -327,7 +343,7 @@ export default function ResponsiveTable<T extends Object>(props: Props<T>) {
|
|||
)
|
||||
})
|
||||
}
|
||||
</TableRow>}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{ isLoading ?
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import SideNavigator from "./SideNavigator";
|
|||
import Users from "pages/Users";
|
||||
import { useMobile } from "hooks";
|
||||
import BottomNavigator from "./BottomNavigator";
|
||||
import TeamPage from "pages/Team";
|
||||
|
||||
interface Props {
|
||||
open: boolean,
|
||||
|
|
@ -52,10 +53,12 @@ export default function Router(props: Props) {
|
|||
|
||||
{/* Redirects */}
|
||||
<Route path="/callback" element={<Navigate to="/" />} />
|
||||
<Route path="/team/:teamID" element={<Navigate to="/teams/:teamID" />} />
|
||||
|
||||
{/* Page routes */}
|
||||
<Route index element={hello()} />
|
||||
<Route path="/teams" element={<Teams/>} />
|
||||
<Route path="/teams/:teamID" element={<TeamPage/>} />
|
||||
<Route path="/users" element={<Users/>} />
|
||||
{/* <Route
|
||||
path="/teams/:teamID"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Container, Theme } from "@mui/material";
|
||||
import { Container, SxProps, Theme } from "@mui/material";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import classNames from "classnames";
|
||||
import React, { PropsWithChildren } from "react";
|
||||
|
|
@ -37,11 +37,12 @@ const useStyles = makeStyles((theme: Theme) => ({
|
|||
interface Props extends PropsWithChildren{
|
||||
fullViewport?: boolean;
|
||||
isCenterCenter?: boolean;
|
||||
sx?: SxProps<Theme>;
|
||||
}
|
||||
|
||||
export const PageContainer: React.FunctionComponent<Props> = props => {
|
||||
const classes = useStyles();
|
||||
const { children, fullViewport, isCenterCenter } = props;
|
||||
const { children, fullViewport, isCenterCenter, sx } = props;
|
||||
// let fullViewport = false
|
||||
// let isCenterCenter = false
|
||||
return (
|
||||
|
|
@ -50,6 +51,7 @@ export const PageContainer: React.FunctionComponent<Props> = props => {
|
|||
fullViewport ? classes.fullViewportContainer : classes.pageContainer,
|
||||
isCenterCenter && classes.centerCenter
|
||||
)}
|
||||
sx={sx}
|
||||
disableGutters
|
||||
maxWidth={false}
|
||||
children={<React.Fragment>{children}</React.Fragment>}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
IconButton,
|
||||
} from "@mui/material";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import { useGlobalState, useTeamAPI, useUserAPI } from "providers";
|
||||
import { useGlobalState, useSnackbar, useTeamAPI, useUserAPI } from "providers";
|
||||
import { Team, teamScope } from "models";
|
||||
import { useNavigate } from "react-router";
|
||||
import { cloneDeep } from "lodash";
|
||||
|
|
@ -57,6 +57,7 @@ export default function TeamList() {
|
|||
const userAPI = useUserAPI();
|
||||
const history = useNavigate();
|
||||
const isMobile = useMobile()
|
||||
const snackbar = useSnackbar();
|
||||
const [{ user, as }] = useGlobalState();
|
||||
const [teamPerms, setTeamPerms] = useState<Map<string | number, pond.Permission[]>>(new Map());
|
||||
const [teamPrefs, setTeamPrefs] = useState<Map<string | number, pond.TeamPreferences> >(new Map());
|
||||
|
|
@ -91,13 +92,17 @@ export default function TeamList() {
|
|||
.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(Team.create(team))
|
||||
newTeams.push(t)
|
||||
});
|
||||
setTeams(newTeams);
|
||||
})
|
||||
|
|
@ -158,7 +163,7 @@ export default function TeamList() {
|
|||
>
|
||||
<ListItemButton
|
||||
onClick={() => {
|
||||
if (team.settings) history(team.settings.key);
|
||||
if (team.settings) history(team.settings.key, { state: {team: team} });
|
||||
}}
|
||||
>
|
||||
<ListItemAvatar sx={{ marginLeft: isMobile ? -2 : 0 }}>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Avatar } from "@mui/material";
|
||||
import { Avatar, SxProps, Theme, useTheme } from "@mui/material";
|
||||
import { Team, User } from "models";
|
||||
import React from "react";
|
||||
|
||||
|
|
@ -40,18 +40,34 @@ interface Props {
|
|||
user: User | Team;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
sx?: SxProps<Theme>;
|
||||
size?: "small" | "medium" | "large";
|
||||
}
|
||||
|
||||
export default function UserSettings(props: Props) {
|
||||
const { user, className, style } = props;
|
||||
export default function UserAvatar(props: Props) {
|
||||
const theme = useTheme()
|
||||
const { user, className, style, sx, size } = props;
|
||||
|
||||
const getSize = () => {
|
||||
if (size==="large") return theme.spacing(8)
|
||||
if (size==="medium") return theme.spacing(6)
|
||||
if (size==="small") return theme.spacing(4)
|
||||
return undefined
|
||||
}
|
||||
|
||||
return (
|
||||
<Avatar
|
||||
alt={user.name()}
|
||||
// sx={sx}
|
||||
src={user.settings.avatar}
|
||||
className={className}
|
||||
style={style}
|
||||
{...stringAvatar(user.name())}
|
||||
sx={{
|
||||
...sx, // Spread the existing styles
|
||||
width: getSize(), // Override the width
|
||||
height: getSize(), // Override the height
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -131,7 +131,6 @@ export default function UserSettings(props: Props) {
|
|||
const changeTimezone = (tz: string) => {
|
||||
let updatedUser = User.clone(user);
|
||||
updatedUser.settings.timezone = tz;
|
||||
|
||||
setUser(updatedUser);
|
||||
};
|
||||
|
||||
|
|
@ -183,7 +182,6 @@ export default function UserSettings(props: Props) {
|
|||
<MuiTelInput value={phoneNumber} onChange={changePhoneNumber} />
|
||||
</FormControl>
|
||||
</Grid2>
|
||||
|
||||
<Grid2 size={{ xs: 12 }}>
|
||||
<Box>
|
||||
<SearchSelect
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue