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:
Carter 2024-11-29 18:28:38 -06:00
parent 091634161f
commit 0e98d7329f
7 changed files with 63 additions and 23 deletions

View file

@ -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>
);
}

View file

@ -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 ?

View file

@ -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"

View file

@ -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>}

View file

@ -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 }}>

View file

@ -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
}}
/>
);
}

View file

@ -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