Enhance admin endpoints and queries for improved filtering and management
- Updated `ListPaymentRequestsQuery` to include `Kind` and `Search` parameters for better filtering of payment requests. - Enhanced `ListAuditLogsQuery` to support additional filters: `Source`, `TargetType`, and `Action`, improving audit log retrieval. - Modified `ListUsersQuery` to accept new filters: `RoleId`, `IsActivated`, `IsBlocked`, and `BillingExpired`, allowing for more granular user management. - Introduced `DeleteInbound` endpoint to allow deletion of inbounds that are not currently available, enhancing inbound management capabilities. - Updated frontend API calls to reflect new query parameters and support for additional filtering options in the admin interface. - Revised API documentation to include new parameters and endpoint functionalities for better clarity and usage guidance.
This commit is contained in:
@@ -5,17 +5,25 @@ import { useTranslation } from 'react-i18next'
|
||||
import { Input } from '@/shared/ui/input'
|
||||
import { Button } from '@/shared/ui/button'
|
||||
import { Badge } from '@/shared/ui/badge'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/shared/ui/select'
|
||||
import { PaidUntilBadge } from '@/features/billing/PaidUntilBadge'
|
||||
import { listUsers } from '@/features/admin/users/api'
|
||||
import { listRoles } from '@/features/admin/roles/api'
|
||||
import { UserManageDialog } from '@/features/admin/users/UserManageDialog'
|
||||
|
||||
export const Route = createFileRoute('/admin/users')({ component: AdminUsersPage })
|
||||
|
||||
const PAGE_SIZE = 20
|
||||
|
||||
type StatusFilter = 'all' | 'active' | 'pending' | 'blocked'
|
||||
type BillingFilter = 'all' | 'expired' | 'paid'
|
||||
|
||||
function AdminUsersPage() {
|
||||
const { t } = useTranslation()
|
||||
const [search, setSearch] = useState('')
|
||||
const [roleId, setRoleId] = useState<string>('all')
|
||||
const [status, setStatus] = useState<StatusFilter>('all')
|
||||
const [billing, setBilling] = useState<BillingFilter>('all')
|
||||
const [page, setPage] = useState(1)
|
||||
// Id, не сам объект — иначе диалог держит "замороженный" снимок пользователя и не видит
|
||||
// изменения, сделанные им же самим (гифт/блок/смена роли инвалидируют этот запрос, но проп
|
||||
@@ -23,23 +31,84 @@ function AdminUsersPage() {
|
||||
// запроса на каждый рендер.
|
||||
const [managingId, setManagingId] = useState<string | null>(null)
|
||||
|
||||
const rolesQuery = useQuery({ queryKey: ['admin-roles'], queryFn: listRoles })
|
||||
|
||||
const roleFilter = roleId === 'all' ? undefined : roleId
|
||||
const isActivated = status === 'active' ? true : status === 'pending' ? false : undefined
|
||||
const isBlocked = status === 'blocked' ? true : status === 'all' ? undefined : false
|
||||
const billingExpired = billing === 'all' ? undefined : billing === 'expired'
|
||||
|
||||
const { data, isLoading, isError, refetch } = useQuery({
|
||||
queryKey: ['admin-users', page, search],
|
||||
queryFn: () => listUsers(page, PAGE_SIZE, search || undefined),
|
||||
queryKey: ['admin-users', page, search, roleFilter, isActivated, isBlocked, billingExpired],
|
||||
queryFn: () => listUsers(page, PAGE_SIZE, search || undefined, roleFilter, isActivated, isBlocked, billingExpired),
|
||||
})
|
||||
const managingUser = managingId ? (data?.items.find((u) => u.id === managingId) ?? null) : null
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<Input
|
||||
placeholder={t('admin.users.searchPlaceholder')}
|
||||
value={search}
|
||||
onChange={(e) => {
|
||||
setSearch(e.target.value)
|
||||
setPage(1)
|
||||
}}
|
||||
className="max-w-sm"
|
||||
/>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Input
|
||||
placeholder={t('admin.users.searchPlaceholder')}
|
||||
value={search}
|
||||
onChange={(e) => {
|
||||
setSearch(e.target.value)
|
||||
setPage(1)
|
||||
}}
|
||||
className="max-w-sm"
|
||||
/>
|
||||
<Select
|
||||
value={roleId}
|
||||
onValueChange={(v) => {
|
||||
setRoleId(v)
|
||||
setPage(1)
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="w-40">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">{t('admin.users.allRoles')}</SelectItem>
|
||||
{rolesQuery.data?.map((role) => (
|
||||
<SelectItem key={role.id} value={role.id}>
|
||||
{role.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Select
|
||||
value={status}
|
||||
onValueChange={(v) => {
|
||||
setStatus(v as StatusFilter)
|
||||
setPage(1)
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="w-40">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">{t('admin.users.allStatuses')}</SelectItem>
|
||||
<SelectItem value="active">{t('admin.users.status.active')}</SelectItem>
|
||||
<SelectItem value="pending">{t('admin.users.status.pending')}</SelectItem>
|
||||
<SelectItem value="blocked">{t('admin.users.status.blocked')}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Select
|
||||
value={billing}
|
||||
onValueChange={(v) => {
|
||||
setBilling(v as BillingFilter)
|
||||
setPage(1)
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="w-40">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">{t('admin.users.allBilling')}</SelectItem>
|
||||
<SelectItem value="expired">{t('admin.users.billingExpired')}</SelectItem>
|
||||
<SelectItem value="paid">{t('admin.users.billingPaid')}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{isLoading && <p className="text-sm text-muted-foreground">…</p>}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user