Загрузка данных


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;