diff --git a/Client/src/Components/TabPanels/Account/TeamPanel.jsx b/Client/src/Components/TabPanels/Account/TeamPanel.jsx index cc717485a..eec8fae28 100644 --- a/Client/src/Components/TabPanels/Account/TeamPanel.jsx +++ b/Client/src/Components/TabPanels/Account/TeamPanel.jsx @@ -2,24 +2,19 @@ import { useTheme } from "@emotion/react"; import TabPanel from "@mui/lab/TabPanel"; import { Box, - Checkbox, - Container, + ButtonGroup, Divider, + IconButton, MenuItem, Modal, Select, Stack, - Table, - TableBody, - TableCell, - TableHead, - TableRow, TextField, Typography, } from "@mui/material"; import ButtonSpinner from "../../ButtonSpinner"; import Button from "../../Button"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import EditSvg from "../../../assets/icons/edit.svg?react"; import Field from "../../Inputs/Field"; import { credentials } from "../../../Validation/validation"; @@ -27,6 +22,8 @@ import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; import axiosInstance from "../../../Utils/axiosConfig"; import { createToast } from "../../../Utils/toastUtils"; import { useSelector } from "react-redux"; +import BasicTable from "../../BasicTable"; +import Remove from "../../../assets/icons/trash-bin.svg?react"; /** * TeamPanel component manages the organization and team members, @@ -36,138 +33,114 @@ import { useSelector } from "react-redux"; * @returns {JSX.Element} */ -const teamColumns = [ - { - id: "checkbox", - label: "", - sx: { minWidth: "20px", width: "40px" }, - }, - { - id: "name", - label: "NAME", - sx: { fontSize: "12px" }, - }, - { id: "email", label: "EMAIL", sx: { fontSize: "12px" } }, - { id: "role", label: "ROLE", sx: { fontSize: "12px" } }, -]; -//for testing, will be removed later -const teamConfig = [ - { - id: 0, - isChecked: false, - name: "John Connor", - email: "john@domain.com", - type: "admin", - role: "Administrator", - createdAt: "10/4/2022", - }, - { - id: 1, - isChecked: false, - name: "Adam McFadden", - email: "adam@domain.com", - type: "member", - role: "Member", - createdAt: "10/4/2022", - }, - { - id: 2, - isChecked: false, - name: "Cris Cross", - email: "cris@domain.com", - type: "member", - role: "Member", - createdAt: "10/4/2022", - }, - { - id: 3, - isChecked: false, - name: "Prince", - email: "prince@domain.com", - type: "member", - role: "Member", - createdAt: "10/4/2022", - }, -]; -const actionsConfig = [ - { - value: "bulk", - label: "Bulk actions", - }, -]; -const roleConfig = [ - { - value: "role", - label: "Change role to", - }, -]; - const TeamPanel = () => { const theme = useTheme(); - //TODO - use redux loading state - //!! - currently all loading buttons are tied to the same state - const [isLoading, setIsLoading] = useState(false); - //TODO - connect to redux + + const { authToken } = useSelector((state) => state.auth); + //TODO const [orgStates, setOrgStates] = useState({ name: "Bluewave Labs", - isLoading: false, isEdit: false, }); + const [toInvite, setToInvite] = useState({ + email: "", + role: "", + }); + const [tableData, setTableData] = useState({}); + const [members, setMembers] = useState([]); + const [filter, setFilter] = useState("all"); + const [errors, setErrors] = useState({}); + useEffect(() => { + const fetchTeam = async () => { + try { + const response = await axiosInstance.get("/auth/users", { + headers: { Authorization: `Bearer ${authToken}` }, + }); + + setMembers(response.data.data); + } catch (error) { + createToast({ + body: error.message || "Error fetching team members.", + }); + } + }; + + fetchTeam(); + }, []); + + useEffect(() => { + let team = members; + if (filter !== "all") + team = members.filter((member) => member.role[0] === filter); + + const data = { + cols: [ + { id: 1, name: "NAME" }, + { id: 2, name: "EMAIL" }, + { id: 3, name: "ROLE" }, + { id: 4, name: "ACTION" }, + ], + rows: team?.map((member, idx) => { + return { + id: member._id, + data: [ + { + id: idx, + data: ( + + + {member.firstName + " " + member.lastName} + + + Created {new Date(member.createdAt).toLocaleDateString()} + + + ), + }, + { id: idx + 1, data: member.email }, + { + // TODO - Add select dropdown + id: idx + 2, + data: member.role[0] === "admin" ? "Administrator" : "Member", + }, + { + // TODO - Add delete onClick + id: idx + 3, + data: ( + + + + ), + }, + ], + }; + }), + }; + + setTableData(data); + }, [members, filter]); + + // RENAME ORGANIZATION const toggleEdit = () => { setOrgStates((prev) => ({ ...prev, isEdit: !prev.isEdit })); }; const handleRename = () => {}; - const [teamStates, setTeamStates] = useState({ - members: teamConfig, - filter: "", - }); - const handleCheckCell = (id) => { - const updatedTeamStates = [...teamStates.members]; - updatedTeamStates[id] = { - ...updatedTeamStates[id], - isChecked: !updatedTeamStates[id].isChecked, - }; - setTeamStates((prev) => ({ - ...prev, - members: updatedTeamStates, - })); - }; - const handleFilter = (filter) => { - setTeamStates((prev) => ({ - ...prev, - filter: filter, - })); - }; - //TODO - implement select action function - const handleSelectActionType = () => { - setIsLoading(true); - setTimeout(() => { - setIsLoading(false); - }, 2000); - }; - //TODO - implement select role function - const handleSelectRoleType = () => { - setIsLoading(true); - setTimeout(() => { - setIsLoading(false); - }, 2000); - }; //TODO - implement save team function - const handleSaveTeam = () => { - setIsLoading(true); - setTimeout(() => { - setIsLoading(false); - }, 2000); - }; + const handleSaveTeam = () => {}; - const { authToken } = useSelector((state) => state.auth); - const [toInvite, setToInvite] = useState({ - email: "", - role: "", - }); - const [errors, setErrors] = useState({}); + // INVITE MEMBER + const [isOpen, setIsOpen] = useState(false); const handleChange = (event) => { const { value } = event.target; @@ -192,7 +165,7 @@ const TeamPanel = () => { return updatedErrors; }); }; - const [isOpen, setIsOpen] = useState(false); + const handleInviteMember = async () => { if (toInvite.role !== "user" || toInvite !== "admin") setToInvite((prev) => ({ ...prev, role: "user" })); @@ -233,97 +206,97 @@ const TeamPanel = () => { setErrors({}); }; - const handleMembersQuery = (type) => { - let count = 0; - teamStates.members.forEach((member) => { - type === "" ? count++ : member.type === type ? count++ : ""; - }); - return count; - }; return ( - - - Organization name - - - - setOrgStates((prev) => ({ - ...prev, - name: event.target.value, - })) - } - disabled={!orgStates.isEdit} - sx={{ - color: theme.palette.otherColors.bluishGray, - "& .Mui-disabled": { - WebkitTextFillColor: "initial !important", - }, - "& .Mui-disabled fieldset": { - borderColor: "transparent !important", - }, - }} - inputProps={{ - sx: { textAlign: "end", padding: theme.gap.small }, - }} - /> - : ""} - onClick={() => toggleEdit()} - sx={{ - minWidth: 0, - paddingX: theme.gap.small, - ml: orgStates.isEdit ? theme.gap.small : 0, - }} - /> - - - + + Organization name + + + + setOrgStates((prev) => ({ + ...prev, + name: event.target.value, + })) + } + disabled={!orgStates.isEdit} + sx={{ + color: theme.palette.otherColors.bluishGray, + "& .Mui-disabled": { + WebkitTextFillColor: "initial !important", + }, + "& .Mui-disabled fieldset": { + borderColor: "transparent !important", + }, + }} + inputProps={{ + sx: { textAlign: "end", padding: theme.gap.small }, + }} + /> + : ""} + onClick={() => toggleEdit()} + sx={{ + minWidth: 0, + paddingX: theme.gap.small, + ml: orgStates.isEdit ? theme.gap.small : 0, + }} + /> + - - - Team members - - + + + Team members + - handleFilter("")}> - All - - {handleMembersQuery("")} - - - handleFilter("admin")}> - Administrator - - {handleMembersQuery("admin")} - - - handleFilter("member")}> - Member - - {handleMembersQuery("member")} - - + + setFilter("all")} + sx={{ backgroundColor: filter === "all" && "#f4f4f4" }} + /> + setFilter("admin")} + sx={{ backgroundColor: filter === "admin" && "#f4f4f4" }} + /> + setFilter("user")} + sx={{ backgroundColor: filter === "user" && "#f4f4f4" }} + /> + { sx={{ paddingX: "30px" }} onClick={() => setIsOpen(true)} /> - - - - - - - {actionsConfig.map((action) => ( - - {action.label} - - ))} - - - - - - {roleConfig.map((role) => ( - - {role.label} - - ))} - - - - - - - - {teamColumns.map((cell) => ( - - {cell.label} - - ))} - - - - {teamStates.members.map((cell) => - teamStates.filter === "" || - teamStates.filter === cell.type ? ( - - - handleCheckCell(cell.id)} - inputProps={{ "aria-label": "controlled" }} - /> - - - - - {cell.name} - - Created at {cell.createdAt} - - - {cell.email} - {cell.role} - - ) : ( - "" - ) - )} - - - - - + + { level="primary" label="Send invite" onClick={handleInviteMember} - isLoading={isLoading} + isLoading={false} disabled={Object.keys(errors).length !== 0} /> diff --git a/Client/src/Pages/Account/index.css b/Client/src/Pages/Account/index.css index fef2e5b49..beb655815 100644 --- a/Client/src/Pages/Account/index.css +++ b/Client/src/Pages/Account/index.css @@ -28,8 +28,8 @@ margin: 50px 0; } .account .MuiDivider-root, -.account .MuiOutlinedInput-root fieldset { - border-color: var(--env-var-color-29); +.account .MuiButtonGroup-root button { + border-color: var(--env-var-color-16); } .account [class$="-form"] { width: inherit; @@ -42,8 +42,7 @@ align-items: flex-start; } .edit-profile-form__wrapper > .MuiStack-root:not(.row-stack), -.edit-password-form__wrapper > .MuiStack-root:not(.row-stack), -.edit-organization-form__wrapper > .MuiStack-root:not(.row-stack) { +.edit-password-form__wrapper > .MuiStack-root:not(.row-stack) { flex-direction: column; gap: 8px; margin-right: 10px; @@ -68,21 +67,6 @@ align-items: center; } -.edit-team-form__wrapper td { - padding: 10px 0; - padding-top: 20px; - vertical-align: text-top; -} -.edit-team-form__wrapper th { - padding: var(--env-var-spacing-1) 0; -} -.edit-team-form__wrapper th:first-of-type { - padding-left: 20px; -} -.edit-team-form__wrapper td { - color: var(--env-var-color-25); -} - #select-actions, #select-role { padding: 5px 0; @@ -101,24 +85,6 @@ border-color: var(--env-var-color-2); } -.MuiStack-root:has(.MuiBox-root > .members-query) .MuiBox-root { - cursor: pointer; -} -.members-query { - display: inline-flex; - justify-content: center; - align-items: center; - margin-left: 6px; - padding: 2px; - font-size: 12px; - font-weight: 500; - color: var(--env-var-color-3); - background-color: #e0e9fd; - min-width: 22px; - min-height: 22px; - border-radius: 50%; -} - /* for testing, will remove later */ @media only screen and (max-width: 1600px) { .edit-profile-form__wrapper .MuiStack-root:not(.row-stack), @@ -131,22 +97,10 @@ .account .MuiStack-root:has(span.MuiTypography-root.input-error) { position: relative; } -.account .text-field-icon { - position: absolute; - top: 50%; - transform: translateY(-50%); - width: 18px !important; - height: 18px !important; - opacity: 0.8; -} .account:not(:has(#modal-invite-member)) span.MuiTypography-root.input-error { position: absolute; top: 100%; } -.account .email-text-field, -.account .password-text-field { - margin: 0; -} .edit-profile-form__wrapper .MuiFormControl-root, .edit-password-form__wrapper .MuiFormControl-root { @@ -173,3 +127,14 @@ .MuiInputBase-root:not(.Mui-focused):has(#team-member-role):hover fieldset { border-color: var(--env-var-color-29); } + +.edit-organization-form { + display: flex; + align-items: center; +} +.edit-team-form .MuiTableCell-root { + color: var(--env-var-color-25) !important; +} +.edit-team-form .MuiTableBody-root .MuiTableCell-root { + padding: var(--env-var-spacing-1-plus) var(--env-var-spacing-2) !important; +} \ No newline at end of file diff --git a/Client/src/Pages/Account/index.jsx b/Client/src/Pages/Account/index.jsx index 2bb4dc018..391cf3940 100644 --- a/Client/src/Pages/Account/index.jsx +++ b/Client/src/Pages/Account/index.jsx @@ -38,7 +38,7 @@ const Account = ({ open = "profile" }) => { diff --git a/Client/src/Pages/Monitors/index.jsx b/Client/src/Pages/Monitors/index.jsx index e08e973e1..a76ccd14e 100644 --- a/Client/src/Pages/Monitors/index.jsx +++ b/Client/src/Pages/Monitors/index.jsx @@ -130,7 +130,7 @@ const Monitors = () => { > - Hello, {authState.user.firstname} + Hello, {authState.user.firstName} + +