From c94b8093ff29440ad64edf8cef8bea8720226b98 Mon Sep 17 00:00:00 2001 From: Carter Date: Tue, 26 Nov 2024 15:28:49 -0600 Subject: [PATCH] teams list now uses responsive container --- src/common/ResponsiveTable.tsx | 29 +-- src/pages/Teams.tsx | 7 +- src/pages/Users.tsx | 6 +- src/teams/TeamList.tsx | 360 +++++++++++---------------------- src/teams/TeamSettings.tsx | 6 +- 5 files changed, 147 insertions(+), 261 deletions(-) diff --git a/src/common/ResponsiveTable.tsx b/src/common/ResponsiveTable.tsx index 2d11b8c..debe1fa 100644 --- a/src/common/ResponsiveTable.tsx +++ b/src/common/ResponsiveTable.tsx @@ -50,7 +50,7 @@ export interface Column { interface Props { rows: T[], columns?: Column[], - title?: string; + title?: string | JSX.Element; total: number, pageSize: number, page: number, @@ -63,6 +63,7 @@ interface Props { rowsPerPageOptions?: number[]; noDataMessage?: string; renderMobile?: (row: T) => JSX.Element; + hideKeys?: boolean; } export default function ResponsiveTable(props: Props) { @@ -82,6 +83,7 @@ export default function ResponsiveTable(props: Props) { rowsPerPageOptions, noDataMessage, renderMobile, + hideKeys, } = props const classes = useStyles(); @@ -169,7 +171,7 @@ export default function ResponsiveTable(props: Props) { ) if (title) return ( - title + {title} ) return null @@ -185,16 +187,16 @@ export default function ResponsiveTable(props: Props) { ) if (columns) { return ( - + {columns.map((column, index) => { return ( - + { !hideKeys && {column.title+": "} - - + } + {column.render(row)} @@ -294,7 +296,7 @@ export default function ResponsiveTable(props: Props) { : title && - title + {title} } @@ -306,7 +308,7 @@ export default function ResponsiveTable(props: Props) { - + { !hideKeys && { renderGutter && } { columns ? columns.map((column, index) => { @@ -325,7 +327,7 @@ export default function ResponsiveTable(props: Props) { ) }) } - + } { isLoading ? @@ -340,14 +342,14 @@ export default function ResponsiveTable(props: Props) { rows.map((row, index) => { return ( - + {renderGutter && handleCollapse(index)}> @@ -357,7 +359,7 @@ export default function ResponsiveTable(props: Props) { {columns ? columns.map((column, j) => { if (column.render) return ( - + {column.render(row)} ) @@ -369,9 +371,10 @@ export default function ResponsiveTable(props: Props) { }) : Object.values(row).map((value, j) => { + console return ( - {value.toString()} + {value ? value.toString() : ""} ) }) diff --git a/src/pages/Teams.tsx b/src/pages/Teams.tsx index ad2d9fd..98901be 100644 --- a/src/pages/Teams.tsx +++ b/src/pages/Teams.tsx @@ -1,15 +1,10 @@ -import { Box } from "@mui/material"; -import { useMobile } from "hooks"; import PageContainer from "pages/PageContainer"; import TeamList from "teams/TeamList"; export default function Teams() { - const isMobile = useMobile(); return ( - - - + ); } diff --git a/src/pages/Users.tsx b/src/pages/Users.tsx index 3b1e2d3..388dbb7 100644 --- a/src/pages/Users.tsx +++ b/src/pages/Users.tsx @@ -239,7 +239,7 @@ export default function Users() { ); return ( - + {avatar} @@ -252,7 +252,7 @@ export default function Users() { render: user => { const actions = user.settings.actions; return ( - + {actions.slice(0, 2).map(action => flagChip(action))} {actions.length > 3 && flagChip("+" + (actions.length - 3) + " more")} @@ -265,7 +265,7 @@ export default function Users() { render: user => { const features = user.settings.features; return ( - + {features.slice(0, 2).map(feature => flagChip(feature))} {features.length > 3 && flagChip("+" + (features.length - 3) + " more")} diff --git a/src/teams/TeamList.tsx b/src/teams/TeamList.tsx index 12143c2..3356087 100644 --- a/src/teams/TeamList.tsx +++ b/src/teams/TeamList.tsx @@ -1,38 +1,27 @@ import React, { useCallback, useEffect, useState } from "react"; import { - Card, Theme, - List, ListItem, ListItemAvatar, Avatar, ListItemText, Typography, - ListItemSecondaryAction, - Divider, - useTheme, - TextField, - InputAdornment, - CircularProgress, + ListItemButton, + Box, IconButton, - MenuItem, } from "@mui/material"; import { pond } from "protobuf-ts/pond"; import { useGlobalState, useTeamAPI, useUserAPI } from "providers"; import { Team, teamScope } from "models"; import { useNavigate } from "react-router"; -// import TeamActions from "./TeamActions"; -import SearchIcon from '@mui/icons-material/Search'; -import NextIcon from "@mui/icons-material/NavigateNext"; -import PrevIcon from "@mui/icons-material/NavigateBefore"; -import LastIcon from "@mui/icons-material/SkipNext"; -import FirstIcon from "@mui/icons-material/SkipPrevious"; -import TeamSettings from "./TeamSettings"; -import { AddBox } from "@mui/icons-material"; 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: { @@ -54,96 +43,63 @@ const useStyles = makeStyles((theme: Theme) => ({ height: theme.spacing(6), marginTop: "auto", marginBottom: "auto" + }, + titleBox: { + display: "flex", + alignItems: "center", } })) -const limits = [ - { - value: 5, - label: "5" - }, - { - value: 10, - label: "10" - }, - { - value: 15, - label: "15" - }, - { - value: 20, - label: "20" - } -]; - export default function TeamList() { const classes = useStyles(); - const [teams, setTeams] = useState([]); + const [teams, setTeams] = useState([]); const teamAPI = useTeamAPI(); const userAPI = useUserAPI(); const history = useNavigate(); - const theme = useTheme(); + const isMobile = useMobile() const [{ user, as }] = useGlobalState(); - const [teamPerms, setTeamPerms] = useState([]); - const [teamPrefs, setTeamPrefs] = useState([]); + const [teamPerms, setTeamPerms] = useState>(new Map()); + const [teamPrefs, setTeamPrefs] = useState >(new Map()); const [total, setTotal] = useState(0); const [limit, setLimit] = useState(5); const [page, setPage] = useState(0); const [loading, setLoading] = useState(true); const [search, setSearch] = useState(""); - const [teamDialogOpen, setTeamDialogOpen] = useState(false); const [asUser, setAsUser] = useState(undefined); - const [userId, setUserId] = useState(undefined); - const [isUser, setIsUser] = useState(true); + const [teamDialogOpen, setTeamDialogOpen] = useState(false); useEffect(() => { if (as.includes("auth0")) setAsUser(as); }, [as]); - useEffect(() => { - setUserId(user.id()); - }, [user]); - - useEffect(() => { - if (!asUser) { - setIsUser(true); - return; - } - setIsUser(userId === asUser); - }, [userId, asUser]); - - // Convert string array to valid Permission[] array - // const convertToPermissions = (permissions: any[]): pond.Permission[] => { - // return permissions - // .map(p => p as any as pond.Permission) // Type assertion to treat strings as Permission - // .filter(p => Object.entries(pond.Permission).includes(p)); // Filter to ensure valid Permission values - // }; - const loadTeams = useCallback(() => { setLoading(true); - let newTeamPerms: pond.Permission[][] = []; - let newTeamPrefs: pond.TeamPreferences[] = []; + let newTeamPerms: Map = new Map(); + let newTeamPrefs: Map = new Map(); let searchString = search; teamAPI .listTeams(limit, limit * page, undefined, "name", searchString, undefined, asUser) .then(resp => { - setTotal(resp.data.total); - resp.data.teams.forEach((team, index) => { + setTotal(resp.data.total ? resp.data.total : 0); + let newTeams: Team[] = [] + resp.data.teams.forEach(team => { let u = asUser ? asUser : user.id(); + let t = Team.create(team) if (team.settings && u !== "") { userAPI - .getUser(u, teamScope(team.settings?.key)) + .getUser(u, teamScope(team.settings.key)) .then(resp => { - newTeamPerms[index] = resp.permissions; - newTeamPrefs[index] = resp.preferences; + newTeamPerms.set(t.key(), resp.permissions) + newTeamPrefs.set(t.key(), resp.preferences) }) .finally(() => { - if (newTeamPerms[index]) setTeamPerms([...newTeamPerms]); - if (newTeamPrefs[index]) setTeamPrefs(newTeamPrefs); + setTeamPerms(newTeamPerms); + setTeamPrefs(newTeamPrefs); }); } + newTeams.push(Team.create(team)) }); - setTeams(resp.data.teams); + setTeams(newTeams); }) .finally(() => { setLoading(false); @@ -154,49 +110,58 @@ export default function TeamList() { loadTeams(); }, [user, teamAPI, userAPI, limit, page, search, asUser, loadTeams]); - const drawTeams = () => { - return ( - - {teams.map((team, index) => { - let permissions = teamPerms[index]; + const columns = (): Column[] => { + return [ + { + title: "", + render: team => { + let permissions = teamPerms.get(team.settings?.key); if (!permissions) { permissions = []; } - let preferences = teamPrefs[index]; + let preferences = teamPrefs.get(team.id()); if (!preferences) { preferences = pond.TeamPreferences.create(); } return ( - - { - if (preferences) { - let newTeamPrefs = cloneDeep(teamPrefs); - newTeamPrefs[index].notify = !newTeamPrefs[index].notify; - teamAPI - .updatePreferences( - Team.create(team).key(), - newTeamPrefs[index], - getContextKeys(), - getContextTypes() - ) - .then(() => { - setTeamPrefs(newTeamPrefs); - }); - } - }} - /> - } + { + if (preferences) { + let newTeamPrefs = cloneDeep(teamPrefs); + let prefs = newTeamPrefs.get(team.id()) ?? pond.TeamPreferences.create() + console.log(prefs) + prefs.notify = !newTeamPrefs.get(team.id())?.notify + newTeamPrefs.set(team.id(), prefs) + teamAPI + .updatePreferences( + team.key(), + prefs, + getContextKeys(), + getContextTypes() + ) + .then(() => { + setTeamPrefs(newTeamPrefs); + }); + } + }} + /> + } + > + { + if (team.settings) history(team.settings.key); + }} > - + { if (team.settings) history(team.settings.key); @@ -208,152 +173,73 @@ export default function TeamList() { { - if (team.settings) history(team.settings.key); - }}> + {team.settings?.name} - + } secondary={ - - + !isMobile && + {team.settings?.info} } /> - - - - ); - })} - - ); - }; - - const showProgress = () => { - return ( -
- -
- ); - }; - - const firstPage = () => { - setPage(0); - }; - - const lastPage = () => { - setPage(Math.round(total / limit)); - }; - - const nextPage = () => { - if (limit * (page + 1) < total) setPage(page + 1); - }; - - const prevPage = () => { - if (page > 0) setPage(page - 1); - }; + +
+ ) + } + } + ] + } const handleChange = (event: any) => { setLimit(event.target.value); }; - const showPageButtons = () => { - return ( -
- Teams per page: - - {limits.map(option => ( - - {option.label} - - ))} - - - - - - - - - {limit * page + 1} ‐ {limit * page + limit <= limit ? limit * page + limit : limit + 1} of{" "} - {total} - - = total} - size="small" - color="inherit" - onClick={nextPage}> - - - = total} - size="small" - color="inherit" - onClick={lastPage}> - - -
- ); - }; + const renderGutter = (team: Team) => { + return( + + {team.settings.info} + + ) + } - return ( - -
- - {isUser ? user.name() + "'s " : "Someone's "} Teams + const renderTitle = () => { + return ( + + + Teams setTeamDialogOpen(true)} color="primary" className={classes.button}> - setSearch(event?.target.value)} - InputProps={{ - startAdornment: ( - - - - ) - }}> -
- - {isUser ? "Viewing all teams that you are a part of" : "These are someone else's teams"} - - {!loading ? drawTeams() : showProgress()} - {showPageButtons()} - setTeamDialogOpen(false)} - refreshCallback={loadTeams} - /> -
- ); + + ) + } + + return ( + <> + + 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 + /> + setTeamDialogOpen(false)} + refreshCallback={loadTeams} + /> + + ) + } diff --git a/src/teams/TeamSettings.tsx b/src/teams/TeamSettings.tsx index 60925fd..8f79391 100644 --- a/src/teams/TeamSettings.tsx +++ b/src/teams/TeamSettings.tsx @@ -247,13 +247,15 @@ export default function TeamSettings(props: Props) { )}