add skeleton for users management
This commit is contained in:
parent
6b695a745b
commit
49ff44ee6f
|
|
@ -1,8 +1,7 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import styled from '@emotion/styled'
|
|
||||||
import { t } from '@/scripts/i18n'
|
import { t } from '@/scripts/i18n'
|
||||||
import { User } from '@/scripts/types'
|
import { User } from '@/scripts/types'
|
||||||
import * as breakpoints from '@/styles/breakpoints'
|
import { Box, Icon, InfoTable } from './styles'
|
||||||
import {
|
import {
|
||||||
humanizePermission,
|
humanizePermission,
|
||||||
verificationStatusText,
|
verificationStatusText,
|
||||||
|
|
@ -10,31 +9,6 @@ import {
|
||||||
canModifyPermission,
|
canModifyPermission,
|
||||||
} from './utils'
|
} from './utils'
|
||||||
|
|
||||||
const Box = styled.div`
|
|
||||||
width: 48%;
|
|
||||||
margin: 7px;
|
|
||||||
|
|
||||||
${breakpoints.lessThan(breakpoints.Breakpoint.lg)} {
|
|
||||||
width: 98%;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
const Icon = styled.div`
|
|
||||||
width: 70px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
padding-top: 22px;
|
|
||||||
`
|
|
||||||
const InfoTable = styled.div`
|
|
||||||
> div:not(:last-child) {
|
|
||||||
${breakpoints.lessThan(breakpoints.Breakpoint.sm)} {
|
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.125);
|
|
||||||
}
|
|
||||||
${breakpoints.greaterThan(breakpoints.Breakpoint.sm)} {
|
|
||||||
border-right: 1px solid rgba(0, 0, 0, 0.125);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
user: User
|
user: User
|
||||||
currentUser: User
|
currentUser: User
|
||||||
|
|
@ -54,7 +28,7 @@ const Card: React.FC<Props> = (props) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box className="info-box">
|
<Box className="info-box">
|
||||||
<Icon>
|
<Icon py>
|
||||||
<img
|
<img
|
||||||
className="bs-avatar"
|
className="bs-avatar"
|
||||||
src={`${blessing.base_url}/avatar/user/${user.uid}`}
|
src={`${blessing.base_url}/avatar/user/${user.uid}`}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
import React from 'react'
|
||||||
|
import styled from '@emotion/styled'
|
||||||
|
import Skeleton from 'react-loading-skeleton'
|
||||||
|
import { t } from '@/scripts/i18n'
|
||||||
|
import { Box, Icon, InfoTable } from './styles'
|
||||||
|
|
||||||
|
const ShrinkedSkeleton = styled(Skeleton)<{ width?: string }>`
|
||||||
|
width: ${(props) => props.width};
|
||||||
|
`
|
||||||
|
|
||||||
|
const LoadingCard: React.FC = () => (
|
||||||
|
<Box className="info-box">
|
||||||
|
<Icon>
|
||||||
|
<Skeleton circle height={50} width={50} />
|
||||||
|
</Icon>
|
||||||
|
<div className="info-box-content">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-10">
|
||||||
|
<Skeleton width="200px" />
|
||||||
|
</div>
|
||||||
|
<div className="col-2">
|
||||||
|
<span className="text-gray">
|
||||||
|
<i className="fas fa-cog"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<Skeleton width="200px" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Skeleton width="200px" />
|
||||||
|
</div>
|
||||||
|
<InfoTable className="row m-2 border-top border-bottom">
|
||||||
|
<div className="col-sm-4 py-1 text-center">
|
||||||
|
<b className="d-block">{t('general.user.score')}</b>
|
||||||
|
<span className="d-block py-1">
|
||||||
|
<ShrinkedSkeleton width="30%" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="col-sm-4 py-1 text-center">
|
||||||
|
<b className="d-block">{t('admin.permission')}</b>
|
||||||
|
<span className="d-block py-1">
|
||||||
|
<ShrinkedSkeleton width="30%" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="col-sm-4 py-1 text-center">
|
||||||
|
<b className="d-block">{t('admin.verification')}</b>
|
||||||
|
<span className="d-block py-1">
|
||||||
|
<ShrinkedSkeleton width="30%" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</InfoTable>
|
||||||
|
<div>
|
||||||
|
<Skeleton width="240px" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default LoadingCard
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
import React from 'react'
|
||||||
|
import styled from '@emotion/styled'
|
||||||
|
import Skeleton from 'react-loading-skeleton'
|
||||||
|
|
||||||
|
const ThickSkeleton = styled(Skeleton)`
|
||||||
|
line-height: 2;
|
||||||
|
`
|
||||||
|
|
||||||
|
const LoadingRow: React.FC = () => (
|
||||||
|
<tr>
|
||||||
|
<td colSpan={8}>
|
||||||
|
<ThickSkeleton />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default LoadingRow
|
||||||
|
|
@ -9,11 +9,12 @@ import { User, UserPermission, Paginator } from '@/scripts/types'
|
||||||
import { toast, showModal } from '@/scripts/notify'
|
import { toast, showModal } from '@/scripts/notify'
|
||||||
import urls from '@/scripts/urls'
|
import urls from '@/scripts/urls'
|
||||||
import type { Props as ModalInputProps } from '@/components/ModalInput'
|
import type { Props as ModalInputProps } from '@/components/ModalInput'
|
||||||
import Loading from '@/components/Loading'
|
|
||||||
import Pagination from '@/components/Pagination'
|
import Pagination from '@/components/Pagination'
|
||||||
import Header from './Header'
|
import Header from './Header'
|
||||||
import Card from './Card'
|
import Card from './Card'
|
||||||
|
import LoadingCard from './LoadingCard'
|
||||||
import Row from './Row'
|
import Row from './Row'
|
||||||
|
import LoadingRow from './LoadingRow'
|
||||||
|
|
||||||
const UsersManagement: React.FC = () => {
|
const UsersManagement: React.FC = () => {
|
||||||
const [users, setUsers] = useImmer<User[]>([])
|
const [users, setUsers] = useImmer<User[]>([])
|
||||||
|
|
@ -296,15 +297,11 @@ const UsersManagement: React.FC = () => {
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</Header>
|
</Header>
|
||||||
{isLoading ? (
|
{users.length === 0 && !isLoading ? (
|
||||||
<div className="card-body">
|
|
||||||
<Loading />
|
|
||||||
</div>
|
|
||||||
) : users.length === 0 ? (
|
|
||||||
<div className="card-body text-center">{t('general.noResult')}</div>
|
<div className="card-body text-center">{t('general.noResult')}</div>
|
||||||
) : isTableMode ? (
|
) : isTableMode ? (
|
||||||
<div className="card-body table-responsive p-0">
|
<div className="card-body table-responsive p-0">
|
||||||
<table className="table table-striped">
|
<table className={`table ${isLoading ? '' : 'table-striped'}`}>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>UID</th>
|
<th>UID</th>
|
||||||
|
|
@ -318,8 +315,33 @@ const UsersManagement: React.FC = () => {
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{users.map((user, i) => (
|
{isLoading
|
||||||
<Row
|
? new Array(10).fill(null).map((_, i) => <LoadingRow key={i} />)
|
||||||
|
: users.map((user, i) => (
|
||||||
|
<Row
|
||||||
|
key={user.uid}
|
||||||
|
user={user}
|
||||||
|
currentUser={currentUser}
|
||||||
|
onEmailChange={() => handleEmailChange(user, i)}
|
||||||
|
onNicknameChange={() => handleNicknameChange(user, i)}
|
||||||
|
onScoreChange={() => handleScoreChange(user, i)}
|
||||||
|
onPermissionChange={() => handlePermissionChange(user, i)}
|
||||||
|
onVerificationToggle={() =>
|
||||||
|
handleVerificationToggle(user, i)
|
||||||
|
}
|
||||||
|
onPasswordChange={() => handlePasswordChange(user)}
|
||||||
|
onDelete={() => handleDelete(user)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="card-body d-flex flex-wrap">
|
||||||
|
{isLoading
|
||||||
|
? new Array(10).fill(null).map((_, i) => <LoadingCard key={i} />)
|
||||||
|
: users.map((user, i) => (
|
||||||
|
<Card
|
||||||
key={user.uid}
|
key={user.uid}
|
||||||
user={user}
|
user={user}
|
||||||
currentUser={currentUser}
|
currentUser={currentUser}
|
||||||
|
|
@ -332,25 +354,6 @@ const UsersManagement: React.FC = () => {
|
||||||
onDelete={() => handleDelete(user)}
|
onDelete={() => handleDelete(user)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="card-body d-flex flex-wrap">
|
|
||||||
{users.map((user, i) => (
|
|
||||||
<Card
|
|
||||||
key={user.uid}
|
|
||||||
user={user}
|
|
||||||
currentUser={currentUser}
|
|
||||||
onEmailChange={() => handleEmailChange(user, i)}
|
|
||||||
onNicknameChange={() => handleNicknameChange(user, i)}
|
|
||||||
onScoreChange={() => handleScoreChange(user, i)}
|
|
||||||
onPermissionChange={() => handlePermissionChange(user, i)}
|
|
||||||
onVerificationToggle={() => handleVerificationToggle(user, i)}
|
|
||||||
onPasswordChange={() => handlePasswordChange(user)}
|
|
||||||
onDelete={() => handleDelete(user)}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="card-footer">
|
<div className="card-footer">
|
||||||
|
|
|
||||||
29
resources/assets/src/views/admin/UsersManagement/styles.ts
Normal file
29
resources/assets/src/views/admin/UsersManagement/styles.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import styled from '@emotion/styled'
|
||||||
|
import * as breakpoints from '@/styles/breakpoints'
|
||||||
|
|
||||||
|
export const Box = styled.div`
|
||||||
|
width: 48%;
|
||||||
|
margin: 7px;
|
||||||
|
|
||||||
|
${breakpoints.lessThan(breakpoints.Breakpoint.lg)} {
|
||||||
|
width: 98%;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const Icon = styled.div<{ py?: boolean }>`
|
||||||
|
width: 70px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: ${(props) => (props.py ? '22px' : '0')};
|
||||||
|
`
|
||||||
|
|
||||||
|
export const InfoTable = styled.div`
|
||||||
|
> div:not(:last-child) {
|
||||||
|
${breakpoints.lessThan(breakpoints.Breakpoint.sm)} {
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.125);
|
||||||
|
}
|
||||||
|
${breakpoints.greaterThan(breakpoints.Breakpoint.sm)} {
|
||||||
|
border-right: 1px solid rgba(0, 0, 0, 0.125);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
Loading…
Reference in New Issue
Block a user