Skip to content
This repository was archived by the owner on Aug 21, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion packages/client-core/i18n/en/admin.json
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@
"avatarRequired": "Avatar is required",
"scopeTypeRequired": "Scope type is required",
"hideGuests": "Hide guests",
"id": "Id",
"name": "Name",
"avatar": "Avatar",
"grantScope": "Grant Scope",
Expand All @@ -378,7 +379,17 @@
"confirmUserDelete": "Do you want to delete user",
"isGuest": "Is guest",
"selectAllScopes": "Select All Scopes",
"clearAllScopes": "Clear All Scopes"
"clearAllScopes": "Clear All Scopes",
"userSearch": "user by id, name or account identifier",
"linkedAccounts": "Linked Accounts",
"discord": "Discord",
"facebook": "Facebook",
"google": "Google",
"github": "Github",
"linkedIn": "LinkedIn",
"twitter": "Twitter",
"sms": "SMS",
"email": "Email"
},
"server": {
"loading": "Fetching Server Info",
Expand Down
5 changes: 4 additions & 1 deletion packages/client-core/src/admin/common/variables/user.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
export interface UserColumn {
id: 'name' | 'avatarId' | 'isGuest' | 'location' | 'inviteCode' | 'instanceId' | 'action'
id: 'id' | 'name' | 'avatarId' | 'accountIdentifier' | 'isGuest' | 'location' | 'inviteCode' | 'instanceId' | 'action'
label: string
minWidth?: number
align?: 'right'
}

export const userColumns: UserColumn[] = [
{ id: 'id', label: 'User Id', minWidth: 65 },
{ id: 'name', label: 'Name', minWidth: 65 },
{ id: 'avatarId', label: 'Avatar', minWidth: 65 },
{ id: 'accountIdentifier', label: 'Linked Accounts', minWidth: 65 },
{
id: 'isGuest',
label: 'Is Guest',
Expand Down Expand Up @@ -45,6 +47,7 @@ export interface UserData {
el: any
name: string
avatarId: string | JSX.Element
accountIdentifier: string | JSX.Element
isGuest: string
location: string | JSX.Element
inviteCode: string | JSX.Element
Expand Down
125 changes: 124 additions & 1 deletion packages/client-core/src/admin/components/Users/UserDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,24 @@ import { useTranslation } from 'react-i18next'
import { AdminScopeType } from '@xrengine/common/src/interfaces/AdminScopeType'
import { CreateEditUser, UserInterface } from '@xrengine/common/src/interfaces/User'

import EmailIcon from '@mui/icons-material/Email'
import GitHubIcon from '@mui/icons-material/GitHub'
import PhoneIcon from '@mui/icons-material/Phone'
import Button from '@mui/material/Button'
import Checkbox from '@mui/material/Checkbox'
import Container from '@mui/material/Container'
import DialogActions from '@mui/material/DialogActions'
import DialogTitle from '@mui/material/DialogTitle'
import FormControlLabel from '@mui/material/FormControlLabel'

import Grid from '@mui/material/Grid'
import Tooltip from '@mui/material/Tooltip'
import Typography from '@mui/material/Typography'

import { DiscordIcon } from '../../../common/components/Icons/DiscordIcon'
import { FacebookIcon } from '../../../common/components/Icons/FacebookIcon'
import { GoogleIcon } from '../../../common/components/Icons/GoogleIcon'
import { LinkedInIcon } from '../../../common/components/Icons/LinkedInIcon'
import { TwitterIcon } from '../../../common/components/Icons/TwitterIcon'
import { NotificationService } from '../../../common/services/NotificationService'
import { useAuthState } from '../../../user/services/AuthService'
import AutoComplete, { AutoCompleteData } from '../../common/AutoComplete'
Expand All @@ -36,6 +47,7 @@ interface Props {
}

const defaultState = {
id: '',
name: '',
avatar: '',
isGuest: true,
Expand Down Expand Up @@ -72,6 +84,16 @@ const UserDrawer = ({ open, mode, selectedUser, onClose }: Props) => {
}
})

const nonGuestLinkedIP = selectedUser?.identity_providers?.filter((ip) => ip.type !== 'guest')
const discordIp = selectedUser?.identity_providers?.find((ip) => ip.type === 'discord')
const googleIp = selectedUser?.identity_providers?.find((ip) => ip.type === 'google')
const facebookIp = selectedUser?.identity_providers?.find((ip) => ip.type === 'facebook')
const twitterIp = selectedUser?.identity_providers?.find((ip) => ip.type === 'twitter')
const linkedinIp = selectedUser?.identity_providers?.find((ip) => ip.type === 'linkedin')
const githubIp = selectedUser?.identity_providers?.find((ip) => ip.type === 'github')
const emailIp = selectedUser?.identity_providers?.find((ip) => ip.type === 'email')
const smsIp = selectedUser?.identity_providers?.find((ip) => ip.type === 'sms')

if (selectedUser) {
for (const scope of selectedUser.scopes || []) {
const scopeExists = scopeMenu.find((item) => item.type === scope.type)
Expand Down Expand Up @@ -104,6 +126,7 @@ const UserDrawer = ({ open, mode, selectedUser, onClose }: Props) => {
if (selectedUser) {
setState({
...defaultState,
id: selectedUser.id,
name: selectedUser.name || '',
avatar: selectedUser.avatarId || '',
isGuest: selectedUser.isGuest,
Expand Down Expand Up @@ -201,6 +224,8 @@ const UserDrawer = ({ open, mode, selectedUser, onClose }: Props) => {
{mode === UserDrawerMode.ViewEdit && !editMode && selectedUser?.name}
</DialogTitle>

<InputText name="id" label={t('admin:components.user.id')} value={state.id} disabled />

<InputText
name="name"
label={t('admin:components.user.name')}
Expand Down Expand Up @@ -238,6 +263,104 @@ const UserDrawer = ({ open, mode, selectedUser, onClose }: Props) => {
/>
)}

{nonGuestLinkedIP && nonGuestLinkedIP.length > 0 && (
<Grid container spacing={1} sx={{ marginTop: 2, marginBottom: 4 }}>
<Grid item md={12}>
<Typography variant="body1">{t('admin:components.user.linkedAccounts')}</Typography>
</Grid>
{discordIp && (
<Grid item md={6} sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Tooltip title={t('admin:components.user.discord')} arrow>
<span>
<DiscordIcon width="20px" height="20px" viewBox="0 0 40 40" />
</span>
</Tooltip>
<Typography variant="body2" sx={{ wordBreak: 'break-word' }}>
{discordIp.accountIdentifier!}
</Typography>
</Grid>
)}
{googleIp && (
<Grid item md={6} sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Tooltip title={t('admin:components.user.google')} arrow>
<span>
<GoogleIcon width="20px" height="20px" viewBox="0 0 40 40" />
</span>
</Tooltip>
<Typography variant="body2" sx={{ wordBreak: 'break-word' }}>
{googleIp.accountIdentifier!}
</Typography>
</Grid>
)}
{facebookIp && (
<Grid item md={6} sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Tooltip title={t('admin:components.user.facebook')} arrow>
<span>
<FacebookIcon width="20px" height="20px" viewBox="0 0 40 40" />
</span>
</Tooltip>
<Typography variant="body2" sx={{ wordBreak: 'break-word' }}>
{facebookIp.accountIdentifier!}
</Typography>
</Grid>
)}
{twitterIp && (
<Grid item md={6} sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Tooltip title={t('admin:components.user.twitter')} arrow>
<span>
<TwitterIcon width="20px" height="20px" viewBox="0 0 40 40" />
</span>
</Tooltip>
<Typography variant="body2" sx={{ wordBreak: 'break-word' }}>
{twitterIp.accountIdentifier!}
</Typography>
</Grid>
)}
{linkedinIp && (
<Grid item md={6} sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Tooltip title={t('admin:components.user.linkedIn')} arrow>
<span>
<LinkedInIcon width="20px" height="20px" viewBox="0 0 40 40" />
</span>
</Tooltip>
<Typography variant="body2" sx={{ wordBreak: 'break-word' }}>
{linkedinIp.accountIdentifier!}
</Typography>
</Grid>
)}
{githubIp && (
<Grid item md={6} sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Tooltip title={t('admin:components.user.github')} arrow>
<GitHubIcon width="20px" height="20px" />
</Tooltip>
<Typography variant="body2" sx={{ wordBreak: 'break-word' }}>
{githubIp.accountIdentifier!}
</Typography>
</Grid>
)}
{emailIp && (
<Grid item md={6} sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Tooltip title={t('admin:components.user.email')} arrow>
<EmailIcon width="20px" height="20px" />
</Tooltip>
<Typography variant="body2" sx={{ wordBreak: 'break-word' }}>
{emailIp.accountIdentifier!}
</Typography>
</Grid>
)}
{smsIp && (
<Grid item md={6} sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Tooltip title={t('admin:components.user.sms')} arrow>
<PhoneIcon width="20px" height="20px" />
</Tooltip>
<Typography variant="body2" sx={{ wordBreak: 'break-word' }}>
{smsIp.accountIdentifier!}
</Typography>
</Grid>
)}
</Grid>
)}

{viewMode && (
<AutoComplete data={scopeMenu} label={t('admin:components.user.grantScope')} value={state.scopes} disabled />
)}
Expand Down
77 changes: 76 additions & 1 deletion packages/client-core/src/admin/components/Users/UserTable.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'

import { IdentityProvider } from '@xrengine/common/src/interfaces/IdentityProvider'
import { UserInterface } from '@xrengine/common/src/interfaces/User'

import EmailIcon from '@mui/icons-material/Email'
import GitHubIcon from '@mui/icons-material/GitHub'
import PhoneIcon from '@mui/icons-material/Phone'
import Box from '@mui/material/Box'
import Tooltip from '@mui/material/Tooltip'

import { DiscordIcon } from '../../../common/components/Icons/DiscordIcon'
import { FacebookIcon } from '../../../common/components/Icons/FacebookIcon'
import { GoogleIcon } from '../../../common/components/Icons/GoogleIcon'
import { LinkedInIcon } from '../../../common/components/Icons/LinkedInIcon'
import { TwitterIcon } from '../../../common/components/Icons/TwitterIcon'
import { useAuthState } from '../../../user/services/AuthService'
import ConfirmDialog from '../../common/ConfirmDialog'
import TableComponent from '../../common/Table'
Expand Down Expand Up @@ -60,16 +70,80 @@ const UserTable = ({ className, search }: UserProps) => {
el: UserInterface,
name: string,
avatarId: string | JSX.Element,
identityProviders: IdentityProvider[],
isGuest: string,
location: string | JSX.Element,
inviteCode: string | JSX.Element,
instanceId: string | JSX.Element
): UserData => {
const discordIp = identityProviders.find((ip) => ip.type === 'discord')
const googleIp = identityProviders.find((ip) => ip.type === 'google')
const facebookIp = identityProviders.find((ip) => ip.type === 'facebook')
const twitterIp = identityProviders.find((ip) => ip.type === 'twitter')
const linkedinIp = identityProviders.find((ip) => ip.type === 'linkedin')
const githubIp = identityProviders.find((ip) => ip.type === 'github')
const emailIp = identityProviders.find((ip) => ip.type === 'email')
const smsIp = identityProviders.find((ip) => ip.type === 'sms')

return {
id,
el,
name,
avatarId,
accountIdentifier: (
<Box sx={{ display: 'flex', gap: 1.5 }}>
{discordIp && (
<Tooltip title={discordIp.accountIdentifier!} arrow>
<span>
<DiscordIcon width="20px" height="20px" viewBox="0 0 40 40" />
</span>
</Tooltip>
)}
{googleIp && (
<Tooltip title={googleIp.accountIdentifier!} arrow>
<span>
<GoogleIcon width="20px" height="20px" viewBox="0 0 40 40" />
</span>
</Tooltip>
)}
{facebookIp && (
<Tooltip title={facebookIp.accountIdentifier!} arrow>
<span>
<FacebookIcon width="20px" height="20px" viewBox="0 0 40 40" />
</span>
</Tooltip>
)}
{twitterIp && (
<Tooltip title={twitterIp.accountIdentifier!} arrow>
<span>
<TwitterIcon width="20px" height="20px" viewBox="0 0 40 40" />
</span>
</Tooltip>
)}
{linkedinIp && (
<Tooltip title={linkedinIp.accountIdentifier!} arrow>
<span>
<LinkedInIcon width="20px" height="20px" viewBox="0 0 40 40" />
</span>
</Tooltip>
)}
{githubIp && (
<Tooltip title={githubIp.accountIdentifier!} arrow>
<GitHubIcon width="20px" height="20px" />
</Tooltip>
)}
{emailIp && (
<Tooltip title={emailIp.accountIdentifier!} arrow>
<EmailIcon width="20px" height="20px" />
</Tooltip>
)}
{smsIp && (
<Tooltip title={smsIp.accountIdentifier!} arrow>
<PhoneIcon width="20px" height="20px" />
</Tooltip>
)}
</Box>
),
isGuest,
location,
inviteCode,
Expand Down Expand Up @@ -106,10 +180,11 @@ const UserTable = ({ className, search }: UserProps) => {

const rows = adminUsers.map((el) => {
return createData(
el.id || '',
el.id,
el,
el.name,
el.avatarId || <span className={styles.spanNone}>{t('admin:components.common.none')}</span>,
el.identity_providers || [],
el.isGuest.toString(),
el.instance && el.instance.location ? (
el.instance.location.name
Expand Down
2 changes: 1 addition & 1 deletion packages/client-core/src/admin/components/Users/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const Users = () => {
<div>
<Grid container spacing={1} className={styles.mb10px}>
<Grid item sm={8} xs={12}>
<Search text="user" handleChange={handleChange} />
<Search text={t('admin:components.user.userSearch')} handleChange={handleChange} />
</Grid>
<Grid item sm={4} xs={8}>
<Box sx={{ display: 'flex' }}>
Expand Down
21 changes: 0 additions & 21 deletions packages/client-core/src/admin/services/UserService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,27 +169,6 @@ export const AdminUserService = {
const result = (await API.instance.client.service('user').remove(id)) as UserInterface
dispatchAction(AdminUserActions.userAdminRemoved({ data: result }))
},
searchUserAction: async (data: any) => {
try {
const userState = accessUserState()
const skip = userState.skip.value
const limit = userState.limit.value
const userResult = (await API.instance.client.service('user').find({
query: {
$sort: {
name: 1
},
$skip: skip || 0,
$limit: limit,
action: 'search',
data
}
})) as Paginated<UserInterface>
dispatchAction(AdminUserActions.searchedUser({ userResult }))
} catch (err) {
NotificationService.dispatchNotify(err.message, { variant: 'error' })
}
},
setSkipGuests: async (skipGuests: boolean) => {
dispatchAction(AdminUserActions.setSkipGuests({ skipGuests }))
},
Expand Down
1 change: 1 addition & 0 deletions packages/common/src/dbmodels/IdentityProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { UserId } from '../interfaces/UserId'
export interface IdentityProviderInterface {
id: string
token: string
accountIdentifier: string
password: string
isVerified: string
verifyToken: string
Expand Down
1 change: 1 addition & 0 deletions packages/common/src/interfaces/IdentityProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { UserId } from './UserId'
export interface IdentityProvider {
id: number
token: string
accountIdentifier?: string
type: string
isVerified: boolean
userId: UserId
Expand Down
Loading