From 9ca79ea793e434530c5557de0bb8ddfe5b8a05dd Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Sun, 8 Feb 2026 16:14:35 +0000 Subject: [PATCH] team initial commit --- client/src/Pages/Account/TabPassword.tsx | 2 +- client/src/Pages/Account/TabTeam.tsx | 30 +++++++- .../Account/components/HeaderTeamControls.tsx | 74 +++++++++++++++++++ .../Pages/Account/components/TeamPanel.jsx | 7 -- .../Pages/Account/components/TeamTable.tsx | 53 +++++++++++++ client/src/locales/en.json | 24 ++++++ 6 files changed, 180 insertions(+), 10 deletions(-) create mode 100644 client/src/Pages/Account/components/HeaderTeamControls.tsx create mode 100644 client/src/Pages/Account/components/TeamTable.tsx diff --git a/client/src/Pages/Account/TabPassword.tsx b/client/src/Pages/Account/TabPassword.tsx index 6adcb99c4..85e13fb7e 100644 --- a/client/src/Pages/Account/TabPassword.tsx +++ b/client/src/Pages/Account/TabPassword.tsx @@ -15,7 +15,7 @@ export const TabPassword = () => { const { resolver, defaults } = usePasswordForm(); const { patch, loading } = usePatch(); - const { control, handleSubmit, reset, setError } = useForm({ + const { control, handleSubmit, reset } = useForm({ resolver, defaultValues: defaults, }); diff --git a/client/src/Pages/Account/TabTeam.tsx b/client/src/Pages/Account/TabTeam.tsx index 0babcad86..48a78033c 100644 --- a/client/src/Pages/Account/TabTeam.tsx +++ b/client/src/Pages/Account/TabTeam.tsx @@ -1,5 +1,31 @@ -import { Box } from "@mui/material"; +import { Stack } from "@mui/material"; +import { useTheme } from "@mui/material"; +import { useState, useMemo } from "react"; +import { HeaderTeamControls } from "./components/HeaderTeamControls"; +import { TeamTable } from "./components/TeamTable"; +import { useGet } from "@/Hooks/UseApi"; +import type { User, UserRole } from "@/Types/User"; export const TabTeam = () => { - return {/* TODO: Implement TabTeam */}; + const theme = useTheme(); + const [filter, setFilter] = useState(""); + + const { data: users } = useGet("/auth/users"); + + const filteredUsers = useMemo(() => { + if (!users) return []; + if (!filter) return users; + + return users.filter((u) => u.role.includes(filter)); + }, [users, filter]); + + return ( + + + + + ); }; diff --git a/client/src/Pages/Account/components/HeaderTeamControls.tsx b/client/src/Pages/Account/components/HeaderTeamControls.tsx new file mode 100644 index 000000000..807a153e4 --- /dev/null +++ b/client/src/Pages/Account/components/HeaderTeamControls.tsx @@ -0,0 +1,74 @@ +import Stack from "@mui/material/Stack"; +import MenuItem from "@mui/material/MenuItem"; +import { Button, Select } from "@/Components/v2/inputs"; +import { Icon } from "@/Components/v2/design-elements"; +import { UserPlus, Mail } from "lucide-react"; + +import { UserRoles } from "@/Types/User"; +import { useTheme } from "@mui/material/styles"; +import type { UserRole } from "@/Types/User"; +import { useTranslation } from "react-i18next"; +import { useIsAdmin, useIsSuperAdmin } from "@/Hooks/useIsAdmin"; + +interface HeaderTeamControlsProps { + filter: UserRole | ""; + onFilterChange: (value: UserRole | "") => void; +} + +export const HeaderTeamControls = ({ + filter, + onFilterChange, +}: HeaderTeamControlsProps) => { + const theme = useTheme(); + const { t } = useTranslation(); + const isAdmin = useIsAdmin(); + const isSuperAdmin = useIsSuperAdmin(); + + const handleFilterChange = (event: { target: { value: unknown } }) => { + onFilterChange(event.target.value as UserRole | ""); + }; + + return ( + + + {isAdmin && ( + + )} + {isSuperAdmin && ( + + )} + + ); +}; diff --git a/client/src/Pages/Account/components/TeamPanel.jsx b/client/src/Pages/Account/components/TeamPanel.jsx index 6c658cc60..f7892bd01 100644 --- a/client/src/Pages/Account/components/TeamPanel.jsx +++ b/client/src/Pages/Account/components/TeamPanel.jsx @@ -15,13 +15,6 @@ import { useGetInviteToken } from "../../../Hooks/inviteHooks.js"; import { useNavigate } from "react-router-dom"; import { useIsSuperAdmin } from "@/Hooks/useIsAdmin.js"; import AddMemberMenu from "./AddMemberMenu/index.jsx"; -/** - * TeamPanel component manages the organization and team members, - * providing functionalities like renaming the organization, managing team members, - * and inviting new members. - * - * @returns {JSX.Element} - */ const TeamPanel = () => { const theme = useTheme(); diff --git a/client/src/Pages/Account/components/TeamTable.tsx b/client/src/Pages/Account/components/TeamTable.tsx new file mode 100644 index 000000000..5c2455bec --- /dev/null +++ b/client/src/Pages/Account/components/TeamTable.tsx @@ -0,0 +1,53 @@ +import Typography from "@mui/material/Typography"; +import { useNavigate } from "react-router-dom"; +import { useTranslation } from "react-i18next"; +import { Table } from "@/Components/v2/design-elements"; +import type { Header } from "@/Components/v2/design-elements/Table"; +import { useIsSuperAdmin } from "@/Hooks/useIsAdmin"; +import type { User } from "@/Types/User"; + +interface TeamTableProps { + users: User[]; +} + +export const TeamTable = ({ users }: TeamTableProps) => { + const { t } = useTranslation(); + const navigate = useNavigate(); + const isSuperAdmin = useIsSuperAdmin(); + + const headers: Header[] = [ + { + id: "name", + content: t("common.table.headers.name"), + render: (row) => {`${row.firstName} ${row.lastName}`}, + }, + { + id: "email", + content: t("pages.account.team.table.headers.email"), + render: (row) => {row.email}, + }, + { + id: "role", + content: t("pages.account.team.table.headers.role"), + render: (row) => ( + + {row.role.map((r) => t(`common.auth.roles.${r}`)).join(", ")} + + ), + }, + ]; + + const handleRowClick = (row: User) => { + if (isSuperAdmin) { + navigate(`/account/team/${row.id}`); + } + }; + + return ( + + ); +}; diff --git a/client/src/locales/en.json b/client/src/locales/en.json index 451c59e17..45b329ea0 100644 --- a/client/src/locales/en.json +++ b/client/src/locales/en.json @@ -176,6 +176,14 @@ "ClickUpload": "Click to upload", "close": "Close", "common": { + "auth": { + "roles": { + "admin": "Admin", + "demo": "Demo", + "superadmin": "Super admin", + "user": "User" + } + }, "alerts": { "pageSpeedApiKey": { "content": "Warning: You haven't added a Google PageSpeed API key yet. Visit Settings to add one. Without it, the PageSpeed monitor won't function." @@ -549,6 +557,22 @@ "title": "Delete account", "description": "This action is permanent and cannot be undone" } + }, + "team": { + "addMember": "Add member", + "inviteMember": "Invite member", + "filter": { + "placeholder": "Filter by role", + "all": "All roles", + "admin": "Admin", + "member": "Member" + }, + "table": { + "headers": { + "email": "Email", + "role": "Role" + } + } } }, "auth": {