blessing-skin-server/resources/assets/src/views/admin/UsersManagement/index.tsx
2020-05-30 10:44:36 +08:00

366 lines
10 KiB
TypeScript

import React, { useState, useEffect } from 'react'
import { hot } from 'react-hot-loader/root'
import { useImmer } from 'use-immer'
import useBlessingExtra from '@/scripts/hooks/useBlessingExtra'
import useIsLargeScreen from '@/scripts/hooks/useIsLargeScreen'
import { t } from '@/scripts/i18n'
import * as fetch from '@/scripts/net'
import { User, UserPermission, Paginator } from '@/scripts/types'
import { toast, showModal } from '@/scripts/notify'
import urls from '@/scripts/urls'
import type { Props as ModalInputProps } from '@/components/ModalInput'
import Loading from '@/components/Loading'
import Pagination from '@/components/Pagination'
import Header from './Header'
import Card from './Card'
import Row from './Row'
const UsersManagement: React.FC = () => {
const [users, setUsers] = useImmer<User[]>([])
const [page, setPage] = useState(1)
const [totalPages, setTotalPages] = useState(1)
const [isLoading, setIsLoading] = useState(false)
const isLargeScreen = useIsLargeScreen()
const [isTableMode, setIsTableMode] = useState(false)
const [query, setQuery] = useState('')
const currentUser = useBlessingExtra<User>('currentUser', {
uid: 0,
permission: UserPermission.Admin,
} as User)
useEffect(() => {
if (isLargeScreen) {
setIsTableMode(true)
}
}, [isLargeScreen])
const getUsers = async () => {
setIsLoading(true)
const { data, last_page }: Paginator<User> = await fetch.get(
urls.admin.users.list(),
{
q: query,
page,
},
)
setUsers(() => data)
setTotalPages(last_page)
setIsLoading(false)
}
useEffect(() => {
getUsers()
}, [page])
const handleModeChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setIsTableMode(event.target.value === 'table')
}
const handleQueryChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setQuery(event.target.value)
}
const handleSubmitQuery = (event: React.FormEvent) => {
event.preventDefault()
getUsers()
}
const handleEmailChange = async (user: User, index: number) => {
let email: string
try {
const { value } = await showModal({
mode: 'prompt',
text: t('admin.newUserEmail'),
input: user.email,
validator: (value: string) => {
if (!value) {
return t('auth.emptyEmail')
}
},
})
email = value
} catch {
return
}
const { code, message } = await fetch.put<fetch.ResponseBody>(
urls.admin.users.email(user.uid),
{ email },
)
if (code === 0) {
toast.success(message)
setUsers((users) => {
users[index].email = email
})
} else {
toast.error(message)
}
}
const handleNicknameChange = async (user: User, index: number) => {
let nickname: string
try {
const { value } = await showModal({
mode: 'prompt',
text: t('admin.newUserNickname'),
input: user.nickname,
validator: (value: string) => {
if (!value) {
return t('auth.emptyNickname')
}
},
})
nickname = value
} catch {
return
}
const { code, message } = await fetch.put<fetch.ResponseBody>(
urls.admin.users.nickname(user.uid),
{ nickname },
)
if (code === 0) {
toast.success(message)
setUsers((users) => {
users[index].nickname = nickname
})
} else {
toast.error(message)
}
}
const handleScoreChange = async (user: User, index: number) => {
let score: number
try {
const { value } = await showModal({
mode: 'prompt',
text: t('admin.newScore'),
input: user.score.toString(),
inputType: 'number',
})
score = Number.parseInt(value)
} catch {
return
}
const { code, message } = await fetch.put<fetch.ResponseBody>(
urls.admin.users.score(user.uid),
{ score },
)
if (code === 0) {
toast.success(message)
setUsers((users) => {
users[index].score = score
})
} else {
toast.error(message)
}
}
const handlePermissionChange = async (user: User, index: number) => {
const permissions: ModalInputProps['choices'] = [
{ text: t('admin.banned'), value: '-1' },
{ text: t('admin.normal'), value: '0' },
]
if (currentUser.permission > UserPermission.Admin) {
permissions.push({ text: t('admin.admin'), value: '1' })
}
let permission: UserPermission
try {
const { value } = await showModal({
mode: 'prompt',
text: t('admin.newPermission'),
input: user.permission.toString(),
inputType: 'radios',
choices: permissions,
})
permission = Number.parseInt(value)
} catch {
return
}
const { code, message } = await fetch.put<fetch.ResponseBody>(
urls.admin.users.permission(user.uid),
{ permission },
)
if (code === 0) {
toast.success(message)
setUsers((users) => {
users[index].permission = permission
})
} else {
toast.error(message)
}
}
const handleVerificationToggle = async (user: User, index: number) => {
const { code, message } = await fetch.put<fetch.ResponseBody>(
urls.admin.users.verification(user.uid),
)
if (code === 0) {
toast.success(message)
setUsers((users) => {
users[index].verified = !users[index].verified
})
} else {
toast.error(message)
}
}
const handlePasswordChange = async (user: User) => {
let password: string
try {
const { value } = await showModal({
mode: 'prompt',
text: t('admin.newUserPassword'),
inputType: 'password',
placeholder: t('admin.changePassword'),
})
password = value
} catch {
return
}
const { code, message } = await fetch.put<fetch.ResponseBody>(
urls.admin.users.password(user.uid),
{ password },
)
if (code === 0) {
toast.success(message)
} else {
toast.error(message)
}
}
const handleDelete = async (user: User) => {
try {
await showModal({
text: t('admin.deleteUserNotice'),
okButtonType: 'danger',
})
} catch {
return
}
const { code, message } = await fetch.del(urls.admin.users.delete(user.uid))
if (code === 0) {
toast.success(message)
setUsers((users) => users.filter(({ uid }) => uid !== user.uid))
} else {
toast.error(message)
}
}
return (
<div className="card">
<Header className="card-header">
<form className="input-group" onSubmit={handleSubmitQuery}>
<input
type="text"
className="form-control"
title={t('vendor.datatable.search')}
value={query}
onChange={handleQueryChange}
/>
<div className="input-group-append">
<button className="btn btn-primary" type="submit">
{t('vendor.datatable.search')}
</button>
</div>
</form>
<div className="btn-group btn-group-toggle">
<label
className={`btn btn-secondary ${isTableMode ? 'active' : ''}`}
title="Table Mode"
>
<input
type="radio"
value="table"
checked={isTableMode}
onChange={handleModeChange}
/>
<i className="fas fa-list"></i>
</label>
<label
className={`btn btn-secondary ${isTableMode ? '' : 'active'}`}
title="Card Mode"
>
<input
type="radio"
value="card"
checked={!isTableMode}
onChange={handleModeChange}
/>
<i className="fas fa-grip-vertical"></i>
</label>
</div>
</Header>
{isLoading ? (
<div className="card-body">
<Loading />
</div>
) : users.length === 0 ? (
<div className="card-body text-center">{t('general.noResult')}</div>
) : isTableMode ? (
<div className="card-body table-responsive p-0">
<table className="table table-striped">
<thead>
<tr>
<th>UID</th>
<th>{t('general.user.email')}</th>
<th>{t('general.user.nickname')}</th>
<th>{t('general.user.score')}</th>
<th>{t('admin.permission')}</th>
<th>{t('admin.verification')}</th>
<th>{t('general.user.register-at')}</th>
<th>{t('admin.operationsTitle')}</th>
</tr>
</thead>
<tbody>
{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">
{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 className="card-footer">
<div className="float-right">
<Pagination page={page} totalPages={totalPages} onChange={setPage} />
</div>
</div>
</div>
)
}
export default hot(UsersManagement)