Загрузка данных
import { useGetTopQuery } from '@/shared/api/notifications/client';
import {
Box,
DateDropdown,
IconButton,
MenuItem,
Row,
SegmentButton,
SegmentItem,
Select,
Typography,
} from '@/shared/components';
import { CollapseTransition } from '@sber-friend/flamingo-core';
import { ChevronsDownIcon, ChevronsUpIcon } from '@sber-friend/flamingo-icons';
import { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { mapTopDonutData } from '../model/mapTopDonutData';
import { mapTopTableRows } from '../model/mapTopTableRows';
import type { EventAnalysisEntityType } from '../model/types';
import {
EntityFiltersState,
useEntityFilters,
} from '../model/useEntityFilters';
import { EventDonut } from './EventDonut';
import { EventTable } from './EventTable';
type EntityBlockProps = {
type: EventAnalysisEntityType;
};
const PAGINATION_LIMITS: number[] = [5, 10, 25, 50, 100];
export const EntityBlock = ({ type }: EntityBlockProps) => {
const filtersModel = useEntityFilters();
const { t } = useTranslation('eventAnalysis');
const [version, setVersion] = useState<'table' | 'distribution'>('table');
const { data, isLoading, isFetching } = useGetTopQuery({
...filtersModel.filters,
type,
});
// @ts-ignore
const handleChangeBtn = (_, newValue) => {
setVersion(newValue);
};
const rows = useMemo(
() => mapTopTableRows(data?.controllers ?? []),
[data?.controllers],
);
const donutData = useMemo(
() =>
mapTopDonutData(data?.controllers ?? [], {
info: t('info'),
normal: t('normal'),
important: t('important'),
critical: t('critical'),
}),
[data?.controllers, t],
);
const donutTotal = useMemo(
() => donutData.reduce((sum, item) => sum + item.value, 0),
[donutData],
);
const [open, setOpen] = useState(false);
const handleClick = () => {
setOpen(!open);
};
const lastYear = new Date(
new Date().getFullYear() - 1,
new Date().getMonth(),
new Date().getDate(),
);
return (
<Box
p={2}
pl={3}
pr={3}
borderRadius={16}
display='flex'
flexDirection='column'
gridGap='32px'
border={'solid 1px'}
borderColor={'outline.outline'}
bgcolor={'background.primary3'}
>
<Box
display='flex'
flexDirection='column'
gridGap={open ? '12px' : '0px'}
>
<Row justifyContent='space-between' onClick={(e) => handleClick()}>
<Row gridGap={36}>
<Typography variant='h4'>{t(`${type}`)}</Typography>
</Row>
<Row>
{open && (
<Box onClick={(e) => e.stopPropagation()}>
<Row>
<SegmentButton
size='small'
value={version}
onChange={handleChangeBtn}
>
<SegmentItem label={t('version.table')} value={'table'} />
<SegmentItem
label={`${t('version.distribution')}`}
value={'distribution'}
/>
</SegmentButton>
<Row>
<DateDropdown
dateFrom={filtersModel.filters.after}
dateTo={filtersModel.filters.before}
onDateFromChange={filtersModel.handleAfterChange}
onDateToChange={filtersModel.handleBeforeChange}
disableFuture
minDate={lastYear}
/>
<Select
value={filtersModel.filters.limit}
size='small'
style={{ zIndex: 10000 }}
onChange={(e) =>
filtersModel.handleLimitChange(
e.target.value as EntityFiltersState['limit'],
)
}
>
{PAGINATION_LIMITS.map((limit) => (
<MenuItem size='small' key={limit} value={limit}>
<Typography variant='caption2'>по {limit}</Typography>
</MenuItem>
))}
</Select>
</Row>
</Row>
</Box>
)}
<IconButton onClick={handleClick}>
{open ? <ChevronsUpIcon /> : <ChevronsDownIcon />}
</IconButton>
</Row>
</Row>
<CollapseTransition in={open} timeout={500}>
<Box display='flex' flexDirection='column' gridGap='16px'>
<Row gridGap={72} alignItems='start'>
<Box minWidth={150}>
<EventDonut
data={donutData}
total={donutTotal}
loading={isLoading || isFetching}
donutTitle={t('total')}
/>
</Box>
<EventTable rows={rows} type={type} loading={isLoading} />
</Row>
</Box>
</CollapseTransition>
</Box>
</Box>
);
};
import { notificationsApi as api } from "./api";
export const addTagTypes = [
"Analyze",
"Notifications",
"Readers",
"Archive",
"Actions",
] as const;
const injectedRtkApi = api
.enhanceEndpoints({
addTagTypes,
})
.injectEndpoints({
endpoints: (build) => ({
getTop: build.query<GetTopApiResponse, GetTopApiArg>({
query: (queryArg) => ({
url: `/api/v1/notification/analyze/top`,
params: {
after: queryArg.after,
before: queryArg.before,
type: queryArg["type"],
sortColumn: queryArg.sortColumn,
sortType: queryArg.sortType,
limit: queryArg.limit,
offset: queryArg.offset,
},
}),
providesTags: ["Analyze"],
}),
getPriorityDiagram: build.query<
GetPriorityDiagramApiResponse,
GetPriorityDiagramApiArg
>({
query: (queryArg) => ({
url: `/api/v1/notification/analyze/diagram/priority`,
params: { type: queryArg["type"] },
}),
providesTags: ["Analyze"],
}),
getActionDiagram: build.query<
GetActionDiagramApiResponse,
GetActionDiagramApiArg
>({
query: (queryArg) => ({
url: `/api/v1/notification/analyze/diagram/action`,
params: { type: queryArg["type"] },
}),
providesTags: ["Analyze"],
}),
getNotifications: build.query<
GetNotificationsApiResponse,
GetNotificationsApiArg
>({
query: (queryArg) => ({
url: `/api/v1/notification`,
params: {
smartroomId: queryArg.smartroomId,
priority: queryArg.priority,
actionId: queryArg.actionId,
entityType: queryArg.entityType,
entityName: queryArg.entityName,
dateFrom: queryArg.dateFrom,
dateTo: queryArg.dateTo,
search: queryArg.search,
hasArchived: queryArg.hasArchived,
hasRead: queryArg.hasRead,
limit: queryArg.limit,
offset: queryArg.offset,
},
}),
providesTags: ["Notifications"],
}),
getNotificationsCursor: build.query<
GetNotificationsCursorApiResponse,
GetNotificationsCursorApiArg
>({
query: (queryArg) => ({
url: `/api/v1/notification/cursor`,
params: {
priority: queryArg.priority,
actionId: queryArg.actionId,
entityType: queryArg.entityType,
dateFrom: queryArg.dateFrom,
dateTo: queryArg.dateTo,
search: queryArg.search,
hasArchived: queryArg.hasArchived,
hasRead: queryArg.hasRead,
limit: queryArg.limit,
cursorId: queryArg.cursorId,
cursorCreatedAt: queryArg.cursorCreatedAt,
direction: queryArg.direction,
},
}),
providesTags: ["Notifications"],
}),
getNotificationsCursorCount: build.query<
GetNotificationsCursorCountApiResponse,
GetNotificationsCursorCountApiArg
>({
query: (queryArg) => ({
url: `/api/v1/notification/cursorCount`,
params: {
priority: queryArg.priority,
actionId: queryArg.actionId,
entityType: queryArg.entityType,
dateFrom: queryArg.dateFrom,
dateTo: queryArg.dateTo,
search: queryArg.search,
hasArchived: queryArg.hasArchived,
hasRead: queryArg.hasRead,
limit: queryArg.limit,
cursorId: queryArg.cursorId,
cursorCreatedAt: queryArg.cursorCreatedAt,
direction: queryArg.direction,
},
}),
providesTags: ["Notifications"],
}),
getNotificationById: build.query<
GetNotificationByIdApiResponse,
GetNotificationByIdApiArg
>({
query: (queryArg) => ({
url: `/api/v1/notification/${queryArg.notificationId}`,
}),
providesTags: ["Notifications"],
}),
readNotification: build.mutation<
ReadNotificationApiResponse,
ReadNotificationApiArg
>({
query: (queryArg) => ({
url: `/api/v1/notification/read/${queryArg.notificationId}`,
method: "POST",
}),
//custom
invalidatesTags: ["Notifications"],
}),
readAllNotifications: build.mutation<
ReadAllNotificationsApiResponse,
ReadAllNotificationsApiArg
>({
query: () => ({ url: `/api/v1/notification/read/all`, method: "POST" }),
//custom
invalidatesTags: ["Notifications"],
}),
archiveNotification: build.mutation<
ArchiveNotificationApiResponse,
ArchiveNotificationApiArg
>({
query: (queryArg) => ({
url: `/api/v1/notification/archive/${queryArg.notificationId}`,
method: "POST",
}),
//custom
invalidatesTags: ["Notifications"],
}),
archiveAllNotifications: build.mutation<
ArchiveAllNotificationsApiResponse,
ArchiveAllNotificationsApiArg
>({
query: () => ({
url: `/api/v1/notification/archive/all`,
method: "POST",
}),
//custom
invalidatesTags: ["Notifications"],
}),
createAction: build.mutation<CreateActionApiResponse, CreateActionApiArg>(
{
query: (queryArg) => ({
url: `/api/v1/notification/action`,
method: "POST",
body: queryArg.actionCreateRequest,
}),
invalidatesTags: ["Actions"],
}
),
getAllActions: build.query<GetAllActionsApiResponse, GetAllActionsApiArg>(
{
query: () => ({ url: `/api/v1/notification/action` }),
providesTags: ["Actions"],
}
),
getAllEnum: build.query<GetAllEnumApiResponse, GetAllEnumApiArg>({
query: () => ({ url: `/api/v1/notification/action/enum` }),
providesTags: ["Actions"],
}),
updateAction: build.mutation<UpdateActionApiResponse, UpdateActionApiArg>(
{
query: (queryArg) => ({
url: `/api/v1/notification/action/${queryArg.actionId}`,
method: "PUT",
body: queryArg.actionUpdateRequest,
}),
invalidatesTags: ["Actions"],
}
),
deleteAction: build.mutation<DeleteActionApiResponse, DeleteActionApiArg>(
{
query: (queryArg) => ({
url: `/api/v1/notification/action/${queryArg.actionId}`,
method: "DELETE",
}),
invalidatesTags: ["Actions"],
}
),
}),
overrideExisting: false,
});
export { injectedRtkApi as client };
export type GetTopApiResponse = /** status 200 Список событий */
| ({
type: "controller";
} & TopControllerResponse)
| ({
type: "location";
} & TopLocationResponse)
| ({
type: "device";
} & TopDeviceResponse)
| ({
type: "network-device";
} & TopNetworkDeviceResponse)
| ({
type: "padlet";
} & TopPadletResponse);
export type GetTopApiArg = {
/** Time format - RFC3339 */
after?: string;
/** Time format - RFC3339 */
before?: string;
/** entity type */
type?: "controller" | "device" | "network-device" | "padlet" | "location";
/** Column name for sorting */
sortColumn?: TopSortColumn;
/** Column name for sorting */
sortType?: SortType;
/** Количество результатов (по умолчанию 5) */
limit?: 5 | 10 | 25 | 50 | 100;
/** Смещение для пагинации */
offset?: number;
};
export type GetPriorityDiagramApiResponse =
/** status 200 Счетчики по приоритетам */ PriorityDiagram;
export type GetPriorityDiagramApiArg = {
/** entity type */
type?: "controller" | "device" | "network-device" | "padlet" | "location";
};
export type GetActionDiagramApiResponse =
/** status 200 Счетчики по приоритетам */ ActionDiagram;
export type GetActionDiagramApiArg = {
/** entity type */
type?: "controller" | "device" | "network-device" | "padlet" | "location";
};
export type GetNotificationsApiResponse =
/** status 200 OK */ NotificationsGetResponse;
export type GetNotificationsApiArg = {
/** smartroom entity UUID */
smartroomId?: string;
/** priority of notification action */
priority?: Priority[];
/** id of notification action, values from 1 to 18 */
actionId?: number[];
/** entity type */
entityType?: EntityType[];
/** entity name */
entityName?: string;
/** date from notification start */
dateFrom?: string;
/** date to notification end */
dateTo?: string;
/** search by id and message */
search?: string;
/** has notification archived */
hasArchived?: boolean;
/** has the notification been read */
hasRead?: boolean;
/** Rows number on the page (pagination parameter) */
limit?: number;
/** The number of records you wish to skip before selecting records (pagination parameter) */
offset?: number;
};
export type GetNotificationsCursorApiResponse =
/** status 200 OK */ NotificationsCursorResponse;
export type GetNotificationsCursorApiArg = {
/** priority of notification action */
priority?: Priority[];
/** id of notification action, values from 1 to 18 */
actionId?: number[];
/** entity type */
entityType?: EntityType[];
/** date from notification start */
dateFrom?: string;
/** date to notification end */
dateTo?: string;
/** search by id and message */
search?: string;
/** has notification archived */
hasArchived?: boolean;
/** has the notification been read */
hasRead?: boolean;
/** Rows number on the page (pagination parameter). Default is 25, max is 100 */
limit?: number;
/** Cursor ID (UUID) for pagination. Must be provided together with cursorCreatedAt */
cursorId?: string;
/** Cursor creation timestamp (RFC3339Nano format) for pagination. Must be provided together with cursorId */
cursorCreatedAt?: string;
/** Pagination direction. Use "next" to get next page (default) or "prev" to get previous page */
direction?: "next" | "prev";
};
export type GetNotificationsCursorCountApiResponse =
/** status 200 Count of records */ number;
export type GetNotificationsCursorCountApiArg = {
/** priority of notification action */
priority?: Priority[];
/** id of notification action, values from 1 to 18 */
actionId?: number[];
/** entity type */
entityType?: EntityType[];
/** date from notification start */
dateFrom?: string;
/** date to notification end */
dateTo?: string;
/** search by id and message */
search?: string;
/** has notification archived */
hasArchived?: boolean;
/** has the notification been read */
hasRead?: boolean;
/** Rows number on the page (pagination parameter). Default is 25, max is 100 */
limit?: number;
/** Cursor ID (UUID) for pagination. Must be provided together with cursorCreatedAt */
cursorId?: string;
/** Cursor creation timestamp (RFC3339Nano format) for pagination. Must be provided together with cursorId */
cursorCreatedAt?: string;
/** Pagination direction. Use "next" to get next page (default) or "prev" to get previous page */
direction?: "next" | "prev";
};
export type GetNotificationByIdApiResponse = /** status 200 OK */ Notification;
export type GetNotificationByIdApiArg = {
notificationId: string;
};
export type ReadNotificationApiResponse = unknown;
export type ReadNotificationApiArg = {
notificationId: string;
};
export type ReadAllNotificationsApiResponse = unknown;
export type ReadAllNotificationsApiArg = void;
export type ArchiveNotificationApiResponse = unknown;
export type ArchiveNotificationApiArg = {
notificationId: string;
};
export type ArchiveAllNotificationsApiResponse = unknown;
export type ArchiveAllNotificationsApiArg = void;
export type CreateActionApiResponse = /** status 201 Created */ number;
export type CreateActionApiArg = {
actionCreateRequest: ActionCreateRequest;
};
export type GetAllActionsApiResponse = /** status 200 OK */ ActionGetResponse;
export type GetAllActionsApiArg = void;
export type GetAllEnumApiResponse = /** status 200 OK */ ActionEnumResponse;
export type GetAllEnumApiArg = void;
export type UpdateActionApiResponse = unknown;
export type UpdateActionApiArg = {
actionId: string;
actionUpdateRequest: ActionUpdateRequest;
};
export type DeleteActionApiResponse = unknown;
export type DeleteActionApiArg = {
actionId: string;
};
export type TopEquipmentItem = {
id?: string;
name?: string;
location?: {
id?: string;
name?: string;
};
counter?: number;
infoCounter?: number;
importantCounter?: number;
criticalCounter?: number;
normalCounter?: number;
};
export type TopControllerResponse = {
controllers?: TopEquipmentItem[];
totalNotifications?: number;
totalEntities?: number;
};
export type TopLocationItem = {
id?: string;
name?: string;
counter?: number;
infoCounter?: number;
importantCounter?: number;
criticalCounter?: number;
normalCounter?: number;
};
export type TopLocationResponse = {
locations?: TopLocationItem[];
totalNotifications?: number;
totalEntities?: number;
};
export type TopDeviceResponse = {
devices?: TopEquipmentItem[];
totalNotifications?: number;
totalEntities?: number;
};
export type TopNetworkDeviceResponse = {
networkDevices?: TopEquipmentItem[];
totalNotifications?: number;
totalEntities?: number;
};
export type TopPadletResponse = {
padlets?: TopEquipmentItem[];
totalNotifications?: number;
totalEntities?: number;
};
export type TopSortColumn = "info" | "normal" | "critical" | "important";
export type SortType = "desc" | "asc";
export type PriorityDiagram = {
infoCounter?: number;
importantCounter?: number;
criticalCounter?: number;
normalCounter?: number;
total?: number;
};
export type ActionDiagram = {
statusDisconnected?: number;
statusUnknown?: number;
statusConnected?: number;
statusWarning?: number;
statusDisabled?: number;
flapping?: number;
flappingResolved?: number;
icmpSuccess?: number;
icmpFailed?: number;
icmpUnknown?: number;
tcpFailed?: number;
tcpSuccess?: number;
tcpUnknown?: number;
gveUnknown?: number;
gveSuccess?: number;
gveFailed?: number;
gveMissingPackets?: number;
total?: number;
};
export type EntityType =
| "controller"
| "location"
| "device"
| "network-device"
| "padlet"
| "offline-device"
| "service-status"
| "ticket";
export type ActionNames =
| "create"
| "update"
| "delete"
| "status_disconnected"
| "status_unknown"
| "status_connected"
| "status_missing_packets"
| "service_status_psi"
| "service_status_in_service"
| "service_status_exploitation"
| "icmp_failed"
| "tcp_failed"
| "icmp_success"
| "tcp_success"
| "due_today"
| "due_tomorrow"
| "network_access_false"
| "is_monitoring_true";
export type Priority = "critical" | "important" | "normal" | "info";
export type Action = {
id?: number;
name?: ActionNames;
priority?: Priority;
};
export type Notification = {
id?: string;
message?: string;
entity?: {
id?: string;
name?: string;
type?: EntityType;
};
actor?: {
id?: string;
fullName?: string;
} | null;
reader?: {
id?: string;
fullName?: string;
} | null;
action?: Action;
attrs?: object;
createdAt?: string;
readAt?: string | null;
archivedAt?: string | null;
};
export type NotificationsGetResponse = {
notifications?: Notification[];
total?: number;
};
export type Cursor = {
createdAt: string;
id: string;
};
export type NotificationsCursorResponse = {
notifications: Notification[];
hasNext: boolean;
hasPrev: boolean;
nextCursor?: Cursor;
prevCursor?: Cursor;
total?: number;
};
export type ActionCreateRequest = {
name?: ActionNames;
priority?: Priority;
};
export type ActionGetResponse = {
actions?: Action[];
};
export type ActionEnumResponse = {
entityTypes: string[];
};
export type ActionUpdateRequest = {
name?: ActionNames;
priority?: Priority;
};
export const {
useGetTopQuery,
useGetPriorityDiagramQuery,
useGetActionDiagramQuery,
useGetNotificationsQuery,
useGetNotificationsCursorQuery,
useGetNotificationsCursorCountQuery,
useGetNotificationByIdQuery,
useReadNotificationMutation,
useReadAllNotificationsMutation,
useArchiveNotificationMutation,
useArchiveAllNotificationsMutation,
useCreateActionMutation,
useGetAllActionsQuery,
useGetAllEnumQuery,
useUpdateActionMutation,
useDeleteActionMutation,
} = injectedRtkApi;