diff --git a/server/nexus/generated/nexus.ts b/server/nexus/generated/nexus.ts index 54c60ae..f5c4694 100644 --- a/server/nexus/generated/nexus.ts +++ b/server/nexus/generated/nexus.ts @@ -567,6 +567,7 @@ export interface NexusGenFieldTypes { resources: NexusGenRootTypes['ResourceUnion'][] // [ResourceUnion!]! user: NexusGenRootTypes['User'] | null // User users: NexusGenRootTypes['User'][] // [User!]! + usersCount: number // Int! } Resource: { // field return type @@ -667,6 +668,7 @@ export interface NexusGenFieldTypeNames { resources: 'ResourceUnion' user: 'User' users: 'User' + usersCount: 'Int' } Resource: { // field return type name @@ -746,6 +748,10 @@ export interface NexusGenArgTypes { take?: number | null // Int where?: NexusGenInputs['bani684_usersWhereInput'] | null // bani684_usersWhereInput } + usersCount: { + // args + where?: NexusGenInputs['bani684_usersWhereInput'] | null // bani684_usersWhereInput + } } } diff --git a/server/nexus/generated/schema.graphql b/server/nexus/generated/schema.graphql index 585f703..5d60a64 100644 --- a/server/nexus/generated/schema.graphql +++ b/server/nexus/generated/schema.graphql @@ -216,6 +216,11 @@ type Query { take: Int where: bani684_usersWhereInput ): [User!]! + + """ + Количество пользователей + """ + usersCount(where: bani684_usersWhereInput): Int! } type Resource { diff --git a/server/nexus/types/Query/definitions/User.ts b/server/nexus/types/Query/definitions/User.ts index fe130d9..9159ed8 100644 --- a/server/nexus/types/Query/definitions/User.ts +++ b/server/nexus/types/Query/definitions/User.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/camelcase */ import { Prisma } from '@prisma/client' -import { ObjectDefinitionBlock } from 'nexus/dist/core' +import { arg, ObjectDefinitionBlock } from 'nexus/dist/core' const select = { id: true, @@ -20,6 +20,20 @@ export default (t: ObjectDefinitionBlock<'Query'>) => { // }, // }) + t.nonNull.int('usersCount', { + description: 'Количество пользователей', + args: { + where: arg({ + type: 'bani684_usersWhereInput', + }), + }, + resolve(_, args, ctx) { + return ctx.prisma.bani684_users.count( + args as Pick + ) + }, + }) + t.crud.bani684Users({ type: 'User', alias: 'user', diff --git a/src/components/ui/Pagination/index.tsx b/src/components/ui/Pagination/index.tsx new file mode 100644 index 0000000..ae7abf0 --- /dev/null +++ b/src/components/ui/Pagination/index.tsx @@ -0,0 +1,118 @@ +import React, { memo, useCallback } from 'react' + +import Link from 'next/link' + +import URI from 'urijs' +import { PaginationProps } from './interfaces' +import { useRouter } from 'next/router' +import { PaginationStyled } from './styles' +export * from './interfaces' + +const Pagination: React.FC = ({ + page, + limit, + total, + ...other +}) => { + const router = useRouter() + + const getNewLocation = useCallback( + (page: number) => { + const asPath = router.asPath + + const uri = new URI(asPath) + + const query = uri.query(true) + + Object.assign(query, { + page: page > 1 ? page : undefined, + }) + + uri.query(query) + + return uri.resource() + }, + [router.asPath] + ) + + if (!page || !limit || !total) { + return null + } + + const pages = Math.ceil(total / limit) + + if (pages < 2) { + return null + } + + const rows = [] + + if (page > 1) { + const href = getNewLocation(page - 1) + + rows.push( +
  • + + « + +
  • + ) + } + + let lstr = false + let rstr = false + for (let i = 1; i <= pages; i++) { + if ( + (page > 2 && i < page - 1 && i > 1) || + (pages - page > 3 && i > page + 1 && i < pages - 1) + ) { + if (!lstr && i > 1 && i < page) { + rows.push( +
  • + ... +
  • + ) + lstr = true + } + if (!rstr && i > page && i < pages) { + rows.push( +
  • + ... +
  • + ) + rstr = true + } + } else { + const href = getNewLocation(i) + + rows.push( +
  • + + + {i} + + +
  • + ) + } + } + if (page < pages) { + const href = getNewLocation(page + 1) + + rows.push( +
  • + + » + +
  • + ) + } + + return ( + +
      {rows}
    +
    + ) +} + +export default memo(Pagination) diff --git a/src/components/ui/Pagination/interfaces.ts b/src/components/ui/Pagination/interfaces.ts new file mode 100644 index 0000000..06f0e33 --- /dev/null +++ b/src/components/ui/Pagination/interfaces.ts @@ -0,0 +1,7 @@ +export type PaginationProps = { + page: number + + limit: number + + total: number +} diff --git a/src/components/ui/Pagination/styles.ts b/src/components/ui/Pagination/styles.ts new file mode 100644 index 0000000..acae406 --- /dev/null +++ b/src/components/ui/Pagination/styles.ts @@ -0,0 +1,44 @@ +import styled from 'styled-components' + +export const PaginationStyled = styled.nav` + display: flex; + + .row { + margin: 20px auto; + display: flex; + flex-direction: row; + flex-wrap: nowrap; + justify-content: center; + list-style: none; + padding: 0; + border: 1px solid #ddd; + border-radius: 2px; + } + + .control { + border-right: 1px solid #ddd; + + &:last-child { + border-right: none; + } + + > * { + display: block; + padding: 8px 12px; + /* border: 1px solid #ddd; */ + /* margin-left: 3px; + margin-right: 3px; */ + text-decoration: none; + } + } + + a { + &.active { + background: #ddd; + } + + &:hover { + background: #dfdfdf; + } + } +` diff --git a/src/gql/users.graphql b/src/gql/users.graphql index e7a0154..420fa64 100644 --- a/src/gql/users.graphql +++ b/src/gql/users.graphql @@ -7,6 +7,7 @@ query users( users(where: $where, orderBy: $orderBy, skip: $skip, take: $take) { ...user_ } + usersCount(where: $where) } query user($where: bani684_usersWhereUniqueInput!) { diff --git a/src/modules/gql/generated/helpers/apollo-helpers.ts b/src/modules/gql/generated/helpers/apollo-helpers.ts index 583e762..613cb99 100644 --- a/src/modules/gql/generated/helpers/apollo-helpers.ts +++ b/src/modules/gql/generated/helpers/apollo-helpers.ts @@ -47,13 +47,14 @@ export type GalleryImageFieldPolicy = { image?: FieldPolicy | FieldReadFunction, title?: FieldPolicy | FieldReadFunction }; -export type QueryKeySpecifier = ('cities' | 'companies' | 'resources' | 'user' | 'users' | QueryKeySpecifier)[]; +export type QueryKeySpecifier = ('cities' | 'companies' | 'resources' | 'user' | 'users' | 'usersCount' | QueryKeySpecifier)[]; export type QueryFieldPolicy = { cities?: FieldPolicy | FieldReadFunction, companies?: FieldPolicy | FieldReadFunction, resources?: FieldPolicy | FieldReadFunction, user?: FieldPolicy | FieldReadFunction, - users?: FieldPolicy | FieldReadFunction + users?: FieldPolicy | FieldReadFunction, + usersCount?: FieldPolicy | FieldReadFunction }; export type ResourceKeySpecifier = ('TemplateVarValues' | 'alias' | 'content' | 'createdby' | 'createdon' | 'description' | 'id' | 'longtitle' | 'pagetitle' | 'published' | 'searchable' | 'template' | 'uri' | ResourceKeySpecifier)[]; export type ResourceFieldPolicy = { diff --git a/src/modules/gql/generated/schema.json b/src/modules/gql/generated/schema.json index 92bbf3d..5e628b2 100644 --- a/src/modules/gql/generated/schema.json +++ b/src/modules/gql/generated/schema.json @@ -1960,6 +1960,35 @@ }, "isDeprecated": false, "deprecationReason": null + }, + { + "name": "usersCount", + "description": "Количество пользователей", + "args": [ + { + "name": "where", + "description": null, + "type": { + "kind": "INPUT_OBJECT", + "name": "bani684_usersWhereInput", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null } ], "inputFields": null, diff --git a/src/modules/gql/generated/types.ts b/src/modules/gql/generated/types.ts index e0a374e..f1e8509 100644 --- a/src/modules/gql/generated/types.ts +++ b/src/modules/gql/generated/types.ts @@ -181,6 +181,8 @@ export interface Query { resources: Array; user?: Maybe; users: Array; + /** Количество пользователей */ + usersCount: Scalars['Int']; } @@ -223,6 +225,11 @@ export type QueryUsersArgs = { where?: Maybe; }; + +export type QueryUsersCountArgs = { + where?: Maybe; +}; + export interface Resource { __typename?: 'Resource'; TemplateVarValues?: Maybe>; diff --git a/src/modules/gql/generated/users.ts b/src/modules/gql/generated/users.ts index 1e3e6ca..2aebe9c 100644 --- a/src/modules/gql/generated/users.ts +++ b/src/modules/gql/generated/users.ts @@ -20,7 +20,7 @@ export type UsersQueryVariables = Types.Exact<{ }>; -export type UsersQuery = { __typename?: 'Query', users: Array<( +export type UsersQuery = { __typename?: 'Query', usersCount: number, users: Array<( { __typename?: 'User' } & UserFragment )> }; @@ -31,6 +31,7 @@ export const UsersDocument = gql` users(where: $where, orderBy: $orderBy, skip: $skip, take: $take) { ...user_ } + usersCount(where: $where) } ${UserFragmentDoc}`; diff --git a/src/pages/Users/View/index.tsx b/src/pages/Users/View/index.tsx index 123409e..f929601 100644 --- a/src/pages/Users/View/index.tsx +++ b/src/pages/Users/View/index.tsx @@ -4,10 +4,15 @@ import { GridTableItemStyled, GridTableStyled, } from 'src/components/ui/GridTable/styles' +import Pagination from 'src/components/ui/Pagination' import { UsersPageViewProps } from './interfaces' import UsersPageViewUser from './User' -const UsersPageView: React.FC = ({ users, ...other }) => { +const UsersPageView: React.FC = ({ + users, + pagination, + ...other +}) => { return useMemo(() => { return ( <> @@ -30,9 +35,11 @@ const UsersPageView: React.FC = ({ users, ...other }) => { return })} + + ) - }, [other, users]) + }, [other, pagination, users]) } export default UsersPageView diff --git a/src/pages/Users/View/interfaces.ts b/src/pages/Users/View/interfaces.ts index 0a6663a..f4b73c5 100644 --- a/src/pages/Users/View/interfaces.ts +++ b/src/pages/Users/View/interfaces.ts @@ -1,7 +1,10 @@ +import { PaginationProps } from 'src/components/ui/Pagination' import { UsersPageViewUserProps } from './User/interfaces' -export interface UsersPageViewProps { +export type UsersPageViewProps = { // data: UsersConnectionQuery | null | undefined users: UsersPageViewUserProps['user'][] + + pagination: PaginationProps } diff --git a/src/pages/Users/index.tsx b/src/pages/Users/index.tsx index d9a5ace..8d801a8 100644 --- a/src/pages/Users/index.tsx +++ b/src/pages/Users/index.tsx @@ -12,15 +12,13 @@ import { Page } from '../_App/interfaces' import { useRouter } from 'next/router' import { ParsedUrlQuery } from 'querystring' -const take = 10 - -const defaultVariables: UsersQueryVariables = { - take, -} - -function getQueryParams(query: ParsedUrlQuery) { +const getQueryParams = ( + query: ParsedUrlQuery +): UsersQueryVariables & { page: number } => { let skip: number | undefined + const take = 10 + const page = (query.page && typeof query.page === 'string' && parseInt(query.page)) || 0 @@ -29,9 +27,19 @@ function getQueryParams(query: ParsedUrlQuery) { } return { + page, skip, take, - page, + where: { + active: { + equals: true, + }, + Attributes: { + blocked: { + equals: false, + }, + }, + }, } } @@ -42,7 +50,6 @@ const UsersPage: Page = () => { const { ...queryVariables } = useMemo(() => { return { - ...defaultVariables, ...getQueryParams(query), } }, [query]) @@ -54,6 +61,9 @@ const UsersPage: Page = () => { // const { variables, loading } = response + const page = + (query.page && typeof query.page === 'string' && parseInt(query.page)) || 1 + return ( <> @@ -69,6 +79,11 @@ const UsersPage: Page = () => { // page={page} // loading={loading} users={response.data?.users || []} + pagination={{ + limit: response.variables?.take || 0, + page, + total: response.data?.usersCount || 0, + }} /> ) @@ -85,7 +100,6 @@ UsersPage.getInitialProps = async (context) => { * иначе при рендеринге не будут получены данные из кеша и рендер будет пустой. */ variables: { - ...defaultVariables, ...getQueryParams(context.query), }, })