From 87a66e9630026e06c2dc54f67440df80878a99a7 Mon Sep 17 00:00:00 2001 From: kimenyikevin <52419102+kimenyikevin@users.noreply.github.com> Date: Tue, 15 Mar 2022 15:50:20 +0200 Subject: [PATCH] Add sorting to admin panel tables (#5461) * implemet logout for admin system * implement logout on admin dashboard * implement filter for user table * add sorting to tables * add user role filter * fix type error * fix project sorting * fix icon style * fix admin nav icon * fix /admin/location alter message * fix drawer style * implemet logout for admin system * implement logout on admin dashboard * implement filter for user table * add sorting to tables * add user role filter * fix type error * fix project sorting * fix icon style * fix admin nav icon * fix /admin/location alter message * fix drawer style * center align column headings in project route of admin panel * refactoring remove unwanted code * refactor admin code * fix update location issue Co-authored-by: Zulqarnain Hanif <30355034+zulqarnainhanif@users.noreply.github.com> --- .../client-core/src/admin/common/Table.tsx | 150 +++++++- .../src/admin/common/variables/projects.ts | 19 + .../src/admin/common/variables/route.ts | 4 +- .../src/admin/common/variables/user.ts | 28 ++ .../admin/components/Avatars/AvatarDetail.tsx | 2 +- .../admin/components/Group/CreateGroup.tsx | 1 - .../src/admin/components/Group/EditGroup.tsx | 1 - .../src/admin/components/Group/GroupTable.tsx | 2 - .../components/Instance/InstanceTable.tsx | 2 - .../admin/components/Invite/InviteModel.tsx | 4 +- .../components/Invite/ReceivedInvite.tsx | 2 - .../admin/components/Invite/SentInvite.tsx | 2 - .../components/Location/CreateLocation.tsx | 11 +- .../components/Location/LocationTable.tsx | 2 - .../components/Location/ViewLocation.tsx | 45 ++- .../src/admin/components/Party/PartyTable.tsx | 2 - .../admin/components/Project/ProjectTable.tsx | 232 ++++++++++++ .../components/Project/UploadProjectModal.tsx | 1 - .../src/admin/components/Project/index.tsx | 341 +----------------- .../admin/components/Routes/RouteTable.tsx | 2 +- .../admin/components/Setting/Chargebee.tsx | 2 - .../src/admin/components/Setting/index.tsx | 3 - .../admin/components/Users/CreateUserRole.tsx | 4 +- .../src/admin/components/Users/index.tsx | 118 +++++- .../client-core/src/admin/pages/bot/index.tsx | 4 +- .../client-core/src/admin/pages/party.tsx | 4 +- .../src/admin/services/AvatarService.ts | 2 +- .../src/admin/services/SceneService.ts | 2 +- .../src/admin/services/UserService.ts | 38 +- packages/client-core/src/admin/styles/ui.ts | 11 +- .../MediaInstanceConnectionService.ts | 4 +- .../src/components/MediaIconsBox/index.tsx | 10 +- .../PartyParticipantWindow/index.tsx | 2 +- .../src/social/services/LocationService.ts | 2 +- .../user/components/Dashboard/Dashboard.tsx | 2 +- .../components/Dashboard/styles.module.scss | 2 +- packages/common/src/interfaces/Location.ts | 4 +- 37 files changed, 625 insertions(+), 442 deletions(-) create mode 100644 packages/client-core/src/admin/common/variables/projects.ts create mode 100644 packages/client-core/src/admin/components/Project/ProjectTable.tsx diff --git a/packages/client-core/src/admin/common/Table.tsx b/packages/client-core/src/admin/common/Table.tsx index 5743947278..aea38a18d4 100644 --- a/packages/client-core/src/admin/common/Table.tsx +++ b/packages/client-core/src/admin/common/Table.tsx @@ -1,5 +1,6 @@ import React from 'react' +import Box from '@mui/material/Box' import Table from '@mui/material/Table' import TableBody from '@mui/material/TableBody' import TableCell from '@mui/material/TableCell' @@ -7,6 +8,8 @@ import TableContainer from '@mui/material/TableContainer' import TableHead from '@mui/material/TableHead' import TablePagination from '@mui/material/TablePagination' import TableRow from '@mui/material/TableRow' +import TableSortLabel from '@mui/material/TableSortLabel' +import { visuallyHidden } from '@mui/utils' import { useStyles } from '../styles/ui' @@ -19,30 +22,145 @@ interface Props { handlePageChange: (e: unknown, newPage: number) => void handleRowsPerPageChange: (e: React.ChangeEvent) => void } +type Order = 'asc' | 'desc' + +interface Data { + calories: number + carbs: number + fat: number + name: string + protein: number +} + +interface HeadCell { + disablePadding: boolean + id: keyof Data + label: string + align?: 'right' + minWidth: any +} + +function descendingComparator(a: T, b: T, orderBy: keyof T) { + if (b[orderBy] < a[orderBy]) { + return -1 + } + if (b[orderBy] > a[orderBy]) { + return 1 + } + return 0 +} + +function getComparator( + order: Order, + orderBy: Key +): (a: { [key in Key]: number | string }, b: { [key in Key]: number | string }) => number { + return order === 'desc' + ? (a, b) => descendingComparator(a, b, orderBy) + : (a, b) => -descendingComparator(a, b, orderBy) +} + +function stableSort(array: readonly T[], comparator: (a: T, b: T) => number) { + const stabilizedThis = array.map((el, index) => [el, index] as [T, number]) + stabilizedThis.sort((a, b) => { + const order = comparator(a[0], b[0]) + if (order !== 0) { + return order + } + return a[1] - b[1] + }) + return stabilizedThis.map((el) => el[0]) +} +interface EnhancedTableProps { + onRequestSort: (event: React.MouseEvent, property: keyof Data) => void + order: Order + orderBy: string + rowCount: number + columns: HeadCell[] +} + +const EnhancedTableHead = (props: EnhancedTableProps) => { + const { order, orderBy, onRequestSort, columns } = props + const classes = useStyles() + const createSortHandler = (property: keyof Data) => (event: React.MouseEvent) => { + onRequestSort(event, property) + } + + return ( + + + {columns.map((headCell) => ( + + {(headCell.id as any) === 'action' ? ( + {headCell.label} + ) : ( + + {headCell.label} + {orderBy === headCell.id ? ( + + {order === 'desc' ? 'sorted descending' : 'sorted ascending'} + + ) : null} + + )} + + ))} + + + ) +} const TableComponent = (props: Props) => { const classes = useStyles() const { rows, column, page, rowsPerPage, count, handlePageChange, handleRowsPerPageChange } = props + const [order, setOrder] = React.useState('asc') + const [orderBy, setOrderBy] = React.useState(column[0].id) + const handleRequestSort = (event: React.MouseEvent, property: keyof Data) => { + const isAsc = orderBy === property && order === 'asc' + setOrder(isAsc ? 'desc' : 'asc') + setOrderBy(property) + } + return ( - - - {column.map((column, index) => ( - - {column.label} - - ))} - - + - {rows.map((row, rIndex) => { + {stableSort(rows, getComparator(order, orderBy)) + .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) + .map((row, index) => { + return ( + + {column.map((column, index) => { + const value = row[column.id] + return ( + + {value} + + ) + })} + + ) + })} + {/* {rows.map((row, rIndex) => { return ( {column.map((column, index) => { @@ -55,7 +173,7 @@ const TableComponent = (props: Props) => { })} ) - })} + })} */}
diff --git a/packages/client-core/src/admin/common/variables/projects.ts b/packages/client-core/src/admin/common/variables/projects.ts new file mode 100644 index 0000000000..3fedbba5a7 --- /dev/null +++ b/packages/client-core/src/admin/common/variables/projects.ts @@ -0,0 +1,19 @@ +export interface ProjectColumn { + id: 'name' | 'update' | 'invalidate' | 'view' | 'action' + label: string + minWidth?: number + align?: 'right' | 'center' +} + +export const projectsColumns: ProjectColumn[] = [ + { id: 'name', label: 'name', minWidth: 65 }, + { id: 'update', label: 'Update', minWidth: 65, align: 'center' }, + { id: 'invalidate', label: 'Invalidate Cache', minWidth: 65, align: 'center' }, + { id: 'view', label: 'View Project Files', minWidth: 65, align: 'center' }, + { + id: 'action', + label: 'Remove', + minWidth: 65, + align: 'center' + } +] diff --git a/packages/client-core/src/admin/common/variables/route.ts b/packages/client-core/src/admin/common/variables/route.ts index 1dd0436a18..6f75dd139d 100644 --- a/packages/client-core/src/admin/common/variables/route.ts +++ b/packages/client-core/src/admin/common/variables/route.ts @@ -1,5 +1,5 @@ export interface RouteColumn { - id: 'project' | 'route' | 'active' + id: 'project' | 'route' | 'action' label: string minWidth?: number align?: 'right' @@ -9,7 +9,7 @@ export const routeColumns: RouteColumn[] = [ { id: 'project', label: 'Project', minWidth: 65 }, { id: 'route', label: 'Route', minWidth: 65 }, { - id: 'active', + id: 'action', label: 'Active', minWidth: 65, align: 'right' diff --git a/packages/client-core/src/admin/common/variables/user.ts b/packages/client-core/src/admin/common/variables/user.ts index 582a9f5486..e49b7ed415 100644 --- a/packages/client-core/src/admin/common/variables/user.ts +++ b/packages/client-core/src/admin/common/variables/user.ts @@ -65,3 +65,31 @@ export interface UserTabPanelProps { index: any value: any } + +export const userFilterMenu = { + elevation: 0, + sx: { + overflow: 'visible', + filter: 'drop-shadow(0px 2px 8px rgba(0,0,0,0.32))', + mt: 1.5, + '& .MuiAvatar-root': { + width: 32, + height: 32, + ml: -0.5, + mr: 1 + }, + bgcolor: 'rgb(58, 65, 73) !important', + '&:before': { + content: '""', + display: 'block', + position: 'absolute', + top: 0, + right: 14, + width: 10, + height: 10, + bgcolor: 'rgb(58, 65, 73) !important', + transform: 'translateY(-50%) rotate(45deg)', + zIndex: 0 + } + } +} diff --git a/packages/client-core/src/admin/components/Avatars/AvatarDetail.tsx b/packages/client-core/src/admin/components/Avatars/AvatarDetail.tsx index ab8b82de5e..36c0c19347 100644 --- a/packages/client-core/src/admin/components/Avatars/AvatarDetail.tsx +++ b/packages/client-core/src/admin/components/Avatars/AvatarDetail.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react' +import React from 'react' import Grid from '@mui/material/Grid' import Typography from '@mui/material/Typography' diff --git a/packages/client-core/src/admin/components/Group/CreateGroup.tsx b/packages/client-core/src/admin/components/Group/CreateGroup.tsx index 790ed687d9..47b3987b86 100644 --- a/packages/client-core/src/admin/components/Group/CreateGroup.tsx +++ b/packages/client-core/src/admin/components/Group/CreateGroup.tsx @@ -34,7 +34,6 @@ const CreateGroup = (props: Props) => { const classes = useStyles() const user = useAuthState().user const adminScopeTypeState = useScopeTypeState() - const adminScopeTypes = adminScopeTypeState.scopeTypes const { t } = useTranslation() const [state, setState] = useState({ diff --git a/packages/client-core/src/admin/components/Group/EditGroup.tsx b/packages/client-core/src/admin/components/Group/EditGroup.tsx index 4f06c5974e..7de7ba8015 100644 --- a/packages/client-core/src/admin/components/Group/EditGroup.tsx +++ b/packages/client-core/src/admin/components/Group/EditGroup.tsx @@ -32,7 +32,6 @@ const EditGroup = (props: Props) => { const { groupAdmin, closeEditModal, closeViewModal } = props const user = useAuthState().user const adminScopeTypeState = useScopeTypeState() - const adminScopeTypes = adminScopeTypeState.scopeTypes const { t } = useTranslation() const [state, setState] = useState({ diff --git a/packages/client-core/src/admin/components/Group/GroupTable.tsx b/packages/client-core/src/admin/components/Group/GroupTable.tsx index 35ff9ffcfe..79b7f6922a 100644 --- a/packages/client-core/src/admin/components/Group/GroupTable.tsx +++ b/packages/client-core/src/admin/components/Group/GroupTable.tsx @@ -3,7 +3,6 @@ import { useTranslation } from 'react-i18next' import { Group } from '@xrengine/common/src/interfaces/Group' -import { useDispatch } from '../../../store' import { useAuthState } from '../../../user/services/AuthService' import ConfirmModel from '../../common/ConfirmModel' import TableComponent from '../../common/Table' @@ -18,7 +17,6 @@ interface Props { const GroupTable = (props: Props) => { const { search } = props - const dispatch = useDispatch() const classes = useStyles() const user = useAuthState().user const [viewModel, setViewModel] = useState(false) diff --git a/packages/client-core/src/admin/components/Instance/InstanceTable.tsx b/packages/client-core/src/admin/components/Instance/InstanceTable.tsx index 67030437b4..1e9b13ffb4 100644 --- a/packages/client-core/src/admin/components/Instance/InstanceTable.tsx +++ b/packages/client-core/src/admin/components/Instance/InstanceTable.tsx @@ -4,7 +4,6 @@ import { useTranslation } from 'react-i18next' import { Instance } from '@xrengine/common/src/interfaces/Instance' import { Location } from '@xrengine/common/src/interfaces/Location' -import { useDispatch } from '../../../store' import { useAuthState } from '../../../user/services/AuthService' import ConfirmModel from '../../common/ConfirmModel' import TableComponent from '../../common/Table' @@ -26,7 +25,6 @@ interface Props { */ const InstanceTable = (props: Props) => { const { search } = props - const dispatch = useDispatch() const classes = useStyles() const [page, setPage] = React.useState(0) const [rowsPerPage, setRowsPerPage] = React.useState(INSTNCE_PAGE_LIMIT) diff --git a/packages/client-core/src/admin/components/Invite/InviteModel.tsx b/packages/client-core/src/admin/components/Invite/InviteModel.tsx index 21c4817a6c..07d874a52b 100755 --- a/packages/client-core/src/admin/components/Invite/InviteModel.tsx +++ b/packages/client-core/src/admin/components/Invite/InviteModel.tsx @@ -22,7 +22,6 @@ import makeStyles from '@mui/styles/makeStyles' import { InviteService } from '../../../social/services/InviteService' import { InviteTypeService } from '../../../social/services/InviteTypeService' import { useInviteTypeState } from '../../../social/services/InviteTypeService' -import { useDispatch } from '../../../store' import styles from '../Admin.module.scss' interface Props { @@ -103,8 +102,7 @@ const InviteModel = (props: Props) => { // const [openInvite ,setOpenInvite] = React.useState(false); const [openWarning, setOpenWarning] = React.useState(false) const { t } = useTranslation() - const [error, setError] = React.useState('') - const dispatch = useDispatch() + const handleCloseWarning = (event, reason) => { if (reason === 'clickaway') { return diff --git a/packages/client-core/src/admin/components/Invite/ReceivedInvite.tsx b/packages/client-core/src/admin/components/Invite/ReceivedInvite.tsx index bde5f4693c..415569dbaa 100755 --- a/packages/client-core/src/admin/components/Invite/ReceivedInvite.tsx +++ b/packages/client-core/src/admin/components/Invite/ReceivedInvite.tsx @@ -21,7 +21,6 @@ import makeStyles from '@mui/styles/makeStyles' import { InviteService } from '../../../social/services/InviteService' import { INVITE_PAGE_LIMIT, useInviteState } from '../../../social/services/InviteService' -import { useDispatch } from '../../../store' interface Props { invites: any @@ -111,7 +110,6 @@ const ReceivedInvite = (props: Props) => { const { invites } = props const [page, setPage] = React.useState(0) const [rowsPerPage, setRowsPerPage] = React.useState(INVITE_PAGE_LIMIT) - const dispatch = useDispatch() const inviteState = useInviteState() const receivedInviteCount = inviteState.receivedInvites.total.value const rows = invites.map((el, index) => createData(el.id, el.user.name, el.passcode, el.inviteType)) diff --git a/packages/client-core/src/admin/components/Invite/SentInvite.tsx b/packages/client-core/src/admin/components/Invite/SentInvite.tsx index f0ab87a750..1b2af1a83f 100755 --- a/packages/client-core/src/admin/components/Invite/SentInvite.tsx +++ b/packages/client-core/src/admin/components/Invite/SentInvite.tsx @@ -23,7 +23,6 @@ import makeStyles from '@mui/styles/makeStyles' import { InviteService } from '../../../social/services/InviteService' import { useInviteState } from '../../../social/services/InviteService' import { INVITE_PAGE_LIMIT } from '../../../social/services/InviteService' -import { useDispatch } from '../../../store' interface Props { sentInvites?: any @@ -116,7 +115,6 @@ const SentInvite = (props: Props) => { const { invites } = props const [page, setPage] = React.useState(0) const [rowsPerPage, setRowsPerPage] = React.useState(INVITE_PAGE_LIMIT) - const dispatch = useDispatch() const inviteState = useInviteState() const { t } = useTranslation() const sentInviteCount = inviteState.sentInvites.total.value diff --git a/packages/client-core/src/admin/components/Location/CreateLocation.tsx b/packages/client-core/src/admin/components/Location/CreateLocation.tsx index f024f65096..a4f53d7c08 100644 --- a/packages/client-core/src/admin/components/Location/CreateLocation.tsx +++ b/packages/client-core/src/admin/components/Location/CreateLocation.tsx @@ -151,7 +151,14 @@ const CreateLocation = (props: Props) => { return ( - handleClose(false)}> + { + closeViewModel && closeViewModel(false) + }} + > {t('admin:components.locationModel.createNewLocation')} @@ -364,9 +371,9 @@ const CreateLocation = (props: Props) => { {t('admin:components.locationModel.lbl-cancel')} - + ) } diff --git a/packages/client-core/src/admin/components/Location/LocationTable.tsx b/packages/client-core/src/admin/components/Location/LocationTable.tsx index 58b4365162..2a45043163 100644 --- a/packages/client-core/src/admin/components/Location/LocationTable.tsx +++ b/packages/client-core/src/admin/components/Location/LocationTable.tsx @@ -7,7 +7,6 @@ import Avatar from '@mui/material/Avatar' import Chip from '@mui/material/Chip' import { useErrorState } from '../../../common/services/ErrorService' -import { useDispatch } from '../../../store' import { useAuthState } from '../../../user/services/AuthService' import ConfirmModel from '../../common/ConfirmModel' import { useFetchAdminInstance } from '../../common/hooks/Instance.hooks' @@ -34,7 +33,6 @@ const LocationTable = (props: LocationProps) => { const [locationName, setLocationName] = React.useState('') const [viewModel, setViewModel] = React.useState(false) const [locationAdmin, setLocationAdmin] = React.useState() - const dispatch = useDispatch() const authState = useAuthState() const user = authState.user const adminScopeReadErrMsg = useErrorState().readError.scopeErrorMessage diff --git a/packages/client-core/src/admin/components/Location/ViewLocation.tsx b/packages/client-core/src/admin/components/Location/ViewLocation.tsx index 88481b8da2..6e3ebd0b82 100644 --- a/packages/client-core/src/admin/components/Location/ViewLocation.tsx +++ b/packages/client-core/src/admin/components/Location/ViewLocation.tsx @@ -1,10 +1,9 @@ -import React from 'react' +import React, { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { Location } from '@xrengine/common/src/interfaces/Location' import { Save } from '@mui/icons-material' -import MuiAlert from '@mui/material/Alert' import Avatar from '@mui/material/Avatar' import Button from '@mui/material/Button' import Chip from '@mui/material/Chip' @@ -22,7 +21,6 @@ import Select from '@mui/material/Select' import Switch from '@mui/material/Switch' import Typography from '@mui/material/Typography' -import { useDispatch } from '../../../store' import { useAuthState } from '../../../user/services/AuthService' import AlertMessage from '../../common/AlertMessage' import { validateForm } from '../../common/validation/formValidation' @@ -36,16 +34,11 @@ interface Props { locationAdmin?: Location } -const Alert = (props) => { - return -} - const ViewLocation = (props: Props) => { const { openView, closeViewModel, locationAdmin } = props - const dispatch = useDispatch() const classes = useStyles() - const [editMode, setEditMode] = React.useState(false) - const [state, setState] = React.useState({ + const [editMode, setEditMode] = useState(false) + const [state, setState] = useState({ name: '', maxUsers: 10, scene: '', @@ -64,9 +57,9 @@ const ViewLocation = (props: Props) => { type: '' } }) - const [location, setLocation] = React.useState('') - const [error, setError] = React.useState('') - const [openWarning, setOpenWarning] = React.useState(false) + const [location, setLocation] = useState('') + const [error, setError] = useState('') + const [openWarning, setOpenWarning] = useState(false) const { t } = useTranslation() const adminScenes = useSceneState().scenes const locationTypes = useLocationState().locationTypes @@ -81,7 +74,7 @@ const ViewLocation = (props: Props) => { } } - React.useEffect(() => { + useEffect(() => { if (locationAdmin) { setLocation(locationAdmin) setState({ @@ -89,12 +82,12 @@ const ViewLocation = (props: Props) => { name: locationAdmin.name, maxUsers: locationAdmin.maxUsersPerInstance, scene: locationAdmin.sceneId, - type: locationAdmin.locationSettings.locationType, - videoEnabled: locationAdmin.locationSettings.videoEnabled, - audioEnabled: locationAdmin.locationSettings.audioEnabled, - screenSharingEnabled: locationAdmin.locationSettings.screenSharingEnabled, - faceStreamingEnabled: locationAdmin.locationSettings.faceStreamingEnabled, - globalMediaEnabled: locationAdmin.locationSettings.instanceMediaChatEnabled, + type: locationAdmin?.locationSetting?.locationType, + videoEnabled: locationAdmin?.locationSetting?.videoEnabled, + audioEnabled: locationAdmin?.locationSetting?.audioEnabled, + screenSharingEnabled: locationAdmin?.locationSetting?.screenSharingEnabled, + faceStreamingEnabled: locationAdmin?.locationSetting?.faceStreamingEnabled, + globalMediaEnabled: locationAdmin?.locationSetting?.instanceMediaChatEnabled, isLobby: locationAdmin.isLobby, isFeatured: locationAdmin.isFeatured }) @@ -114,7 +107,7 @@ const ViewLocation = (props: Props) => { case 'scene': temp.scene = value.length < 2 ? t('admin:components.locationModel.sceneRequired') : '' break - case 'private': + case 'type': temp.type = value.length < 2 ? t('admin:components.locationModel.privateRoleRequired') : '' break default: @@ -123,6 +116,7 @@ const ViewLocation = (props: Props) => { setState({ ...state, [name]: value, formErrors: temp }) } + console.log(state) const handleSubmit = () => { const locationData = { name: state.name, @@ -151,7 +145,7 @@ const ViewLocation = (props: Props) => { temp.scene = t('admin:components.locationModel.sceneCantEmpty') } if (!state.type) { - temp.scene = t('admin:components.locationModel.typeCantEmpty') + temp.type = t('admin:components.locationModel.typeCantEmpty') } setState({ ...state, formErrors: temp }) if (validateForm(state, state.formErrors)) { @@ -284,7 +278,10 @@ const ViewLocation = (props: Props) => { - + 0 ? classes.redBorder : classes.createInput} + >