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


import { SideFilterSelect } from '@/shared/components/SideFilterSelect/SideFilterSelect';
import { Helpers } from '@/shared/lib';
import { CheckIcon, SortIcon } from '@sber-friend/flamingo-icons';
import { Badge, Box, Button, Dropdown } from '@shared/components';
import { ChangeEvent, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';

// TO DO вынести в отдельный файл с типами
type LocationEvent = ChangeEvent<{ name?: string; value: unknown }>;

type LocationOutputIdType = 'treeId' | 'id';

type LocationLevel = 'region' | 'city' | 'place' | 'floor' | 'room';

type SelectedLocationIdsByLevel = Record<LocationLevel, number[]>;

type LocationFiltersProps = {
  'data-fui-tid'?: string;
  withDropdown?: boolean;
  isRooms?: boolean;
  ids?: number[];

  /**
   * Что отдавать наружу в onLocationIdsChange.
   * treeId - старое поведение по умолчанию.
   * id - обычный location id, при этом внутренние запросы все равно идут по treeId.
   */
  outputIdType?: LocationOutputIdType;

  regionId: string[];
  cityId: string[];
  placeId: string[];
  floorId: string[];
  roomId: string[];

  setRegionId: (regionId: string[]) => void;
  setCityId: (cityId: string[]) => void;
  setPlaceId: (placeId: string[]) => void;
  setFloorId: (floorId: string[]) => void;
  setRoomId: (roomId: string[]) => void;

  onLocationIdsChange: (locationIds: number[]) => void;
};

const EMPTY_SELECTED_LOCATION_IDS: SelectedLocationIdsByLevel = {
  region: [],
  city: [],
  place: [],
  floor: [],
  room: [],
};

const getDeepestSelectedLevel = ({
  regionId,
  cityId,
  placeId,
  floorId,
  roomId,
}: Pick<
  LocationFiltersProps,
  'regionId' | 'cityId' | 'placeId' | 'floorId' | 'roomId'
>): LocationLevel => {
  if (roomId?.length > 0) {
    return 'room';
  }

  if (floorId?.length > 0) {
    return 'floor';
  }

  if (placeId?.length > 0) {
    return 'place';
  }

  if (cityId?.length > 0) {
    return 'city';
  }

  return 'region';
};

// TO DO refactor вместо locationId будет treeId
export const LocationFilters = ({
  'data-fui-tid': tid,
  isRooms = false,
  ids,
  regionId,
  cityId,
  placeId,
  floorId,
  roomId,
  setRegionId,
  setCityId,
  setFloorId,
  setPlaceId,
  setRoomId,
  onLocationIdsChange,
  withDropdown = true,
  outputIdType = 'treeId',
}: LocationFiltersProps) => {
  const { t } = useTranslation('locations');

  const [selectedLocationIdsByLevel, setSelectedLocationIdsByLevel] =
    useState<SelectedLocationIdsByLevel>(EMPTY_SELECTED_LOCATION_IDS);

  useEffect(() => {
    if (ids?.length === 0) {
      setRegionId([]);
      setCityId([]);
      setPlaceId([]);
      setFloorId([]);
      setRoomId([]);
    }
  }, [ids, setCityId, setFloorId, setPlaceId, setRegionId, setRoomId]);

  const locationTreeIds =
    roomId?.length > 0
      ? roomId
      : floorId?.length > 0
        ? floorId
        : placeId?.length > 0
          ? placeId
          : cityId?.length > 0
            ? cityId
            : regionId;

  const deepestSelectedLevel = useMemo(
    () =>
      getDeepestSelectedLevel({
        regionId,
        cityId,
        placeId,
        floorId,
        roomId,
      }),
    [cityId, floorId, placeId, regionId, roomId],
  );

  const outputLocationIds = useMemo(() => {
    if (outputIdType === 'treeId') {
      return Helpers.convertStringsToNumbers(locationTreeIds);
    }

    return selectedLocationIdsByLevel[deepestSelectedLevel];
  }, [
    deepestSelectedLevel,
    locationTreeIds,
    outputIdType,
    selectedLocationIdsByLevel,
  ]);

  useEffect(() => {
    if (outputIdType === 'id' && locationTreeIds.length > 0) {
      const selectedIds = selectedLocationIdsByLevel[deepestSelectedLevel];

      if (selectedIds.length !== locationTreeIds.length) {
        return;
      }
    }

    onLocationIdsChange(outputLocationIds);
  }, [
    deepestSelectedLevel,
    locationTreeIds.length,
    onLocationIdsChange,
    outputIdType,
    outputLocationIds,
    selectedLocationIdsByLevel,
  ]);

  const updateSelectedLocationIdsByLevel =
    (level: LocationLevel) => (locationIds: number[]) => {
      setSelectedLocationIdsByLevel((prev) => ({
        ...prev,
        [level]: locationIds,
      }));
    };

  const updateLocationIds = (newLocationIds: number[]) => {
    onLocationIdsChange(newLocationIds);
  };

  const handleChangeRegion = (event: LocationEvent) => {
    const { value } = event.target;
    const newRegionId = value as string[];

    setRegionId(newRegionId);
    setCityId([]);
    setPlaceId([]);
    setFloorId([]);
    setRoomId([]);
  };

  const handleChangeCity = (event: LocationEvent) => {
    const { value } = event.target;
    const newCityId = value as string[];

    setCityId(newCityId);
    setPlaceId([]);
    setFloorId([]);
    setRoomId([]);
  };

  const handleChangePlace = (event: LocationEvent) => {
    const { value } = event.target;
    const newPlaceId = value as string[];

    setPlaceId(newPlaceId);
    setFloorId([]);
    setRoomId([]);
  };

  const handleChangeFloor = (event: LocationEvent) => {
    const { value } = event.target;
    const newFloorId = value as string[];

    setFloorId(newFloorId);
    setRoomId([]);
  };

  const handleChangeRoom = (event: LocationEvent) => {
    const { value } = event.target;
    const newRoomId = value as string[];

    setRoomId(newRoomId);
  };

  const handleReset = () => {
    setRegionId([]);
    setCityId([]);
    setPlaceId([]);
    setFloorId([]);
    setRoomId([]);
    setSelectedLocationIdsByLevel(EMPTY_SELECTED_LOCATION_IDS);
    updateLocationIds([]);
  };

  const [anchorElement, setAnchorElement] = useState(null);

  const handleOpen = (event: any) => {
    setAnchorElement(event.currentTarget);
  };

  const handleClose = (event: any) => {
    event.preventDefault();
    setAnchorElement(null);
  };

  const filtersContent = (
    <>
      <SideFilterSelect
        value={regionId}
        label={t('region')}
        onChange={handleChangeRegion}
        onSelectedLocationIdsChange={updateSelectedLocationIdsByLevel('region')}
        multiple
        isRoot
      />
      <SideFilterSelect
        value={cityId}
        parentIds={regionId}
        label={t('city')}
        onChange={handleChangeCity}
        onSelectedLocationIdsChange={updateSelectedLocationIdsByLevel('city')}
        multiple
      />
      <SideFilterSelect
        value={placeId}
        parentIds={cityId}
        label={t('place')}
        onChange={handleChangePlace}
        onSelectedLocationIdsChange={updateSelectedLocationIdsByLevel('place')}
        multiple
      />
      <SideFilterSelect
        value={floorId}
        parentIds={placeId}
        label={t('floor')}
        onChange={handleChangeFloor}
        onSelectedLocationIdsChange={updateSelectedLocationIdsByLevel('floor')}
        multiple
        isFloor
      />
      {isRooms && (
        <SideFilterSelect
          value={roomId}
          parentIds={floorId}
          label={t('room')}
          onChange={handleChangeRoom}
          onSelectedLocationIdsChange={updateSelectedLocationIdsByLevel('room')}
          multiple
        />
      )}
      <Button size='small' onClick={handleReset}>
        {t('common:buttons.reset')}
      </Button>
    </>
  );

  if (!withDropdown) {
    return filtersContent;
  }

  return (
    <Badge
      invisible={
        !regionId?.length &&
        !cityId?.length &&
        !placeId?.length &&
        !floorId?.length &&
        !roomId?.length
      }
      badgeContent={<CheckIcon />}
      color='primary'
    >
      <Dropdown
        data-fui-tid={tid}
        size='small'
        icon={<SortIcon />}
        onClickButton={handleOpen}
        onClosePopover={handleClose}
        zIndexMenu={100}
        anchorElement={anchorElement}
      >
        <Box
          display={'flex'}
          gridGap={8}
          flexDirection={'column'}
          p={2}
          minWidth={330}
          maxWidth={330}
        >
          {filtersContent}
        </Box>
      </Dropdown>
    </Badge>
  );
};