Your ROOT_URL in app.ini is https://git.cloudchain.link/ but you are visiting https://dash.bss.nz/open-source-mirrors/overseerr/commit/ef5d019c18d7f6cdbbb1e1b7f8ff7816ed9b117b You should set ROOT_URL correctly, otherwise the web may not work correctly.

feat(ui): Add sort options to user list ()

pull/905/head
TheCatLady 4 years ago committed by GitHub
parent 04fa9f79e2
commit ef5d019c18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -2669,6 +2669,13 @@ paths:
description: Returns all users in a JSON array.
tags:
- users
parameters:
- in: query
name: sort
schema:
type: string
enum: [created, updated, requests, displayname]
default: created
responses:
'200':
description: A JSON array of all users

@ -11,10 +11,35 @@ import { UserType } from '../constants/user';
const router = Router();
router.get('/', async (_req, res) => {
const userRepository = getRepository(User);
router.get('/', async (req, res) => {
let query = getRepository(User).createQueryBuilder('user');
switch (req.query.sort) {
case 'updated':
query = query.orderBy('user.updatedAt', 'DESC');
break;
case 'displayname':
query = query.orderBy(
'(CASE WHEN user.username IS NULL THEN user.plexUsername ELSE user.username END)',
'ASC'
);
break;
case 'requests':
query = query
.addSelect((subQuery) => {
return subQuery
.select('COUNT(request.id)', 'requestCount')
.from(MediaRequest, 'request')
.where('request.requestedBy.id = user.id');
}, 'requestCount')
.orderBy('requestCount', 'DESC');
break;
default:
query = query.orderBy('user.id', 'ASC');
break;
}
const users = await userRepository.find();
const users = await query.getMany();
return res.status(200).json(User.filterMany(users));
});

@ -56,11 +56,11 @@ const RequestList: React.FC = () => {
return (
<>
<PageTitle title={intl.formatMessage(messages.requests)} />
<div className="flex flex-col justify-between md:items-end md:flex-row">
<div className="flex flex-col justify-between lg:items-end lg:flex-row">
<Header>{intl.formatMessage(messages.requests)}</Header>
<div className="flex flex-col mt-2 md:flex-row">
<div className="flex mb-2 md:mb-0 md:mr-2">
<span className="inline-flex items-center px-3 text-gray-100 bg-gray-800 border border-r-0 border-gray-500 cursor-default rounded-l-md sm:text-sm">
<div className="flex flex-col flex-grow mt-2 sm:flex-row lg:flex-grow-0">
<div className="flex flex-grow mb-2 sm:mb-0 sm:mr-2 lg:flex-grow-0">
<span className="inline-flex items-center px-3 text-sm text-gray-100 bg-gray-800 border border-r-0 border-gray-500 cursor-default rounded-l-md">
<svg
className="w-6 h-6"
fill="currentColor"
@ -81,12 +81,8 @@ const RequestList: React.FC = () => {
setPageIndex(0);
setCurrentFilter(e.target.value as Filter);
}}
onBlur={(e) => {
setPageIndex(0);
setCurrentFilter(e.target.value as Filter);
}}
value={currentFilter}
className="rounded-r-only"
className="text-sm rounded-r-only"
>
<option value="all">
{intl.formatMessage(messages.filterAll)}
@ -99,8 +95,8 @@ const RequestList: React.FC = () => {
</option>
</select>
</div>
<div className="flex">
<span className="inline-flex items-center px-3 text-gray-100 bg-gray-800 border border-r-0 border-gray-500 cursor-default rounded-l-md sm:text-sm">
<div className="flex flex-grow mb-2 sm:mb-0 lg:flex-grow-0">
<span className="inline-flex items-center px-3 text-gray-100 bg-gray-800 border border-r-0 border-gray-500 cursor-default sm:text-sm rounded-l-md">
<svg
className="w-6 h-6"
fill="currentColor"
@ -122,7 +118,7 @@ const RequestList: React.FC = () => {
setCurrentSort(e.target.value as Sort);
}}
value={currentSort}
className="rounded-r-only"
className="text-sm rounded-r-only"
>
<option value="added">
{intl.formatMessage(messages.sortAdded)}

@ -29,7 +29,7 @@ const messages = defineMessages({
importfromplexerror: 'Something went wrong while importing users from Plex.',
importedfromplex:
'{userCount, plural, =0 {No new users} one {# new user} other {# new users}} imported from Plex.',
username: 'Username',
user: 'User',
totalrequests: 'Total Requests',
usertype: 'User Type',
role: 'Role',
@ -39,7 +39,6 @@ const messages = defineMessages({
bulkedit: 'Bulk Edit',
delete: 'Delete',
admin: 'Admin',
user: 'User',
plexuser: 'Plex User',
deleteuser: 'Delete User',
userdeleted: 'User deleted',
@ -62,13 +61,22 @@ const messages = defineMessages({
'Email notifications need to be configured and enabled in order to automatically generate passwords.',
autogeneratepassword: 'Automatically generate password',
validationEmail: 'You must provide a valid email address',
sortCreated: 'Creation Date',
sortUpdated: 'Last Updated',
sortDisplayName: 'Display Name',
sortRequests: 'Request Count',
});
type Sort = 'created' | 'updated' | 'requests' | 'displayname';
const UserList: React.FC = () => {
const intl = useIntl();
const router = useRouter();
const { addToast } = useToasts();
const { data, error, revalidate } = useSWR<User[]>('/api/v1/user');
const [currentSort, setCurrentSort] = useState<Sort>('created');
const { data, error, revalidate } = useSWR<User[]>(
`/api/v1/user?sort=${currentSort}`
);
const [isDeleting, setDeleting] = useState(false);
const [isImporting, setImporting] = useState(false);
const [deleteModal, setDeleteModal] = useState<{
@ -368,24 +376,60 @@ const UserList: React.FC = () => {
/>
</Transition>
<div className="flex flex-col justify-between md:items-end md:flex-row">
<div className="flex flex-col justify-between lg:items-end lg:flex-row">
<Header>{intl.formatMessage(messages.userlist)}</Header>
<div className="flex flex-row justify-between mt-2 sm:flex-row md:mb-0">
<Button
className="flex-grow mr-2 outline"
buttonType="primary"
onClick={() => setCreateModal({ isOpen: true })}
>
{intl.formatMessage(messages.createlocaluser)}
</Button>
<Button
className="flex-grow outline"
buttonType="primary"
disabled={isImporting}
onClick={() => importFromPlex()}
>
{intl.formatMessage(messages.importfromplex)}
</Button>
<div className="flex flex-col flex-grow mt-2 lg:flex-row lg:flex-grow-0">
<div className="flex flex-row justify-between flex-grow mb-2 lg:mb-0 lg:flex-grow-0">
<Button
className="flex-grow mr-2 outline"
buttonType="primary"
onClick={() => setCreateModal({ isOpen: true })}
>
{intl.formatMessage(messages.createlocaluser)}
</Button>
<Button
className="flex-grow outline lg:mr-2"
buttonType="primary"
disabled={isImporting}
onClick={() => importFromPlex()}
>
{intl.formatMessage(messages.importfromplex)}
</Button>
</div>
<div className="flex flex-grow mb-2 lg:mb-0 lg:flex-grow-0">
<span className="inline-flex items-center px-3 text-sm text-gray-100 bg-gray-800 border border-r-0 border-gray-500 cursor-default rounded-l-md">
<svg
className="w-6 h-6"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M3 3a1 1 0 000 2h11a1 1 0 100-2H3zM3 7a1 1 0 000 2h7a1 1 0 100-2H3zM3 11a1 1 0 100 2h4a1 1 0 100-2H3zM15 8a1 1 0 10-2 0v5.586l-1.293-1.293a1 1 0 00-1.414 1.414l3 3a1 1 0 001.414 0l3-3a1 1 0 00-1.414-1.414L15 13.586V8z" />
</svg>
</span>
<select
id="sort"
name="sort"
onChange={(e) => {
setCurrentSort(e.target.value as Sort);
}}
value={currentSort}
className="text-sm rounded-r-only"
>
<option value="created">
{intl.formatMessage(messages.sortCreated)}
</option>
<option value="updated">
{intl.formatMessage(messages.sortUpdated)}
</option>
<option value="requests">
{intl.formatMessage(messages.sortRequests)}
</option>
<option value="displayname">
{intl.formatMessage(messages.sortDisplayName)}
</option>
</select>
</div>
</div>
</div>
<Table>
@ -404,7 +448,7 @@ const UserList: React.FC = () => {
/>
)}
</Table.TH>
<Table.TH>{intl.formatMessage(messages.username)}</Table.TH>
<Table.TH>{intl.formatMessage(messages.user)}</Table.TH>
<Table.TH>{intl.formatMessage(messages.totalrequests)}</Table.TH>
<Table.TH>{intl.formatMessage(messages.usertype)}</Table.TH>
<Table.TH>{intl.formatMessage(messages.role)}</Table.TH>

@ -628,6 +628,10 @@
"components.UserList.passwordinfodescription": "Email notifications need to be configured and enabled in order to automatically generate passwords.",
"components.UserList.plexuser": "Plex User",
"components.UserList.role": "Role",
"components.UserList.sortCreated": "Creation Date",
"components.UserList.sortDisplayName": "Display Name",
"components.UserList.sortRequests": "Request Count",
"components.UserList.sortUpdated": "Last Updated",
"components.UserList.totalrequests": "Total Requests",
"components.UserList.user": "User",
"components.UserList.usercreatedfailed": "Something went wrong while creating the user.",
@ -635,7 +639,6 @@
"components.UserList.userdeleted": "User deleted.",
"components.UserList.userdeleteerror": "Something went wrong while deleting the user.",
"components.UserList.userlist": "User List",
"components.UserList.username": "Username",
"components.UserList.users": "Users",
"components.UserList.userssaved": "Users saved!",
"components.UserList.usertype": "User Type",

Loading…
Cancel
Save