diff --git a/apps/schools/domains/circle/components/currentCircle/index.tsx b/apps/schools/domains/circle/components/currentCircle/index.tsx
index 29539644..e1dffb62 100644
--- a/apps/schools/domains/circle/components/currentCircle/index.tsx
+++ b/apps/schools/domains/circle/components/currentCircle/index.tsx
@@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react'
-import router, { useRouter } from 'next/router'
+import { useRouter } from 'next/router'
import { Col, Row, Typography } from 'antd'
import Image from 'next/image'
import Link from 'next/link'
@@ -23,7 +23,7 @@ import { searchColumns } from './constants'
import { CurrentCircleRowType } from './interfaces'
import styles from './styles/styles.module.scss'
import { getVarsForAddressColumn } from '@domains/common/utils/geo'
-import { QueryStatuses } from '@domains/common/constants/Enums'
+import { StatusesEnum } from '@domains/common/constants/Enums'
import { ErrorType } from '@store/commonApi'
import { AppRoutes, RoutePath } from '@domains/common/constants/routerEnums'
@@ -98,7 +98,7 @@ const CurrentCircle = () => {
Принято
{queriesCount.ACCEPTED}
@@ -108,7 +108,7 @@ const CurrentCircle = () => {
На рассмотрении
{queriesCount.IN_PROGRESS}
diff --git a/apps/schools/domains/common/components/bubbleFilter/styles/styles.module.scss b/apps/schools/domains/common/components/bubbleFilter/styles/styles.module.scss
index 50e137a3..d680d6d9 100644
--- a/apps/schools/domains/common/components/bubbleFilter/styles/styles.module.scss
+++ b/apps/schools/domains/common/components/bubbleFilter/styles/styles.module.scss
@@ -6,7 +6,6 @@
align-items: center;
margin-bottom: 20px;
gap: 10px;
- cursor: pointer;
.text {
font-family: "Open Sans", sans-serif;
@@ -22,6 +21,7 @@
padding: 7px 10px 7px 5px;
border: 1px solid #D9D9D9;
border-radius: 20px;
+ cursor: pointer;
&:hover {
background-color: #f1f1f1;
diff --git a/apps/schools/domains/common/components/containers/BaseLayoutComponents/BaseLayout/index.tsx b/apps/schools/domains/common/components/containers/BaseLayoutComponents/BaseLayout/index.tsx
index 11b4bfe1..b8a86ac8 100644
--- a/apps/schools/domains/common/components/containers/BaseLayoutComponents/BaseLayout/index.tsx
+++ b/apps/schools/domains/common/components/containers/BaseLayoutComponents/BaseLayout/index.tsx
@@ -12,7 +12,7 @@ import { IBaseLayoutProps } from './interfaces'
import { OrganizationSelect } from '../OrganizationSelect'
import { useLayoutContext } from '@domains/user/providers/baseLayoutProvider'
import { COLLAPSED_DIVIDER_WIDTH, COLLAPSED_LAYOUT_WIDTH, DIVIDER_WIDTH, LAYOUT_WIDTH } from './styles/styles'
-import { QueryStatuses } from '@domains/common/constants/Enums'
+import { StatusesEnum } from '@domains/common/constants/Enums'
import Image from 'next/image'
import ExclamationCircleOutlined from '@public/icons/ExclamationCircleOutlined.svg'
import { Button } from '@domains/common/components/button'
@@ -36,7 +36,7 @@ export const BaseLayout: React.FC = (props) => {
{invitations?.results.map((invite) => {
- if (invite.status === QueryStatuses.SENT || invite.status === QueryStatuses.IN_PROGRESS) {
+ if (invite.status === StatusesEnum.SENT || invite.status === StatusesEnum.IN_PROGRESS) {
return (
@@ -50,7 +50,7 @@ export const BaseLayout: React.FC
= (props) => {
handleChangeStatusInvitation(
mutation,
invite.id,
- QueryStatuses.ACCEPTED,
+ StatusesEnum.ACCEPTED,
).then(() => {
refetch()
emit(EventKey.RefetchOrganizationsQuery)
@@ -65,7 +65,7 @@ export const BaseLayout: React.FC = (props) => {
handleChangeStatusInvitation(
mutation,
invite.id,
- QueryStatuses.DECLINED,
+ StatusesEnum.DECLINED,
).then(refetch)
}
>
diff --git a/apps/schools/domains/common/components/containers/BaseLayoutComponents/Menu/index.tsx b/apps/schools/domains/common/components/containers/BaseLayoutComponents/Menu/index.tsx
index f94fa5cb..cedc8ad1 100644
--- a/apps/schools/domains/common/components/containers/BaseLayoutComponents/Menu/index.tsx
+++ b/apps/schools/domains/common/components/containers/BaseLayoutComponents/Menu/index.tsx
@@ -1,5 +1,5 @@
import { Menu } from 'antd'
-import { FileDoneOutlined, ReadOutlined, TeamOutlined, UserAddOutlined } from '@ant-design/icons'
+import { FileDoneOutlined, MailOutlined, ReadOutlined, TeamOutlined, UserAddOutlined } from '@ant-design/icons'
import React, { useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import styles from './styles/styles.module.scss'
@@ -15,6 +15,7 @@ const menuList: MenuItemObj[] = [
new MenuItemObj('employee', 'Сотрудники', , [
isOrganizationSelected,
]),
+ new MenuItemObj('ticket', 'Обращения', , [isOrganizationSelected]),
]
const MenuCustom = () => {
diff --git a/apps/schools/domains/common/constants/Enums.ts b/apps/schools/domains/common/constants/Enums.ts
index da755dd3..41623b3f 100644
--- a/apps/schools/domains/common/constants/Enums.ts
+++ b/apps/schools/domains/common/constants/Enums.ts
@@ -1,4 +1,4 @@
-export enum QueryStatuses {
+export enum StatusesEnum {
SENT = 'SENT',
ACCEPTED = 'ACCEPTED',
IN_PROGRESS = 'IN_PROGRESS',
diff --git a/apps/schools/domains/common/constants/routerEnums.ts b/apps/schools/domains/common/constants/routerEnums.ts
index 8f14da78..07b2e0a2 100644
--- a/apps/schools/domains/common/constants/routerEnums.ts
+++ b/apps/schools/domains/common/constants/routerEnums.ts
@@ -9,6 +9,7 @@ export const enum AppRoutes {
EMPLOYEE_CREATE = 'employee_create',
MAIN = 'main',
MOBILE_RECAPTCHA = 'mobile_recaptcha',
+ TICKETS_LIST = 'tickets_list',
NOT_FOUND = 'not_found',
QUERY_LIST = 'query_list',
STUDENT_LIST = 'student_list',
@@ -28,6 +29,7 @@ export const RoutePath: Record = {
[AppRoutes.EMPLOYEE_CREATE]: '/employee/create',
[AppRoutes.MAIN]: '/',
[AppRoutes.MOBILE_RECAPTCHA]: '/mobile-recaptcha',
+ [AppRoutes.TICKETS_LIST]: '/ticket',
[AppRoutes.NOT_FOUND]: '/404',
[AppRoutes.QUERY_LIST]: '/query',
[AppRoutes.STUDENT_LIST]: '/student',
diff --git a/apps/schools/domains/common/handlers/changeStatusInvitation.ts b/apps/schools/domains/common/handlers/changeStatusInvitation.ts
index 2c5d9b56..8754fb8b 100644
--- a/apps/schools/domains/common/handlers/changeStatusInvitation.ts
+++ b/apps/schools/domains/common/handlers/changeStatusInvitation.ts
@@ -2,7 +2,7 @@ import { withLoadingMessage } from '@domains/common/utils/loading'
import { LoadingMsg } from '@domains/user/components/auth/constants/message'
import { message } from 'antd'
import { QueriesTypes } from '@domains/common/redux/interfaces'
-import { QueryStatuses } from '@domains/common/constants/Enums'
+import { StatusesEnum } from '@domains/common/constants/Enums'
export async function handleChangeStatusInvitation(mutation: any, id: string | undefined, status: QueriesTypes) {
if (id === undefined) {
@@ -16,7 +16,7 @@ export async function handleChangeStatusInvitation(mutation: any, id: string | u
})
if ('data' in response)
- status === QueryStatuses.ACCEPTED
+ status === StatusesEnum.ACCEPTED
? message.success(`Вы успешно приняли заявку`)
: message.success(`Вы успешно отклонили заявку`)
}
diff --git a/apps/schools/domains/common/redux/serializers.ts b/apps/schools/domains/common/redux/serializers.ts
index 14ac85d7..6165fb90 100644
--- a/apps/schools/domains/common/redux/serializers.ts
+++ b/apps/schools/domains/common/redux/serializers.ts
@@ -139,6 +139,16 @@ export interface GetAnalytics {
CANCELED: number
}
+export interface GetTicketAnalytics {
+ 'ticket-analytics': {
+ IN_PROGRESS: number
+ SENT: number
+ ACCEPTED: number
+ DECLINED: number
+ CANCELED: number
+ }
+}
+
export interface GetQueryStatus {
id?: string
status: QueriesTypes
@@ -189,6 +199,12 @@ export interface GetFamily {
name: string
}
+export interface GetAnalyticsData {
+ date_from?: string
+ date_to?: string
+ organization_id: string
+}
+
export interface GetFamilyRecipient {
id?: string
name: string
diff --git a/apps/schools/domains/organization/redux/interfaces.ts b/apps/schools/domains/organization/redux/interfaces.ts
index 84d1d24d..eb95fd90 100644
--- a/apps/schools/domains/organization/redux/interfaces.ts
+++ b/apps/schools/domains/organization/redux/interfaces.ts
@@ -106,12 +106,6 @@ export interface AllStudentJoinCircleQueriesData extends BasePaginationData {
student__student_profile__phone?: string
}
-export interface GetOrganizationAnalyticsData {
- date_from?: string
- date_to?: string
- organization_id: string
-}
-
export interface GetOrganizationCircleList {
id: string
name: string
diff --git a/apps/schools/domains/organization/redux/organizationApi.ts b/apps/schools/domains/organization/redux/organizationApi.ts
index c31bbedb..12dabb15 100644
--- a/apps/schools/domains/organization/redux/organizationApi.ts
+++ b/apps/schools/domains/organization/redux/organizationApi.ts
@@ -17,12 +17,12 @@ import {
GetOrganizationCircleListData,
GetCurrentCircleData,
AllStudentJoinCircleQueriesData,
- GetOrganizationAnalyticsData,
} from './interfaces'
import { GetEmployee } from '../../employee/redux/interfaces'
import {
CreateOrganizationInviteEmployee,
GetAnalytics,
+ GetAnalyticsData,
GetCircleInviteStudent,
GetOrganizationInviteEmployee,
GetOrganizationSender,
@@ -32,6 +32,7 @@ import {
GetTeacher,
UpdateOrganizationInviteEmployee,
} from '@domains/common/redux/serializers'
+import { GetTicket, GetTicketsData } from '@domains/ticket/redux/serializers'
const organizationApi = commonApi.injectEndpoints({
endpoints: (build) => ({
@@ -166,7 +167,7 @@ const organizationApi = commonApi.injectEndpoints({
}),
providesTags: (result) => providesList(result?.results, 'StudentJoinCircleQuery'),
}),
- getOrganizationAnalytics: build.query<{ analytics: GetAnalytics }, GetOrganizationAnalyticsData>({
+ getOrganizationAnalytics: build.query<{ analytics: GetAnalytics }, GetAnalyticsData>({
query: (params) => ({
url: `/organization-management/organizations/${params.organization_id}/analytics`,
method: 'GET',
@@ -174,6 +175,20 @@ const organizationApi = commonApi.injectEndpoints({
}),
providesTags: ['StudentJoinCircleQuery'],
}),
+ getTicketsAnalytics: build.query<{ 'ticket-analytics': GetAnalytics }, GetAnalyticsData>({
+ query: (params) => ({
+ url: `/organization-management/organizations/${params.organization_id}/ticket-analytics`,
+ method: 'GET',
+ params: params,
+ }),
+ }),
+ getAllTickets: build.query, GetTicketsData>({
+ query: (params) => ({
+ url: `/organization-management/organizations/${params.organization_id}/family-tickets`,
+ method: 'GET',
+ params: params,
+ }),
+ }),
}),
})
@@ -197,4 +212,6 @@ export const {
useGetAllCirclesQuery,
useGetAllJoinCircleQueriesQuery,
useGetOrganizationAnalyticsQuery,
+ useGetTicketsAnalyticsQuery,
+ useGetAllTicketsQuery,
} = organizationApi
diff --git a/apps/schools/domains/query/components/queryList/constants.ts b/apps/schools/domains/query/components/queryList/constants.ts
index 3575adf2..234e4358 100644
--- a/apps/schools/domains/query/components/queryList/constants.ts
+++ b/apps/schools/domains/query/components/queryList/constants.ts
@@ -1,4 +1,4 @@
-import { QueryStatuses } from '@domains/common/constants/Enums'
+import { StatusesEnum } from '@domains/common/constants/Enums'
import {
ACCEPTED_FILTER_COLOR,
CANCELED_FILTER_COLOR,
@@ -23,9 +23,9 @@ interface TagType {
}
export const StatusDictionary: { [key: string]: TagType } = {
- [QueryStatuses.SENT]: { text: 'Отправлено', color: SENT_FILTER_COLOR, antdColor: 'gold' },
- [QueryStatuses.IN_PROGRESS]: { text: 'На рассмотрении', color: IN_PROGRESS_FILTER_COLOR, antdColor: 'blue' },
- [QueryStatuses.ACCEPTED]: { text: 'Принято', color: ACCEPTED_FILTER_COLOR, antdColor: 'green' },
- [QueryStatuses.CANCELED]: { text: 'Отменено', color: CANCELED_FILTER_COLOR, antdColor: 'volcano' },
- [QueryStatuses.DECLINED]: { text: 'Отклонено', color: DECLINED_FILTER_COLOR, antdColor: 'red' },
+ [StatusesEnum.SENT]: { text: 'Отправлено', color: SENT_FILTER_COLOR, antdColor: 'gold' },
+ [StatusesEnum.IN_PROGRESS]: { text: 'На рассмотрении', color: IN_PROGRESS_FILTER_COLOR, antdColor: 'blue' },
+ [StatusesEnum.ACCEPTED]: { text: 'Принято', color: ACCEPTED_FILTER_COLOR, antdColor: 'green' },
+ [StatusesEnum.CANCELED]: { text: 'Отменено', color: CANCELED_FILTER_COLOR, antdColor: 'volcano' },
+ [StatusesEnum.DECLINED]: { text: 'Отклонено', color: DECLINED_FILTER_COLOR, antdColor: 'red' },
}
diff --git a/apps/schools/domains/student/components/studentList/index.tsx b/apps/schools/domains/student/components/studentList/index.tsx
index e491ed46..f72267d6 100644
--- a/apps/schools/domains/student/components/studentList/index.tsx
+++ b/apps/schools/domains/student/components/studentList/index.tsx
@@ -9,7 +9,7 @@ import { createSearchTextForRequest } from '@domains/common/utils/searchText'
import { RowType, TableType } from './interfaces'
import { searchInvitesColumns, searchStudentsColumns } from './constants'
import { useGetAllStudentInvitationsQuery, useGetAllStudentsQuery } from '@domains/organization/redux/organizationApi'
-import { QueryStatuses } from '@domains/common/constants/Enums'
+import { StatusesEnum } from '@domains/common/constants/Enums'
import EmptyWrapper from '@domains/common/components/containers/EmptyWrapper'
import { AppRoutes, RoutePath } from '@domains/common/constants/routerEnums'
@@ -19,7 +19,7 @@ export function StudentList() {
const { data: invites, isLoading: isLoadingInvites } = useGetAllStudentInvitationsQuery({
circle__organization__id: organizationId,
- status: QueryStatuses.SENT,
+ status: StatusesEnum.SENT,
or_search: createSearchTextForRequest(searchRequestText, searchInvitesColumns),
})
diff --git a/apps/schools/domains/ticket/components/ticketList/constants.ts b/apps/schools/domains/ticket/components/ticketList/constants.ts
new file mode 100644
index 00000000..f775aa7b
--- /dev/null
+++ b/apps/schools/domains/ticket/components/ticketList/constants.ts
@@ -0,0 +1,18 @@
+import { StatusesEnum } from '@domains/common/constants/Enums'
+import type { LiteralUnion } from 'antd/lib/_util/type'
+import type { PresetColorType, PresetStatusColorType } from 'antd/lib/_util/colors'
+import { CANCELED_FILTER_COLOR, IN_PROGRESS_FILTER_COLOR, SENT_FILTER_COLOR } from './styles/styles'
+
+export const searchTicketsColumns = ['family__name', 'ticket_comment__value']
+
+interface TagType {
+ text: string
+ color: string
+ antdColor: LiteralUnion
+}
+
+export const StatusDictionary: { [key: string]: TagType } = {
+ [StatusesEnum.SENT]: { text: 'Новое', color: SENT_FILTER_COLOR, antdColor: 'red' },
+ [StatusesEnum.IN_PROGRESS]: { text: 'Открыто', color: IN_PROGRESS_FILTER_COLOR, antdColor: 'blue' },
+ [StatusesEnum.CANCELED]: { text: 'Закрыто', color: CANCELED_FILTER_COLOR, antdColor: 'green' },
+}
diff --git a/apps/schools/domains/ticket/components/ticketList/index.tsx b/apps/schools/domains/ticket/components/ticketList/index.tsx
new file mode 100644
index 00000000..5a0505dc
--- /dev/null
+++ b/apps/schools/domains/ticket/components/ticketList/index.tsx
@@ -0,0 +1,196 @@
+import React, { useEffect, useState } from 'react'
+import { Tag, Typography } from 'antd'
+import styles from './styles/styles.module.scss'
+import { useOrganization } from '@domains/organization/providers/organizationProvider'
+import { Table } from '@domains/common/components/table'
+import { createSearchTextForRequest } from '@domains/common/utils/searchText'
+import { RowType, TableType } from './interfaces'
+import { searchTicketsColumns, StatusDictionary } from './constants'
+import {
+ useGetAllJoinCircleQueriesQuery,
+ useGetAllTicketsQuery,
+ useGetOrganizationAnalyticsQuery,
+ 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 { sumObjectValues } from '@domains/common/utils/sumObjectValues'
+import { CloseCircleOutlined, SearchOutlined } from '@ant-design/icons'
+import { Input } from '@domains/common/components/input'
+import { BubbleFilter } from '@domains/common/components/bubbleFilter'
+import { BubbleFilterListItem } from '@domains/common/components/bubbleFilter/interface'
+import { useQueryState } from 'next-usequerystate'
+import { parseAsArrayOf, parseAsString } from 'next-usequerystate'
+import { AppRoutes, RoutePath } from '@domains/common/constants/routerEnums'
+
+export function TicketList() {
+ const [inputText, setInputText] = useState('')
+ const [searchRequestText, setSearchRequestText] = useState('')
+ const [isTableLoading, setIsTableLoading] = useState(false)
+ const { organizationId } = useOrganization()
+
+ const [statuses, setStatuses] = useQueryState(
+ 'statuses',
+ parseAsArrayOf(parseAsString).withOptions({
+ history: 'push',
+ }),
+ )
+
+ const { data: analytics, isLoading: isAnalyticsLoading } = useGetTicketsAnalyticsQuery({
+ organization_id: organizationId,
+ })
+
+ const bubbleFilterItems: any = {}
+
+ for (const key in StatusDictionary) {
+ const obj = StatusDictionary[key]
+
+ bubbleFilterItems[key] = {
+ key: key,
+ text: obj.text,
+ color: obj.color,
+ count: analytics ? (analytics['ticket-analytics'] as unknown as { [index: string]: number })[key] : 0,
+ isSelected: statuses?.includes(key) ?? false,
+ onClick: () => {
+ setStatuses((x) => [...(x ?? []), key])
+ },
+ onExit: () => {
+ setStatuses((x) => {
+ const res = [...(x ?? []).filter((y) => y != key)]
+ return res.length === 0 ? null : res
+ })
+ },
+ } as BubbleFilterListItem
+ }
+
+ const { data: tickets, isLoading: isTicketsLoading } = useGetAllTicketsQuery({
+ organization_id: organizationId,
+ or_search: createSearchTextForRequest(searchRequestText, searchTicketsColumns),
+ })
+
+ console.log(tickets)
+
+ const reformattedData = mapReturnedData(tickets, (query) => {
+ const transformedQuery = structuredClone(query) as unknown as TableType
+ transformedQuery.last_message = query.last_comment.value
+ transformedQuery.sender = query.last_comment?.sender?.name || query.sender?.name
+ return transformedQuery
+ })
+
+ useEffect(() => {
+ if (!isTicketsLoading && tickets) {
+ setIsTableLoading(false)
+ }
+ }, [tickets])
+
+ return (
+
+
+ Обращения
+
+ {
+ setIsTableLoading(true)
+ setInputText(text.target.value)
+ setTimeout(() => {
+ setSearchRequestText(text.target.value)
+ }, 1000)
+ }}
+ customType={'inputSearch'}
+ placeholder={'Поиск'}
+ value={inputText}
+ children={
+ <>
+
+ {inputText && (
+ {
+ setInputText('')
+ setSearchRequestText('')
+ }}
+ />
+ )}
+ >
+ }
+ />
+
+
+ loading={isTableLoading}
+ customType={'tableWithoutSearch'}
+ columnsTitlesAndKeys={[
+ ['Создано', 'created_at'],
+ ['Статус', 'status'],
+ ['Содержание', 'last_message'],
+ ['Отправитель', 'sender'],
+ ]}
+ customWidths={[10, 10, 40, 30]}
+ data={reformattedData}
+ isLoading={isTicketsLoading}
+ mainRoute={RoutePath[AppRoutes.TICKETS_LIST]}
+ searchFields={['created_at', 'last_message', 'sender']}
+ customFields={{
+ created_at: ({ text, searchText }) => {
+ const [date, time] = new Intl.DateTimeFormat('pt-BR', {
+ dateStyle: 'short',
+ timeStyle: 'short',
+ })
+ .format(new Date(text))
+ .replaceAll('/', '.')
+ .split(',')
+
+ return (
+
+ )
+ },
+ status: ({ text }) => {
+ return (
+
+ {StatusDictionary[text].text}
+
+ )
+ },
+ }}
+ customFilterFields={{
+ status: {
+ filters: Object.entries(StatusDictionary).map(([key, value]) => ({
+ value: key,
+ text: value.text,
+ })),
+ filteredValue: statuses ?? [],
+ onFilter: (value, record) => {
+ const obj = (record as any)['status']
+ if (!isReactElement(obj)) return obj === value
+
+ return obj.key === value
+ },
+ },
+ }}
+ sortFields={['created_at']}
+ searchRequestText={searchRequestText}
+ setSearchRequestText={setSearchRequestText}
+ onChange={(pagination, filters, sorter) => {
+ const localStatuses = [...(filters['status'] ?? [])] as string[]
+ setStatuses(localStatuses.length === 0 ? null : localStatuses)
+ }}
+ />
+
+ )
+}
diff --git a/apps/schools/domains/ticket/components/ticketList/interfaces.ts b/apps/schools/domains/ticket/components/ticketList/interfaces.ts
new file mode 100644
index 00000000..35f15789
--- /dev/null
+++ b/apps/schools/domains/ticket/components/ticketList/interfaces.ts
@@ -0,0 +1,15 @@
+export interface RowType {
+ id?: string
+ created_up?: string
+ status?: string
+ last_message?: string
+ sender?: string
+}
+
+export interface TableType {
+ id?: string
+ created_up?: string
+ status?: string
+ last_message?: string
+ sender?: string
+}
diff --git a/apps/schools/domains/ticket/components/ticketList/styles/styles.module.scss b/apps/schools/domains/ticket/components/ticketList/styles/styles.module.scss
new file mode 100644
index 00000000..e1852186
--- /dev/null
+++ b/apps/schools/domains/ticket/components/ticketList/styles/styles.module.scss
@@ -0,0 +1,60 @@
+@import '../../../../common/components/styles/abstracts/common';
+
+.tableContainer {
+ @extend %table;
+}
+
+.inputSearch {
+ margin-bottom: 0;
+}
+
+.allQueriesText {
+ font-family: "Open Sans", sans-serif;
+ font-size: 16px;
+ font-style: normal;
+ font-weight: 600;
+ line-height: 140%;
+}
+
+.header {
+ margin-bottom: 31px;
+ align-items: center;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+
+ .button {
+ min-width: 200px;
+ text-align: center;
+ font-family: 'Roboto', sans-serif;
+ font-size: 16px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: 24px;
+ width: 14%;
+ }
+}
+
+.search {
+ font-size: 150%;
+ position: absolute;
+ right: 30px;
+}
+
+.cross {
+ font-size: 100%;
+ position: absolute;
+ right: 60px;
+
+ :hover {
+ opacity: .6;
+ }
+
+ :active, :focus {
+ opacity: .8;
+ }
+}
+
+.tags {
+ border-radius: 8px;
+}
\ No newline at end of file
diff --git a/apps/schools/domains/ticket/components/ticketList/styles/styles.ts b/apps/schools/domains/ticket/components/ticketList/styles/styles.ts
new file mode 100644
index 00000000..0a7bffeb
--- /dev/null
+++ b/apps/schools/domains/ticket/components/ticketList/styles/styles.ts
@@ -0,0 +1,3 @@
+export const IN_PROGRESS_FILTER_COLOR = '#2F54EB'
+export const SENT_FILTER_COLOR = '#EB3468'
+export const CANCELED_FILTER_COLOR = '#52C41A'
diff --git a/apps/schools/domains/ticket/redux/serializers.ts b/apps/schools/domains/ticket/redux/serializers.ts
new file mode 100644
index 00000000..7e00603e
--- /dev/null
+++ b/apps/schools/domains/ticket/redux/serializers.ts
@@ -0,0 +1,42 @@
+import { GetFamily, GetOrganization } from '@domains/common/redux/serializers'
+import { BasePaginationData, QueriesTypes } from '@domains/common/redux/interfaces'
+
+export interface GetTicket {
+ id?: string
+ last_comment: GetTicketComment
+ recipient: GetOrganization
+ created_at?: string
+ unread_sender_comments_count?: string
+ unread_recipient_comments_count?: string
+ sender?: GetFamily
+ status?: QueriesTypes
+}
+
+export interface GetTicketComment {
+ id?: string
+ is_sender: boolean
+ is_seen?: boolean
+ value: string
+ created_at?: string
+ sender: GetTicketCommentSender
+}
+
+export interface GetTicketCommentSender {
+ id: string
+ name: string
+}
+
+export interface GetTicketsData extends BasePaginationData {
+ id?: string
+ created_at?: string
+ recipient_id?: string
+ recipient_ct?: string
+ status?: string
+ or_search?: string
+ family__id?: string
+ family__name?: string
+ family__parent_phone?: string
+ ticket_comment__id?: string
+ ticket_comment__value?: string
+ organization_id?: string
+}
diff --git a/apps/schools/pages/ticket/index.tsx b/apps/schools/pages/ticket/index.tsx
new file mode 100644
index 00000000..c09a41d0
--- /dev/null
+++ b/apps/schools/pages/ticket/index.tsx
@@ -0,0 +1,23 @@
+import React from 'react'
+import Head from 'next/head'
+import { PageContent } from '@domains/common/components/containers/PageContent'
+
+import { OrganizationRequired } from '@domains/common/components/containers/OrganizationRequired'
+import { TicketList } from '@domains/ticket/components/ticketList'
+
+const TicketsPageContent = () => {
+ return (
+ <>
+
+ Обращения
+
+
+
+
+
+
+ >
+ )
+}
+
+export default TicketsPageContent