diff --git a/apps/schools/domains/circle/components/changeCircleForm/index.tsx b/apps/schools/domains/circle/components/changeCircleForm/index.tsx index 8ad76a1a..1df2e8f3 100644 --- a/apps/schools/domains/circle/components/changeCircleForm/index.tsx +++ b/apps/schools/domains/circle/components/changeCircleForm/index.tsx @@ -82,8 +82,8 @@ export const ChangeCircleForm = () => { required={true} label={ - * Название - + * Название + } name={CIRCLE_NAME} className={styles.label} diff --git a/apps/schools/domains/common/handlers/paginationChange.ts b/apps/schools/domains/common/handlers/paginationChange.ts new file mode 100644 index 00000000..bbd0217c --- /dev/null +++ b/apps/schools/domains/common/handlers/paginationChange.ts @@ -0,0 +1,43 @@ +export const handlePaginationChange = ( + setPaginationParams: (params: { page: number; pageSize: number }) => void, + setQueryPaginationParams: (params: { [key: string]: { page: number; pageSize: number } }) => void, + counts: { [key: string]: number | undefined }, + newPage: number, + newPageSize: number, + defaultPaginationTablePage: number, + defaultPaginationTablePageSize: number, + scrollToTop: () => void, +) => { + setPaginationParams({ + page: newPage, + pageSize: newPageSize, + }) + + const newQueryParams: { [key: string]: { page: number; pageSize: number } } = {} + + let currentPage = newPage + let currentOffset = 0 + + Object.keys(counts).forEach((key, index) => { + const count = counts[key] ?? 0 + const maxPages = Math.ceil(count / newPageSize) + + if (currentPage <= maxPages) { + newQueryParams[key] = { + page: currentPage, + pageSize: newPageSize, + } + } else { + currentPage -= maxPages + newQueryParams[key] = { + page: defaultPaginationTablePage, + pageSize: defaultPaginationTablePageSize, + } + } + + currentOffset += count + }) + + setQueryPaginationParams(newQueryParams) + scrollToTop() +} diff --git a/apps/schools/domains/common/redux/serializers.ts b/apps/schools/domains/common/redux/serializers.ts index 6165fb90..b16df007 100644 --- a/apps/schools/domains/common/redux/serializers.ts +++ b/apps/schools/domains/common/redux/serializers.ts @@ -125,10 +125,10 @@ interface GetStudentProfile { id?: string name: string age?: number - phone?: string + phone: string photo: GetPhoto parent_names?: string - parent_phones?: string + parent_phones: string } export interface GetAnalytics { diff --git a/apps/schools/domains/common/utils/getTotalPages.ts b/apps/schools/domains/common/utils/getTotalPages.ts new file mode 100644 index 00000000..7ebaea31 --- /dev/null +++ b/apps/schools/domains/common/utils/getTotalPages.ts @@ -0,0 +1,5 @@ +export const getTotalPages = (counts: { [key: string]: { count: number | undefined } }, pageSize: number) => { + return Object.keys(counts).reduce((total, key) => { + return total + Math.ceil((counts[key].count ?? 0) / pageSize) * pageSize + }, 0) +} diff --git a/apps/schools/domains/employee/components/createEmployeeForm/index.tsx b/apps/schools/domains/employee/components/createEmployeeForm/index.tsx index 1660b138..239b47e1 100644 --- a/apps/schools/domains/employee/components/createEmployeeForm/index.tsx +++ b/apps/schools/domains/employee/components/createEmployeeForm/index.tsx @@ -48,8 +48,8 @@ export const CreateEmployeeForm = () => { required={true} label={ - * Телефон сотрудника - + * Телефон сотрудника + } name={EMPLOYEE_PHONE} className={styles.label} @@ -62,8 +62,8 @@ export const CreateEmployeeForm = () => { required label={ - * Ф. И. О. сотрудника - + * Ф. И. О. сотрудника + } name={EMPLOYEE_NAME} className={styles.label} @@ -72,12 +72,7 @@ export const CreateEmployeeForm = () => { - + diff --git a/apps/schools/domains/organization/redux/organizationApi.ts b/apps/schools/domains/organization/redux/organizationApi.ts index 6dcafe3d..2307d8a5 100644 --- a/apps/schools/domains/organization/redux/organizationApi.ts +++ b/apps/schools/domains/organization/redux/organizationApi.ts @@ -37,6 +37,7 @@ import { mapReturnedData } from '@domains/common/redux/utils' import { TableType as TableTypeCircle } from '@domains/circle/components/circleList/interfaces' import { TableType as TableTypeTickets } from '@domains/ticket/components/ticketList/interfaces' import { TableType as TableTypeQuery } from '@domains/query/components/queryList/interfaces' +import { TableType as TableTypeStudent } from '@domains/student/components/studentList/interfaces' const organizationApi = commonApi.injectEndpoints({ endpoints: (build) => ({ @@ -70,6 +71,17 @@ const organizationApi = commonApi.injectEndpoints({ method: 'GET', params: params, }), + transformResponse: (response: ReturnedData) => { + return mapReturnedData(response, (query) => { + const transformedQuery = structuredClone(query) as unknown as TableTypeStudent + transformedQuery.id = query.id + transformedQuery.student_name = query.name + transformedQuery.student_phone = query.student_profile.phone + transformedQuery.parent_phone = query.student_profile.parent_phones?.replaceAll(',', '\n') + transformedQuery.circle_name = query.circle.name + return transformedQuery + })! + }, providesTags: (result) => providesList(result?.results, 'Student'), }), getAllCircles: build.query, GetOrganizationCircleListData>({ @@ -168,6 +180,17 @@ const organizationApi = commonApi.injectEndpoints({ method: 'GET', params: params, }), + transformResponse: (response: ReturnedData) => { + return mapReturnedData(response, (query) => { + const transformedQuery = structuredClone(query) as unknown as TableTypeStudent + transformedQuery.id = query.body.id + transformedQuery.student_name = query.body.name + transformedQuery.student_phone = query.additional.phone + transformedQuery.parent_phone = query.recipient.parent_phones.replaceAll(',', '\n') + transformedQuery.circle_name = query.sender.name + return transformedQuery + })! + }, providesTags: (result) => providesList(result?.results, 'Student'), }), getAllJoinCircleQueries: build.query, AllStudentJoinCircleQueriesData>({ diff --git a/apps/schools/domains/student/components/createStudentForm/index.tsx b/apps/schools/domains/student/components/createStudentForm/index.tsx index e61141c0..30b656b8 100644 --- a/apps/schools/domains/student/components/createStudentForm/index.tsx +++ b/apps/schools/domains/student/components/createStudentForm/index.tsx @@ -66,8 +66,8 @@ export const CreateStudentForm = () => { - * Ф. И. О. обучающегося - + * Ф. И. О. обучающегося + } name={STUDENT_NAME} className={styles.label} @@ -82,8 +82,8 @@ export const CreateStudentForm = () => { - * Телефон родителя - + * Телефон родителя + } name={PARENT_PHONE} className={styles.label} @@ -122,8 +122,8 @@ export const CreateStudentForm = () => { - * Название кружка - + * Название кружка + } name={CIRCLES} className={styles.label} diff --git a/apps/schools/domains/student/components/studentList/index.tsx b/apps/schools/domains/student/components/studentList/index.tsx index f5af871a..f035c9a2 100644 --- a/apps/schools/domains/student/components/studentList/index.tsx +++ b/apps/schools/domains/student/components/studentList/index.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React, { useCallback, useEffect, useState } from 'react' import { Typography } from 'antd' import router from 'next/router' import styles from './styles/styles.module.scss' @@ -14,8 +14,22 @@ import EmptyWrapper from '@domains/common/components/containers/EmptyWrapper' import { AppRoutes, RoutePath } from '@domains/common/constants/routerEnums' import { defaultPaginationTablePage, defaultPaginationTablePageSize } from '@domains/common/constants/Table' import { scrollToTop } from '@domains/common/utils/scrollInDirection' +import { handlePaginationChange } from '@domains/common/handlers/paginationChange' +import { calculateResults } from '@domains/student/handlers/resultsCalculate' +import { getTotalPages } from '@domains/common/utils/getTotalPages' export function StudentList() { + const [queryPaginationParams, setQueryPaginationParams] = useState({ + invites: { + page: defaultPaginationTablePage, + pageSize: defaultPaginationTablePageSize, + }, + students: { + page: defaultPaginationTablePage, + pageSize: defaultPaginationTablePageSize, + }, + }) + const [searchRequestText, setSearchRequestText] = useState('') const { organizationId } = useOrganization() @@ -23,6 +37,15 @@ export function StudentList() { circle__organization__id: organizationId, status: StatusesEnum.SENT, or_search: createSearchTextForRequest(searchRequestText, searchInvitesColumns), + page: queryPaginationParams.invites.page, + page_size: queryPaginationParams.invites.pageSize, + }) + + const { data: students, isFetching: isFetchingStudents } = useGetAllStudentsQuery({ + circle__organization: organizationId, + or_search: createSearchTextForRequest(searchRequestText, searchStudentsColumns), + page: queryPaginationParams.students.page, + page_size: queryPaginationParams.students.pageSize, }) const [paginationParams, setPaginationParams] = useState({ @@ -30,40 +53,32 @@ export function StudentList() { pageSize: defaultPaginationTablePageSize, }) - const { data: students, isFetching: isFetchingStudents } = useGetAllStudentsQuery({ - circle__organization: organizationId, - or_search: createSearchTextForRequest(searchRequestText, searchStudentsColumns), - page: paginationParams.page, - page_size: paginationParams.pageSize, - }) + const resultsCalculate = useCallback( + () => calculateResults(paginationParams, { invites, students }), + [paginationParams, invites, students], + ) const data = { count: (invites?.count ?? 0) + (students?.count ?? 0), next: invites?.next ?? '', previous: invites?.previous ?? '', - results: (invites?.results ?? []) - .map( - (x) => - ({ - id: x.body.id, - student_name: x.body.name, - student_phone: x.additional.phone, - parent_phone: x.recipient.parent_phones.replaceAll(',', '\n'), - circle_name: x.sender.name, - }) as RowType, - ) - .concat( - (students?.results ?? []).map( - (x) => - ({ - id: x.id, - student_name: x.name, - student_phone: x.student_profile.phone, - parent_phone: x.student_profile.parent_phones?.replaceAll(',', '\n'), - circle_name: x.circle.name, - }) as RowType, - ), - ), + results: resultsCalculate(), + } + + const handlePageChange = (newPage: number, newPageSize: number) => { + handlePaginationChange( + setPaginationParams, + setQueryPaginationParams as any, + { + invites: invites?.count, + students: students?.count, + }, + newPage, + newPageSize, + defaultPaginationTablePage, + defaultPaginationTablePageSize, + scrollToTop, + ) } return ( @@ -99,13 +114,12 @@ export function StudentList() { pagination={{ current: paginationParams.page, pageSize: paginationParams.pageSize, - total: students?.count, + total: getTotalPages( + { invites: { count: invites?.count }, students: { count: students?.count } }, + paginationParams.pageSize, + ), onChange: (page, pageSize) => { - setPaginationParams({ - page, - pageSize, - }) - scrollToTop() + handlePageChange(page, pageSize) }, }} filterFields={['circle_name']} diff --git a/apps/schools/domains/student/handlers/resultsCalculate.ts b/apps/schools/domains/student/handlers/resultsCalculate.ts new file mode 100644 index 00000000..b1fcde23 --- /dev/null +++ b/apps/schools/domains/student/handlers/resultsCalculate.ts @@ -0,0 +1,23 @@ +import { RowType } from '@domains/student/components/studentList/interfaces' + +export const calculateResults = ( + paginationParams: { page: number; pageSize: number }, + data: { + [key: string]: { count: number | undefined; results: any[] } | undefined + }, +): RowType[] => { + const dataArray = Object.values(data) + const pageIndex = paginationParams.page - 1 + let offset = 0 + + for (const item of dataArray) { + const count = item?.count ?? 0 + const pageCount = Math.ceil(count / paginationParams.pageSize) + if (pageIndex < offset + pageCount) { + return item?.results ?? [] + } + offset += pageCount + } + + return [] +} diff --git a/apps/schools/domains/user/components/auth/registerForm/index.tsx b/apps/schools/domains/user/components/auth/registerForm/index.tsx index a9c1c849..2bd20dd7 100644 --- a/apps/schools/domains/user/components/auth/registerForm/index.tsx +++ b/apps/schools/domains/user/components/auth/registerForm/index.tsx @@ -1,5 +1,5 @@ import { Col, Form, Row } from 'antd' -import React, {PropsWithChildren, useCallback, useContext, useEffect, useState} from 'react' +import React, { PropsWithChildren, useCallback, useContext, useEffect, useState } from 'react' import styles from '../styles/formStyles.module.scss' import { ResponsiveCol } from '../containers/ResponsiveCol' @@ -10,9 +10,9 @@ import { IRegisterFormProps } from './interfaces' import { BUTTON_FORM_GUTTER_20 } from '../constants/styles' import { FirebaseReCaptchaContext } from '@domains/user/providers/firebaseReCaptchaProvider' import { registrationHandler } from '@domains/user/handlers/auth/register' -import {useUsersMutation} from '@domains/user/redux/userApi' -import {useUpdateEmployeeProfileByIdMutation} from "@domains/employee/redux/employeeApi"; -import {useLazyGetUserQuery} from "@domains/user/redux/authenticationApi"; +import { useUsersMutation } from '@domains/user/redux/userApi' +import { useUpdateEmployeeProfileByIdMutation } from '@domains/employee/redux/employeeApi' +import { useLazyGetUserQuery } from '@domains/user/redux/authenticationApi' const RequiredFlagWrapper: React.FC> = (props) => { return
{props.children}
@@ -30,35 +30,34 @@ export const RegisterForm: React.FC = ({ onFinish, onError } signInByPhone: () => {}, } - const [updateProfile] = useUpdateEmployeeProfileByIdMutation(); - const [getLazyUser, {data}] = useLazyGetUserQuery(); + const [updateProfile] = useUpdateEmployeeProfileByIdMutation() + const [getLazyUser, { data }] = useLazyGetUserQuery() const registerComplete = useCallback(() => { - const { password } = form.getFieldsValue(['password']); + const { password } = form.getFieldsValue(['password']) - registrationHandler(phone, password, userRegistration, onError, form) - .then(async () => { - await getLazyUser({}); - }); - }, [form, signInByPhone, getLazyUser]); + registrationHandler(phone, password, userRegistration, onError, form).then(async () => { + await getLazyUser({}) + }) + }, [form, signInByPhone, getLazyUser]) useEffect(() => { - const { email } = form.getFieldsValue(['email']); - const { name } = form.getFieldsValue(['name']); + const { email } = form.getFieldsValue(['email']) + const { name } = form.getFieldsValue(['name']) if (data && data.user.employee_profile?.id) { const updateEmail = { employee_profile_id: data.user.employee_profile.id, name: name, - email: email - }; - updateProfile(updateEmail); - onFinish(); + email: email, + } + updateProfile(updateEmail) + onFinish() } - }, [data, updateProfile]); + }, [data, updateProfile]) const initialValues = { - phone + phone, } return ( @@ -104,10 +103,7 @@ export const RegisterForm: React.FC = ({ onFinish, onError } data-cy='register-email-item' validateFirst > - +
diff --git a/apps/schools/domains/user/handlers/auth/register.ts b/apps/schools/domains/user/handlers/auth/register.ts index 648de720..356a3ef0 100644 --- a/apps/schools/domains/user/handlers/auth/register.ts +++ b/apps/schools/domains/user/handlers/auth/register.ts @@ -80,14 +80,14 @@ export async function registrationHandler( password: string, userRegistrationMutation: any, onError: () => void, - formComponent: FormInstance + formComponent: FormInstance, ) { let token = localStorage.getItem('token') const cookies = new Cookies() cookies.remove('jwtToken') - const { email } = formComponent.getFieldsValue(['email']); - const { name } = formComponent.getFieldsValue(['name']); + const { email } = formComponent.getFieldsValue(['email']) + const { name } = formComponent.getFieldsValue(['name']) let response = await withLoadingMessage(LoadingMsg, userRegistrationMutation, { token: token, diff --git a/apps/schools/domains/user/redux/userApi.ts b/apps/schools/domains/user/redux/userApi.ts index 1bf4f97a..f5463a82 100644 --- a/apps/schools/domains/user/redux/userApi.ts +++ b/apps/schools/domains/user/redux/userApi.ts @@ -53,9 +53,9 @@ const userApi = commonApi.injectEndpoints({ method: 'PATCH', body: { otp: data.otp }, }), - }) + }), }), }) -export const { useTokenMutation, useVerifyMutation, useUsersMutation, useResetPasswordMutation, useResendMutation} = +export const { useTokenMutation, useVerifyMutation, useUsersMutation, useResetPasswordMutation, useResendMutation } = userApi