diff --git a/apps/schools/dev.Dockerfile b/apps/schools/dev.Dockerfile index fb5281fb..1f20126f 100644 --- a/apps/schools/dev.Dockerfile +++ b/apps/schools/dev.Dockerfile @@ -1,4 +1,4 @@ -FROM node:16-alpine AS runner +FROM node:18-alpine AS runner RUN node -v diff --git a/apps/schools/domains/circle/components/circleList/index.tsx b/apps/schools/domains/circle/components/circleList/index.tsx index 83b7faaf..d9a1c062 100644 --- a/apps/schools/domains/circle/components/circleList/index.tsx +++ b/apps/schools/domains/circle/components/circleList/index.tsx @@ -14,20 +14,23 @@ import { mapReturnedData } from '@domains/common/redux/utils' import { HighlightText } from '@domains/common/components/table/forming' import { getVarsForAddressColumn } from '@domains/common/utils/geo' import { AppRoutes, RoutePath } from '@domains/common/constants/routerEnums' +import { defaultPaginationTablePage, defaultPaginationTablePageSize } from '@domains/common/constants/Table' +import { scrollToTop } from '@domains/common/utils/scrollInDirection' export function CircleList() { const [searchRequestText, setSearchRequestText] = useState('') const { organizationId } = useOrganization() + const [paginationParams, setPaginationParams] = useState({ + page: defaultPaginationTablePage, + pageSize: defaultPaginationTablePageSize, + }) + const { data: circles, isFetching: isFetching } = useGetAllCirclesQuery({ organization_id: organizationId, or_search: createSearchTextForRequest(searchRequestText, searchStudentsColumns), - }) - - const reformattedData = mapReturnedData(circles, (circle) => { - const transformedCircle = structuredClone(circle) as unknown as TableType - transformedCircle.accepted_count = circle.student_profile_queries.ACCEPTED - return transformedCircle + page: paginationParams.page, + page_size: paginationParams.pageSize, }) return ( @@ -58,7 +61,19 @@ export function CircleList() { ['Адрес', 'address'], ['Кол-во принятых заявок', 'accepted_count'], ]} - data={reformattedData} + pagination={{ + current: paginationParams.page, + pageSize: paginationParams.pageSize, + total: circles?.count, + onChange: (page, pageSize) => { + setPaginationParams({ + page, + pageSize, + }) + scrollToTop() + }, + }} + data={circles} isLoading={isFetching} mainRoute={RoutePath[AppRoutes.CIRCLE_LIST]} searchFields={['name', 'address']} diff --git a/apps/schools/domains/circle/components/circleList/interfaces.ts b/apps/schools/domains/circle/components/circleList/interfaces.ts index 20430d06..0924594e 100644 --- a/apps/schools/domains/circle/components/circleList/interfaces.ts +++ b/apps/schools/domains/circle/components/circleList/interfaces.ts @@ -6,7 +6,7 @@ export interface RowType { } export interface TableType { - id?: string + id: string name: string address: string accepted_count: number diff --git a/apps/schools/domains/circle/components/currentCircle/index.tsx b/apps/schools/domains/circle/components/currentCircle/index.tsx index 7b0306bb..48cbcb25 100644 --- a/apps/schools/domains/circle/components/currentCircle/index.tsx +++ b/apps/schools/domains/circle/components/currentCircle/index.tsx @@ -13,14 +13,13 @@ import { useOrganization } from '@domains/organization/providers/organizationPro import { CARD_HEAD_STYLE } from '@domains/circle/components/currentCircle/styles/styles' import { Table } from '@domains/common/components/table' import { createSearchTextForRequest } from '@domains/common/utils/searchText' -import { mapReturnedData } from '@domains/common/redux/utils' import { ActionBar } from '@domains/common/components/stickyBlock/actionBar' import { sumObjectValues } from '@domains/common/utils/sumObjectValues' import android from '@public/image/Android.svg' import { searchColumns } from './constants' -import { CurrentCircleRowType } from './interfaces' +import { RowType, TableType } from './interfaces' import styles from './styles/styles.module.scss' import { getVarsForAddressColumn } from '@domains/common/utils/geo' import { StatusesEnum } from '@domains/common/constants/Enums' @@ -53,16 +52,6 @@ const CurrentCircle = () => { CANCELED: 0, } - const reformattedData = mapReturnedData(students, (student) => { - const transformedCircle = structuredClone(student) as unknown as CurrentCircleRowType - transformedCircle.id = student.id - transformedCircle.student_name = student.name - transformedCircle.student_phone = student.student_profile.phone - transformedCircle.parent_names = student.student_profile.parent_names?.replaceAll(',', ',\n') - transformedCircle.parent_phones = student.student_profile.parent_phones?.replaceAll(',', '\n') - return transformedCircle - }) - const countAllQueries = sumObjectValues(queriesCount) const addressVars = getVarsForAddressColumn(circle?.circle.address ?? '') @@ -134,14 +123,14 @@ const CurrentCircle = () => {
- + columnsTitlesAndKeys={[ ['Ф. И. О обучающегося', 'student_name'], ['Телефон обучающегося', 'student_phone'], ['Ф. И. О родителя', 'parent_names'], ['Телефон родителя', 'parent_phones'], ]} - data={reformattedData} + data={students} mainRoute={RoutePath[AppRoutes.STUDENT_LIST]} isLoading={isLoading} needNumbering={true} diff --git a/apps/schools/domains/circle/components/currentCircle/interfaces.ts b/apps/schools/domains/circle/components/currentCircle/interfaces.ts index 04ae9bf0..18b1f49c 100644 --- a/apps/schools/domains/circle/components/currentCircle/interfaces.ts +++ b/apps/schools/domains/circle/components/currentCircle/interfaces.ts @@ -1,4 +1,12 @@ -export interface CurrentCircleRowType { +export interface RowType { + id?: string + student_name: string + student_phone?: string + parent_names?: string + parent_phones?: string +} + +export interface TableType { id?: string student_name: string student_phone?: string diff --git a/apps/schools/domains/circle/redux/circleApi.ts b/apps/schools/domains/circle/redux/circleApi.ts index c4fb8c49..f2a16777 100644 --- a/apps/schools/domains/circle/redux/circleApi.ts +++ b/apps/schools/domains/circle/redux/circleApi.ts @@ -9,6 +9,8 @@ import { ChangeCircleData, } from './interfaces' import { GetCircle, GetQueryStatus, GetStudent } from '@domains/common/redux/serializers' +import { mapReturnedData } from '@domains/common/redux/utils' +import { TableType as TableTypeCurrentCircle } from '@domains/circle/components/currentCircle/interfaces' const circleApi = commonApi.injectEndpoints({ endpoints: (build) => ({ @@ -43,12 +45,26 @@ const circleApi = commonApi.injectEndpoints({ }), providesTags: (result, error, arg) => [{ type: 'Circle', id: arg.circle_id }], }), - getCircleStudents: build.query, CircleStudentsData>({ + getCircleStudents: build.query, CircleStudentsData>({ query: (params) => ({ url: `/organization-management/circles/${params.circle_id}/students`, method: 'GET', params: params, }), + transformResponse: (response: ReturnedData) => { + return mapReturnedData(response, (student: GetStudent) => { + return { + ...student, + student_name: student.name, + student_profile: { + ...student.student_profile, + phone: student.student_profile.phone, + parent_names: student.student_profile.parent_names, + parent_phones: student.student_profile.parent_phones, + }, + } + })! + }, providesTags: (result, error, arg) => [{ type: 'Circle', id: arg.circle_id }], }), deleteCircle: build.mutation<{ circle: GetCircle }, CircleData>({ diff --git a/apps/schools/domains/common/components/table/forming.tsx b/apps/schools/domains/common/components/table/forming.tsx index a1c1668f..3ba0aa9a 100644 --- a/apps/schools/domains/common/components/table/forming.tsx +++ b/apps/schools/domains/common/components/table/forming.tsx @@ -1,5 +1,5 @@ import { ColumnType } from 'antd/lib/table/interface' -import React from 'react' +import React, { ReactElement } from 'react' import styles from './styles/styles.module.scss' import { getSearchText } from '@domains/common/utils/searchText' import { @@ -53,7 +53,7 @@ export function useGenerateFullColumns( const obj = (record as any)[column.dataIndex] if (!isReactElement(obj)) return obj === value - return obj.props.text === value + return (obj as ReactElement).props.text === value }, } } diff --git a/apps/schools/domains/common/constants/Cookies.ts b/apps/schools/domains/common/constants/Cookies.ts new file mode 100644 index 00000000..596a4336 --- /dev/null +++ b/apps/schools/domains/common/constants/Cookies.ts @@ -0,0 +1 @@ +export const oneYearExpiresDate = new Date(Date.now() + 60 * 60 * 24 * 365 * 1000) diff --git a/apps/schools/domains/common/constants/Table.ts b/apps/schools/domains/common/constants/Table.ts index 4f177cec..3004ba17 100644 --- a/apps/schools/domains/common/constants/Table.ts +++ b/apps/schools/domains/common/constants/Table.ts @@ -1 +1,3 @@ export const typeTable = ['tableWithoutSearch'] +export const defaultPaginationTablePage = 1 +export const defaultPaginationTablePageSize = 10 diff --git a/apps/schools/domains/common/utils/scrollInDirection.ts b/apps/schools/domains/common/utils/scrollInDirection.ts new file mode 100644 index 00000000..6175b7c1 --- /dev/null +++ b/apps/schools/domains/common/utils/scrollInDirection.ts @@ -0,0 +1,7 @@ +export function scrollToTop() { + window.scrollTo({ top: 0, left: 0 }) +} + +export function scrollToBottom() { + window.scrollTo({ top: document.body.scrollHeight, left: 0 }) +} diff --git a/apps/schools/domains/employee/components/employeeList/index.tsx b/apps/schools/domains/employee/components/employeeList/index.tsx index 9aaccc0e..7247578b 100644 --- a/apps/schools/domains/employee/components/employeeList/index.tsx +++ b/apps/schools/domains/employee/components/employeeList/index.tsx @@ -11,14 +11,23 @@ import { GetListEmployee } from '@domains/employee/redux/interfaces' import { RowType } from '@domains/employee/components/employeeList/interfaces' import { searchColumns } from '@domains/employee/components/employeeList/constants' import { AppRoutes, RoutePath } from '@domains/common/constants/routerEnums' +import { defaultPaginationTablePage, defaultPaginationTablePageSize } from '@domains/common/constants/Table' +import { scrollToTop } from '@domains/common/utils/scrollInDirection' export function EmployeeList() { const [searchRequestText, setSearchRequestText] = useState('') const { organizationId } = useOrganization() - const { data, isLoading } = useGetAllEmployeesQuery({ + const [paginationParams, setPaginationParams] = useState({ + page: defaultPaginationTablePage, + pageSize: defaultPaginationTablePageSize, + }) + + const { data, isFetching } = useGetAllEmployeesQuery({ organization: organizationId, or_search: createSearchTextForRequest(searchRequestText, searchColumns), + page: paginationParams.page, + page_size: paginationParams.pageSize, }) return ( @@ -41,8 +50,20 @@ export function EmployeeList() { ['Должность', 'position'], ['Телефон', 'phone'], ]} + pagination={{ + current: paginationParams.page, + pageSize: paginationParams.pageSize, + total: data?.count, + onChange: (page, pageSize) => { + setPaginationParams({ + page, + pageSize, + }) + scrollToTop() + }, + }} data={data} - isLoading={isLoading} + isLoading={isFetching} mainRoute={RoutePath[AppRoutes.EMPLOYEE_LIST]} searchFields={searchColumns} searchRequestText={searchRequestText} diff --git a/apps/schools/domains/organization/redux/organizationApi.ts b/apps/schools/domains/organization/redux/organizationApi.ts index 2a4e2af2..6dcafe3d 100644 --- a/apps/schools/domains/organization/redux/organizationApi.ts +++ b/apps/schools/domains/organization/redux/organizationApi.ts @@ -33,6 +33,10 @@ import { UpdateOrganizationInviteEmployee, } from '@domains/common/redux/serializers' import { GetTicket, GetTicketData, GetTicketsData } from '@domains/ticket/redux/serializers' +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' const organizationApi = commonApi.injectEndpoints({ endpoints: (build) => ({ @@ -68,12 +72,19 @@ const organizationApi = commonApi.injectEndpoints({ }), providesTags: (result) => providesList(result?.results, 'Student'), }), - getAllCircles: build.query, GetOrganizationCircleListData>({ + getAllCircles: build.query, GetOrganizationCircleListData>({ query: (params) => ({ url: `/organization-management/organizations/${params.organization_id}/circles`, method: 'GET', params: params, }), + transformResponse: (response: ReturnedData) => { + return mapReturnedData(response, (circle) => { + const transformedCircle = structuredClone(circle) as unknown as TableTypeCircle + transformedCircle.accepted_count = circle.student_profile_queries.ACCEPTED + return transformedCircle + })! + }, providesTags: (result) => providesList(result?.results, 'Circle'), }), getCurrentCircle: build.query<{ circle: GetOrganizationCircleList }, GetCurrentCircleData>({ @@ -165,6 +176,16 @@ const organizationApi = commonApi.injectEndpoints({ method: 'GET', params: params, }), + transformResponse: (response: ReturnedData) => { + return mapReturnedData(response, (query) => { + const transformedQuery = structuredClone(query) as unknown as TableTypeQuery + transformedQuery.parent_name = query.additional.parent_name + transformedQuery.parent_phone = query.additional.parent_phone + transformedQuery.circle_name = query.recipient.name + transformedQuery.student_name = query.body.name + return transformedQuery + })! + }, providesTags: (result) => providesList(result?.results, 'StudentJoinCircleQuery'), }), getOrganizationAnalytics: build.query<{ analytics: GetAnalytics }, GetAnalyticsData>({ @@ -183,12 +204,25 @@ const organizationApi = commonApi.injectEndpoints({ }), providesTags: ['Ticket'], }), - getAllTickets: build.query, GetTicketsData>({ + getAllTickets: build.query, GetTicketsData>({ query: (params) => ({ url: `/organization-management/organizations/${params.organization_id}/family-tickets`, method: 'GET', params: params, }), + transformResponse: (response: ReturnedData) => { + return mapReturnedData(response, (query) => { + return { + ...query, + content: query.last_comment.value, + sender: query.sender?.name, + contentTrimmed: + query.last_comment.value.length > 200 + ? query.last_comment.value.slice(0, 200) + '…' + : query.last_comment.value, + } as TableTypeTickets + })! + }, providesTags: ['Ticket'], }), getTicket: build.query<{ ticket: GetTicket }, GetTicketData>({ diff --git a/apps/schools/domains/query/components/queryList/index.tsx b/apps/schools/domains/query/components/queryList/index.tsx index e466a7b7..e140c8b8 100644 --- a/apps/schools/domains/query/components/queryList/index.tsx +++ b/apps/schools/domains/query/components/queryList/index.tsx @@ -11,7 +11,6 @@ import { useGetOrganizationAnalyticsQuery, } from '@domains/organization/redux/organizationApi' import EmptyWrapper from '@domains/common/components/containers/EmptyWrapper' -import { mapReturnedData } from '@domains/common/redux/utils' import { HighlightText } from '@domains/common/components/table/forming' import { isReactElement } from '@domains/common/utils/react' import { sumObjectValues } from '@domains/common/utils/sumObjectValues' @@ -21,6 +20,8 @@ import { useQueryState } from 'next-usequerystate' import { parseAsArrayOf, parseAsString } from 'next-usequerystate' import { AppRoutes, RoutePath } from '@domains/common/constants/routerEnums' import SearchInput from '@domains/common/components/searchInput' +import { defaultPaginationTablePage, defaultPaginationTablePageSize } from '@domains/common/constants/Table' +import { scrollToTop } from '@domains/common/utils/scrollInDirection' export function QueryList() { const { organizationId } = useOrganization() @@ -69,9 +70,16 @@ export function QueryList() { return items }, [analytics, statuses]) - const { data: queries, isLoading: isQueriesLoading } = useGetAllJoinCircleQueriesQuery({ + const [paginationParams, setPaginationParams] = useState({ + page: defaultPaginationTablePage, + pageSize: defaultPaginationTablePageSize, + }) + + const { data: queries, isFetching: isQueriesFetching } = useGetAllJoinCircleQueriesQuery({ circle__organization__id: organizationId, or_search: createSearchTextForRequest(searchRequestText, searchStudentsColumns), + page: paginationParams.page, + page_size: paginationParams.pageSize, }) const countAllQueries = useMemo( @@ -79,24 +87,11 @@ export function QueryList() { [analytics, isAnalyticsLoading], ) - const reformattedData = useMemo( - () => - mapReturnedData(queries, (query) => { - const transformedQuery = structuredClone(query) as unknown as TableType - transformedQuery.parent_name = query.additional.parent_name - transformedQuery.parent_phone = query.additional.parent_phone - transformedQuery.circle_name = query.recipient.name - transformedQuery.student_name = query.body.name - return transformedQuery - }), - [queries], - ) - useEffect(() => { - if (!isQueriesLoading && queries) { + if (!isQueriesFetching && queries) { setIsTableLoading(false) } - }, [isQueriesLoading, queries]) + }, [isQueriesFetching, queries]) const handleSearchChange = useCallback((value: string) => { setIsTableLoading(true) @@ -111,7 +106,7 @@ export function QueryList() { descriptionText={'Дождитесь первой заявки'} pageTitle={'Заявки'} data={queries} - isLoading={isQueriesLoading} + isLoading={isQueriesFetching} searchTrigger={searchRequestText} >
@@ -130,8 +125,20 @@ export function QueryList() { ['Телефон родителя', 'parent_phone'], ['Кружок', 'circle_name'], ]} - data={reformattedData} - isLoading={isQueriesLoading} + pagination={{ + current: paginationParams.page, + pageSize: paginationParams.pageSize, + total: queries?.count, + onChange: (page, pageSize) => { + setPaginationParams({ + page, + pageSize, + }) + scrollToTop() + }, + }} + data={queries} + isLoading={isQueriesFetching} mainRoute={RoutePath[AppRoutes.QUERY_LIST]} searchFields={[ 'created_at', diff --git a/apps/schools/domains/student/components/studentList/index.tsx b/apps/schools/domains/student/components/studentList/index.tsx index f72267d6..f5af871a 100644 --- a/apps/schools/domains/student/components/studentList/index.tsx +++ b/apps/schools/domains/student/components/studentList/index.tsx @@ -12,6 +12,8 @@ import { useGetAllStudentInvitationsQuery, useGetAllStudentsQuery } from '@domai import { StatusesEnum } from '@domains/common/constants/Enums' 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' export function StudentList() { const [searchRequestText, setSearchRequestText] = useState('') @@ -23,9 +25,16 @@ export function StudentList() { or_search: createSearchTextForRequest(searchRequestText, searchInvitesColumns), }) - const { data: students, isLoading: isLoadingStudents } = useGetAllStudentsQuery({ + const [paginationParams, setPaginationParams] = useState({ + page: defaultPaginationTablePage, + pageSize: defaultPaginationTablePageSize, + }) + + const { data: students, isFetching: isFetchingStudents } = useGetAllStudentsQuery({ circle__organization: organizationId, or_search: createSearchTextForRequest(searchRequestText, searchStudentsColumns), + page: paginationParams.page, + page_size: paginationParams.pageSize, }) const data = { @@ -64,7 +73,7 @@ export function StudentList() { buttonText={'Добавить обучающегося'} pageTitle={'Обучающиеся'} data={data} - isLoading={isLoadingStudents || isLoadingInvites} + isLoading={isFetchingStudents || isLoadingInvites} handleRunTask={() => router.push(RoutePath[AppRoutes.STUDENT_CREATE])} searchTrigger={searchRequestText} > @@ -87,9 +96,21 @@ export function StudentList() { ['Телефон обучающегося', 'student_phone'], ['Телефон родителя', 'parent_phone'], ]} + pagination={{ + current: paginationParams.page, + pageSize: paginationParams.pageSize, + total: students?.count, + onChange: (page, pageSize) => { + setPaginationParams({ + page, + pageSize, + }) + scrollToTop() + }, + }} filterFields={['circle_name']} data={data} - isLoading={isLoadingInvites || isLoadingStudents} + isLoading={isLoadingInvites || isFetchingStudents} mainRoute={RoutePath[AppRoutes.STUDENT_LIST]} searchFields={['student_name', 'student_phone', 'parent_phone', 'circle_name']} searchRequestText={searchRequestText} diff --git a/apps/schools/domains/ticket/components/ticketList/index.tsx b/apps/schools/domains/ticket/components/ticketList/index.tsx index 163a6a14..7245b37a 100644 --- a/apps/schools/domains/ticket/components/ticketList/index.tsx +++ b/apps/schools/domains/ticket/components/ticketList/index.tsx @@ -8,7 +8,6 @@ import { RowType, TableType } from './interfaces' import { searchTicketsColumns, StatusDictionary } from './constants' import { useGetAllTicketsQuery, useGetTicketsAnalyticsQuery } from '@domains/organization/redux/organizationApi' import EmptyWrapper from '@domains/common/components/containers/EmptyWrapper' -import { mapReturnedData } from '@domains/common/redux/utils' import { HighlightText } from '@domains/common/components/table/forming' import { isReactElement } from '@domains/common/utils/react' import { BubbleFilter } from '@domains/common/components/bubbleFilter' @@ -20,6 +19,8 @@ import Image from 'next/image' import dot from '@public/icons/dot.svg' import SearchInput from '@domains/common/components/searchInput' import { FilterValue, SorterResult, TableCurrentDataSource } from 'antd/es/table/interface' +import { defaultPaginationTablePage, defaultPaginationTablePageSize } from '@domains/common/constants/Table' +import { scrollToTop } from '@domains/common/utils/scrollInDirection' type HandleInputChange = (text: React.ChangeEvent | string) => void type HandleChange = ( @@ -42,7 +43,7 @@ export function TicketList() { }), ) - const { data: analytics, isLoading: isAnalyticsLoading } = useGetTicketsAnalyticsQuery({ + const { data: analytics } = useGetTicketsAnalyticsQuery({ organization_id: organizationId, }) @@ -69,26 +70,23 @@ export function TicketList() { } as BubbleFilterListItem } - const { data: tickets, isLoading: isTicketsLoading } = useGetAllTicketsQuery({ - organization_id: organizationId, - or_search: createSearchTextForRequest(searchRequestText, searchTicketsColumns), + const [paginationParams, setPaginationParams] = useState({ + page: defaultPaginationTablePage, + pageSize: defaultPaginationTablePageSize, }) - const reformattedData = mapReturnedData(tickets, (query) => { - const transformedQuery = structuredClone(query) as unknown as TableType - transformedQuery.content = query.last_comment.value - if (transformedQuery.content.length > 200) { - transformedQuery.content = transformedQuery.content.slice(0, 200) + '…' - } - transformedQuery.sender = 'Семья ' + query.sender?.name - return transformedQuery + const { data: tickets, isFetching: isTicketsFetching } = useGetAllTicketsQuery({ + organization_id: organizationId, + or_search: createSearchTextForRequest(searchRequestText, searchTicketsColumns), + page: paginationParams.page, + page_size: paginationParams.pageSize, }) useEffect(() => { - if (!isTicketsLoading) { + if (!isTicketsFetching && tickets) { setIsTableLoading(false) } - }, [isTicketsLoading]) + }, [isTicketsFetching, tickets]) const handleInputChange: HandleInputChange = useCallback( (text) => { @@ -123,7 +121,7 @@ export function TicketList() { descriptionText={'Дождитесь первого обращения'} pageTitle={'Обращения'} data={tickets} - isLoading={isTicketsLoading} + isLoading={isTicketsFetching} searchTrigger={searchRequestText} >
@@ -134,16 +132,28 @@ export function TicketList() {
loading={isTableLoading} + pagination={{ + current: paginationParams.page, + pageSize: paginationParams.pageSize, + total: tickets?.count, + onChange: (page, pageSize) => { + setPaginationParams({ + page, + pageSize, + }) + scrollToTop() + }, + }} customType={'tableWithoutSearch'} columnsTitlesAndKeys={[ ['Создано', 'created_at'], ['Статус', 'status'], ['Содержание', 'content'], - ['Отправитель', 'sender'], + ['Семья', 'sender'], ]} customWidths={[10, 10, 40, 30]} - data={reformattedData} - isLoading={isTicketsLoading} + data={tickets} + isLoading={isTicketsFetching} mainRoute={RoutePath[AppRoutes.TICKETS_LIST]} searchFields={['created_at', 'content', 'sender']} customFields={{ @@ -158,12 +168,11 @@ export function TicketList() { return (
- {reformattedData?.results[index].unread_sender_comments_count > 0 && ( + {(tickets?.results[index] as TableType).unread_sender_comments_count > 0 && (
{'Unread
)} -
diff --git a/apps/schools/domains/ticket/components/ticketList/interfaces.ts b/apps/schools/domains/ticket/components/ticketList/interfaces.ts index 23ecccc0..b5a81bce 100644 --- a/apps/schools/domains/ticket/components/ticketList/interfaces.ts +++ b/apps/schools/domains/ticket/components/ticketList/interfaces.ts @@ -12,4 +12,5 @@ export interface TableType { status?: string content?: string sender?: string + unread_sender_comments_count: number } diff --git a/apps/schools/domains/ticket/redux/serializers.ts b/apps/schools/domains/ticket/redux/serializers.ts index c8b9edc7..2290bbc9 100644 --- a/apps/schools/domains/ticket/redux/serializers.ts +++ b/apps/schools/domains/ticket/redux/serializers.ts @@ -6,7 +6,7 @@ export interface GetTicket { last_comment: GetTicketComment recipient: GetOrganization created_at: string - unread_sender_comments_count?: string + unread_sender_comments_count: number unread_recipient_comments_count?: string sender?: GetFamily status?: QueriesTypes diff --git a/apps/schools/domains/user/components/auth/containers/PopupCookie.tsx b/apps/schools/domains/user/components/auth/containers/PopupCookie.tsx index a93dcc70..11c89f04 100644 --- a/apps/schools/domains/user/components/auth/containers/PopupCookie.tsx +++ b/apps/schools/domains/user/components/auth/containers/PopupCookie.tsx @@ -7,6 +7,7 @@ import { Button } from '@domains/common/components/button' import { Typography } from 'antd' import Cookie from 'universal-cookie' import { COOKIE_AGREEMENT_KEY } from '@domains/user/components/auth/constants/variables' +import { oneYearExpiresDate } from '@domains/common/constants/Cookies' export const PopupCookie = () => { const cookies = new Cookie() @@ -15,7 +16,7 @@ export const PopupCookie = () => { const [cookiesNotAccepted, setCookiesNotAccepted] = useState(true) const acceptCookieAgreement = useCallback(() => { - cookies.set(COOKIE_AGREEMENT_KEY, 'true') + cookies.set(COOKIE_AGREEMENT_KEY, 'true', { expires: oneYearExpiresDate }) setCookiesNotAccepted(false) setIsShown(false) }, []) diff --git a/apps/schools/domains/user/components/auth/registerForm/index.tsx b/apps/schools/domains/user/components/auth/registerForm/index.tsx index 4cb4b579..a9c1c849 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, 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,7 +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 {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}
@@ -28,12 +30,36 @@ export const RegisterForm: React.FC = ({ onFinish, onError } signInByPhone: () => {}, } - const registerComplete = useCallback(async () => { - const { password } = form.getFieldsValue(['password']) - registrationHandler(phone, password, userRegistration, onFinish, onError, form) - }, [form, signInByPhone]) + const [updateProfile] = useUpdateEmployeeProfileByIdMutation(); + const [getLazyUser, {data}] = useLazyGetUserQuery(); - const initialValues = { phone } + const registerComplete = useCallback(() => { + const { password } = form.getFieldsValue(['password']); + + registrationHandler(phone, password, userRegistration, onError, form) + .then(async () => { + await getLazyUser({}); + }); + }, [form, signInByPhone, getLazyUser]); + + useEffect(() => { + 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(); + } + }, [data, updateProfile]); + + const initialValues = { + phone + } return (
= ({ onFinish, onError } data-cy='register-email-item' validateFirst > - + diff --git a/apps/schools/domains/user/components/profile/profileEdit/hooks.ts b/apps/schools/domains/user/components/profile/profileEdit/hooks.ts index 6ba350f6..20c0d407 100644 --- a/apps/schools/domains/user/components/profile/profileEdit/hooks.ts +++ b/apps/schools/domains/user/components/profile/profileEdit/hooks.ts @@ -1,9 +1,10 @@ import { useMemo } from 'react' import { ValidatorsMap } from '@domains/common/redux/interfaces' import { - PersonNameMustContainMsg, PersonNameMustNotStartOrAndMsg, + PersonNameMustContainMsg, + PersonNameMustNotStartOrAndMsg, PleaseInputYourEmailMsg, - PleaseInputYourNameMsg + PleaseInputYourNameMsg, } from '@domains/user/components/auth/constants/message' import { getGreaterValidator } from '@domains/common/utils/validators' import { USER_EMAIL, USER_NAME } from '@domains/user/components/profile/profileEdit/constants' diff --git a/apps/schools/domains/user/handlers/auth/register.ts b/apps/schools/domains/user/handlers/auth/register.ts index 17542557..648de720 100644 --- a/apps/schools/domains/user/handlers/auth/register.ts +++ b/apps/schools/domains/user/handlers/auth/register.ts @@ -13,6 +13,7 @@ import { import { withLoadingMessage } from '@domains/common/utils/loading' import Cookies from 'universal-cookie' import { AppRoutes, RoutePath } from '@domains/common/constants/routerEnums' +import { oneYearExpiresDate } from '@domains/common/constants/Cookies' export async function tokenHandler( recaptchaToken: string, @@ -78,22 +79,25 @@ export async function registrationHandler( phone: string, password: string, userRegistrationMutation: any, - onFinish: () => void, 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']); + let response = await withLoadingMessage(LoadingMsg, userRegistrationMutation, { token: token, - name: phone, + name: name, + email: email, password: password, }) if (!('error' in response)) { - cookies.set('jwtToken', response.data.token, { path: '/' }) + cookies.set('jwtToken', response.data.token, { path: '/', expires: oneYearExpiresDate }) message.success(SuccessRegistrationMsg) - onFinish() } else if (response.error?.status === 401) { message.error(PleaseReloadPageMsg) onError() diff --git a/apps/schools/domains/user/handlers/auth/signin.ts b/apps/schools/domains/user/handlers/auth/signin.ts index d62be8f3..77fc79dd 100644 --- a/apps/schools/domains/user/handlers/auth/signin.ts +++ b/apps/schools/domains/user/handlers/auth/signin.ts @@ -2,6 +2,7 @@ import { FormInstance, message } from 'antd' import { LoadingMsg, SuccessSignInMsg, WrongLoginOrPasswordMsg } from '@domains/user/components/auth/constants/message' import { withLoadingMessage } from '@domains/common/utils/loading' import Cookies from 'universal-cookie' +import { oneYearExpiresDate } from '@domains/common/constants/Cookies' export async function loginHandler(phone: string, password: string, login: any, formComponent: FormInstance) { const cookies = new Cookies() @@ -11,7 +12,7 @@ export async function loginHandler(phone: string, password: string, login: any, password: password, }) if ('data' in response) { - cookies.set('jwtToken', response.data.token, { path: '/' }) + cookies.set('jwtToken', response.data.token, { path: '/', expires: oneYearExpiresDate }) message.success(SuccessSignInMsg) window.location.href = '/' } else if (response.error?.data.error.message.indexOf('non_field_errors') >= 0) { diff --git a/apps/schools/domains/user/redux/authenticationApi.ts b/apps/schools/domains/user/redux/authenticationApi.ts index c4eaf1e1..8ecc265d 100644 --- a/apps/schools/domains/user/redux/authenticationApi.ts +++ b/apps/schools/domains/user/redux/authenticationApi.ts @@ -45,6 +45,7 @@ export const { useLoginMutation, useLogoutMutation, useGetUserQuery, + useLazyGetUserQuery, useUpdateUserMutation, useUpdatePasswordMutation, } = authenticationApi diff --git a/apps/schools/domains/user/redux/userApi.ts b/apps/schools/domains/user/redux/userApi.ts index f5463a82..1bf4f97a 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 diff --git a/apps/schools/prod.Dockerfile b/apps/schools/prod.Dockerfile index b10ae5c4..1f20126f 100644 --- a/apps/schools/prod.Dockerfile +++ b/apps/schools/prod.Dockerfile @@ -1,4 +1,4 @@ -FROM node:16-alpine AS runner +FROM node:18-alpine AS runner RUN node -v @@ -13,4 +13,4 @@ WORKDIR /opt/app RUN yarn install #RUN ["chmod", "+x", "/opt/app/setEnv.sh"] -CMD /bin/sh -c 'source /opt/app/setEnv.sh'; yarn build; yarn start \ No newline at end of file +CMD /bin/sh -c 'source /opt/app/setEnv.sh'; yarn build; yarn start diff --git a/apps/schools/tsconfig.json b/apps/schools/tsconfig.json index dab73a1e..88e6bf69 100644 --- a/apps/schools/tsconfig.json +++ b/apps/schools/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "es2018", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true,