1- // --- НАЧАЛО ИСПРАВЛЕННОГО КОДА ---
2- import { Badge , Container , Flex , Heading , Table } from "@chakra-ui/react"
3- import { useQuery , useQueryClient } from "@tanstack/react-query"
1+ /**
2+ * @file Defines the Admin page component for managing users.
3+ * @description Renders a page with a paginated table of users, providing functionality to add users and perform actions on existing users.
4+ * @module Admin
5+ */
6+
7+ import { Badge , Container , EmptyState , Flex , Heading , Table , VStack } from "@chakra-ui/react"
8+ import { type UseQueryResult , useQuery } from "@tanstack/react-query"
49import { createFileRoute , useNavigate } from "@tanstack/react-router"
10+ import type React from "react"
11+ import type { FC } from "react"
12+ import { useCallback , useEffect } from "react"
13+ import { FiUsers } from "react-icons/fi"
514import { z } from "zod"
615
7- import {
8- // ИМПОРТИРУЕМ ТОЧНУЮ ФУНКЦИЮ И ТИП
9- type UserPublic ,
10- usersUsersRouterReadUsers ,
11- } from "@/client"
16+ import { type CancelablePromise , type UserPublic , type UsersPublic , usersUsersRouterReadUsers } from "@/client"
1217import AddUser from "@/components/Admin/AddUser"
1318import { UserActionsMenu } from "@/components/Common/UserActionsMenu"
14- import PendingUsers from "@/components/Pending/PendingUsers"
1519import {
1620 PaginationItems ,
1721 PaginationNextTrigger ,
1822 PaginationPrevTrigger ,
1923 PaginationRoot ,
20- } from "@/components/ui/pagination.tsx"
24+ } from "@/components/ui/pagination"
25+ import useAuth from "@/hooks/useAuth"
26+
27+ // region Type Aliases
28+
29+ /**
30+ * Type alias for the search parameters schema.
31+ * @type {SearchParams }
32+ */
33+ type SearchParams = { page : number }
34+
35+ /**
36+ * Type alias for the Admin component.
37+ * @type {AdminComponent }
38+ */
39+ type AdminComponent = FC
40+
41+ /**
42+ * Type alias for the UsersTable component.
43+ * @type {UsersTableComponent }
44+ */
45+ type UsersTableComponent = FC
46+
47+ // endregion
2148
49+ // region Main Code
50+
51+ /**
52+ * Schema for validating search parameters for users pagination.
53+ * @constant {z.ZodObject}
54+ */
2255const usersSearchSchema = z . object ( {
23- page : z . number ( ) . catch ( 1 ) ,
56+ /** The current page number. */
57+ page : z . number ( ) . int ( ) . min ( 1 ) . catch ( 1 ) ,
2458} )
2559
26- const PER_PAGE = 5
27-
28- function getUsersQueryOptions ( { page } : { page : number } ) {
29- return {
30- // ВЫЗЫВАЕМ ФУНКЦИЮ НАПРЯМУЮ
31- queryFn : ( ) =>
32- usersUsersRouterReadUsers ( {
33- skip : ( page - 1 ) * PER_PAGE ,
34- limit : PER_PAGE ,
35- } ) ,
36- queryKey : [ "users" , { page } ] ,
37- }
60+ /**
61+ * Number of users per page.
62+ * @constant {number}
63+ */
64+ const PER_PAGE : number = 5
65+
66+ /**
67+ * Main Admin page component for managing users.
68+ * @returns {React.ReactElement } The rendered Admin component.
69+ */
70+ const Admin : AdminComponent = ( ) : React . ReactElement => {
71+ return (
72+ < main >
73+ < Container maxW = "full" >
74+ < Heading size = "lg" pt = { 12 } >
75+ Users Management
76+ </ Heading >
77+ < AddUser />
78+ < UsersTable />
79+ </ Container >
80+ </ main >
81+ )
3882}
3983
40- export const Route = createFileRoute ( "/_layout/admin" ) ( {
41- component : Admin ,
42- validateSearch : ( search ) => usersSearchSchema . parse ( search ) ,
43- } )
84+ /**
85+ * Component for rendering a paginated table of users with actions.
86+ * @returns {React.ReactElement } The rendered UsersTable component.
87+ */
88+ const UsersTable : UsersTableComponent = ( ) : React . ReactElement => {
89+ const { user : currentUser } = useAuth ( )
90+ const navigate : ReturnType < typeof useNavigate > = useNavigate ( { from : Route . fullPath } )
91+ const { page } : SearchParams = Route . useSearch ( )
4492
45- function UsersTable ( ) {
46- const queryClient = useQueryClient ( )
47- const currentUser = queryClient . getQueryData < UserPublic > ( [ "currentUser" ] )
48- const navigate = useNavigate ( { from : Route . fullPath } )
49- const { page } = Route . useSearch ( )
93+ const usersQueryKey : [ string , { page : number } ] = [ "users" , { page } ]
5094
51- const { data, isLoading, isPlaceholderData } = useQuery ( {
52- ...getUsersQueryOptions ( { page } ) ,
53- placeholderData : ( prevData ) => prevData ,
95+ const { data, isPlaceholderData } : UseQueryResult < UsersPublic , Error > = useQuery ( {
96+ queryFn : ( ) : CancelablePromise < UsersPublic > =>
97+ usersUsersRouterReadUsers ( { skip : ( page - 1 ) * PER_PAGE , limit : PER_PAGE } ) ,
98+ queryKey : usersQueryKey ,
99+ placeholderData : ( prevData : UsersPublic | undefined ) : UsersPublic | undefined => prevData ,
100+ staleTime : 1000 * 10 ,
54101 } )
55102
56- const setPage = ( page : number ) =>
57- // Добавляем 'void'
58- void navigate ( {
59- search : ( prev : { [ key : string ] : string } ) => ( { ...prev , page } ) ,
60- } )
103+ const setPage : ( page : number ) => void = useCallback (
104+ ( newPage : number ) : void => {
105+ void navigate ( {
106+ search : ( prev : SearchParams ) : { page : number } => ( { ...prev , page : newPage } ) ,
107+ } )
108+ } ,
109+ [ navigate ] ,
110+ )
111+
112+ useEffect ( ( ) : void => {
113+ if ( data && data . data . length === 0 && data . count > 0 && page > 1 ) {
114+ setPage ( page - 1 )
115+ }
116+ } , [ data , page , setPage ] )
61117
62- const users = data ?. data . slice ( 0 , PER_PAGE ) ?? [ ]
63- const count = data ?. count ?? 0
118+ const users : UserPublic [ ] = data ?. data ?? [ ]
119+ const count : number = data ?. count ?? 0
64120
65- if ( isLoading ) {
66- return < PendingUsers />
121+ if ( users . length === 0 ) {
122+ return (
123+ < EmptyState . Root >
124+ < EmptyState . Content >
125+ < EmptyState . Indicator >
126+ < FiUsers />
127+ </ EmptyState . Indicator >
128+ < VStack textAlign = "center" >
129+ < EmptyState . Title > No users found</ EmptyState . Title >
130+ < EmptyState . Description > Add a new user to get started</ EmptyState . Description >
131+ </ VStack >
132+ </ EmptyState . Content >
133+ </ EmptyState . Root >
134+ )
67135 }
68136
69137 return (
@@ -79,51 +147,61 @@ function UsersTable() {
79147 </ Table . Row >
80148 </ Table . Header >
81149 < Table . Body >
82- { users ?. map ( ( user ) => (
83- < Table . Row key = { user . id } opacity = { isPlaceholderData ? 0.5 : 1 } >
84- < Table . Cell color = { ! user . full_name ? "gray" : "inherit" } >
85- { user . full_name || "N/A" }
86- { currentUser ?. id === user . id && (
87- < Badge ml = "1" colorScheme = "teal" >
88- You
89- </ Badge >
90- ) }
91- </ Table . Cell >
92- < Table . Cell truncate maxW = "sm" >
93- { user . email }
94- </ Table . Cell >
95- < Table . Cell > { user . is_superuser ? "Superuser" : "User" } </ Table . Cell >
96- < Table . Cell > { user . is_active ? "Active" : "Inactive" } </ Table . Cell >
97- < Table . Cell >
98- < UserActionsMenu user = { user } disabled = { currentUser ?. id === user . id } />
99- </ Table . Cell >
100- </ Table . Row >
101- ) ) }
150+ { users . map (
151+ ( user : UserPublic ) : React . ReactElement => (
152+ < Table . Row key = { user . id } opacity = { isPlaceholderData ? 0.5 : 1 } >
153+ < Table . Cell color = { ! user . full_name ? "gray" : "inherit" } >
154+ { user . full_name || "N/A" }
155+ { currentUser ?. id === user . id && (
156+ < Badge ml = "1" colorScheme = "teal" >
157+ You
158+ </ Badge >
159+ ) }
160+ </ Table . Cell >
161+ < Table . Cell truncate maxW = "sm" >
162+ { user . email }
163+ </ Table . Cell >
164+ < Table . Cell > { user . is_superuser ? "Superuser" : "User" } </ Table . Cell >
165+ < Table . Cell > { user . is_active ? "Active" : "Inactive" } </ Table . Cell >
166+ < Table . Cell >
167+ < UserActionsMenu user = { user } disabled = { currentUser ?. id === user . id } />
168+ </ Table . Cell >
169+ </ Table . Row >
170+ ) ,
171+ ) }
102172 </ Table . Body >
103173 </ Table . Root >
104174 < Flex justifyContent = "flex-end" mt = { 4 } >
105- < PaginationRoot count = { count } pageSize = { PER_PAGE } onPageChange = { ( { page } ) => setPage ( page ) } >
175+ < PaginationRoot
176+ count = { count }
177+ pageSize = { PER_PAGE }
178+ page = { page }
179+ onPageChange = { ( { page : newPage } ) => setPage ( newPage ) }
180+ >
106181 < Flex >
107- < PaginationPrevTrigger />
182+ < PaginationPrevTrigger aria-label = "Previous page" />
108183 < PaginationItems />
109- < PaginationNextTrigger />
184+ < PaginationNextTrigger aria-label = "Next page" />
110185 </ Flex >
111186 </ PaginationRoot >
112187 </ Flex >
113188 </ >
114189 )
115190}
116191
117- function Admin ( ) {
118- return (
119- < Container maxW = "full" >
120- < Heading size = "lg" pt = { 12 } >
121- Users Management
122- </ Heading >
123-
124- < AddUser />
125- < UsersTable />
126- </ Container >
127- )
128- }
129- // --- КОНЕЦ ИСПРАВЛЕННОГО КОДА ---
192+ // endregion
193+
194+ // region Optional Declarations
195+
196+ Admin . displayName = "Admin"
197+ UsersTable . displayName = "UsersTable"
198+
199+ export const Route = createFileRoute ( "/_layout/admin" ) ( {
200+ component : Admin ,
201+ validateSearch : ( search : Record < string , unknown > ) : SearchParams => usersSearchSchema . parse ( search ) ,
202+ } )
203+
204+ // endregion
205+
206+ // noinspection JSUnusedGlobalSymbols
207+ export default Admin
0 commit comments