Загрузка данных
import { WithLocationFilter } from '@/app/router/queries';
import { type AuditRouteSearch } from '@/app/router/routes/audit';
import { useCallback } from 'react';
type Params = {
searchParams: AuditRouteSearch;
updateParamAndResetPage: (paramName: string, value: unknown) => void;
};
export const useAuditLocationFilters = ({
searchParams,
updateParamAndResetPage,
}: Params) => {
const setLocationStringArray = useCallback(
(paramName: string, value: string[]) => {
updateParamAndResetPage(paramName, value);
},
[updateParamAndResetPage],
);
const setRegionId = useCallback(
(value: string[]) =>
setLocationStringArray(
WithLocationFilter.WITH_LOCATION_FILTER_QUERY.regionId,
value,
),
[setLocationStringArray],
);
const setCityId = useCallback(
(value: string[]) =>
setLocationStringArray(
WithLocationFilter.WITH_LOCATION_FILTER_QUERY.cityId,
value,
),
[setLocationStringArray],
);
const setPlaceId = useCallback(
(value: string[]) =>
setLocationStringArray(
WithLocationFilter.WITH_LOCATION_FILTER_QUERY.placeId,
value,
),
[setLocationStringArray],
);
const setFloorId = useCallback(
(value: string[]) =>
setLocationStringArray(
WithLocationFilter.WITH_LOCATION_FILTER_QUERY.floorId,
value,
),
[setLocationStringArray],
);
const setRoomId = useCallback(
(value: string[]) =>
setLocationStringArray(
WithLocationFilter.WITH_LOCATION_FILTER_QUERY.roomId,
value,
),
[setLocationStringArray],
);
const changeLocationIds = useCallback(
(value: string[]) => {
updateParamAndResetPage('locationIds', value);
},
[updateParamAndResetPage],
);
return {
locationIds: searchParams.locationIds,
cityId: searchParams.cityId,
floorId: searchParams.floorId,
placeId: searchParams.placeId,
regionId: searchParams.regionId,
roomId: searchParams.roomId,
setRegionId,
setCityId,
setPlaceId,
setFloorId,
setRoomId,
changeLocationIds,
};
};
import { type AuditRouteSearch } from '@/app/router/routes/audit';
import { useGetApiV1MessageTypesQuery } from '@/shared/api/audit/client';
import {
useGetUserByIdQuery,
useGetUsersQuery,
} from '@/shared/api/user/client';
import { useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { AuditLib } from '../../lib/AuditLib';
import { AUDIT_TYPE_ORDER } from './messagesOrder';
export const useAuditOptions = (searchParams: AuditRouteSearch) => {
const { t } = useTranslation('common');
const [searchUser, setSearchUser] = useState('');
const hasFetchedUser = useRef(false);
const { data: auditTypesData } = useGetApiV1MessageTypesQuery();
const { data: usersData, isFetching: isFetchingUsers } = useGetUsersQuery({
limit: 10,
sortType: 'asc',
sortColumn: 'full_name',
search: searchUser,
});
const { data: user } = useGetUserByIdQuery(
{ userId: searchParams.userId ?? '' },
{ skip: false },
);
useEffect(() => {
if (searchParams.userId && user) {
hasFetchedUser.current = true;
}
}, [searchParams.userId, user]);
const typeOptions = useMemo(() => {
const types = auditTypesData?.types;
if (!Array.isArray(types)) {
return [];
}
const typeSet = new Set<string>(types);
const orderedTypes = AUDIT_TYPE_ORDER.filter((type) => typeSet.has(type));
const unknownTypes = types.filter(
(type) =>
!AUDIT_TYPE_ORDER.includes(type as (typeof AUDIT_TYPE_ORDER)[number]),
);
return [
{ label: t('all'), value: undefined },
...[...orderedTypes, ...unknownTypes].map((type) => ({
value: type,
label: t(`audit:${type}`, { defaultValue: type }),
})),
];
}, [auditTypesData, t]);
const userOptions = useMemo(() => {
const options = usersData?.users
? AuditLib.mapUsersToOptions(usersData.users)
: [];
if (!searchParams.userId || !user) {
return options;
}
return [...options, { label: user.fullName!, value: user.id! }];
}, [searchParams.userId, user, usersData]);
return {
searchUser,
setSearchUser,
hasFetchedUser,
isFetchingUsers,
typeOptions,
userOptions,
};
};
import { useUpdateSearchParams } from '@/shared/hooks';
import { useCallback } from 'react';
export const useAuditSearchUpdater = () => {
const updateSearchParams = useUpdateSearchParams();
const resetPage = useCallback(() => {
updateSearchParams({ paramName: 'offset', value: 0 });
}, [updateSearchParams]);
const updateParamAndResetPage = useCallback(
(paramName: string, value: unknown) => {
updateSearchParams({ paramName, value });
resetPage();
},
[resetPage, updateSearchParams],
);
return {
updateSearchParams,
resetPage,
updateParamAndResetPage,
};
};
import { auditRoute } from '@/app/router/routes/audit';
import { AppConsts } from '@/shared';
import { useGetApiV1MessageQuery } from '@/shared/api/audit/client';
import { RouterConsts } from '@/shared/consts/RouterConsts';
import { ChangeEvent, useCallback, useState } from 'react';
import { useAuditLocationFilters } from './useAuditLocationFilters';
import { useAuditOptions } from './useAuditOptions';
import { useAuditSearchUpdater } from './useAuditSearchUpdater';
import { useSmartRoomIdFilter } from './useAuditSmartRoomIdFilter';
type ModelSelectValue = string | number | boolean | null;
export const useAuditSideFilter = () => {
const searchParams = auditRoute.useSearch();
const { refetch } = useGetApiV1MessageQuery(searchParams);
const { updateSearchParams, updateParamAndResetPage } =
useAuditSearchUpdater();
const [isLocationFiltersOpen, setIsLocationFiltersOpen] = useState(false);
const auditOptions = useAuditOptions(searchParams);
const smartRoomFilter = useSmartRoomIdFilter({
initialValue: searchParams.smartroomId,
updateParamAndResetPage,
});
const locationFilters = useAuditLocationFilters({
searchParams,
updateParamAndResetPage,
});
const handleChange =
(paramName: string) =>
(event: ChangeEvent<{ name?: string | undefined; value: unknown }>) => {
updateParamAndResetPage(paramName, event.target.value);
};
const handleChangeUser = useCallback(
(value: string) => {
updateParamAndResetPage('userId', value);
},
[updateParamAndResetPage],
);
const handleChangeTimeFrom = useCallback(
(time: Date) => {
updateParamAndResetPage(
'tmMin',
new Date(time).getTime() / AppConsts.THOUSAND,
);
},
[updateParamAndResetPage],
);
const handleChangeTimeMax = useCallback(
(time: Date) => {
updateParamAndResetPage(
'tmMax',
new Date(time).getTime() / AppConsts.THOUSAND,
);
},
[updateParamAndResetPage],
);
const handleMacChange = useCallback(
(event: ChangeEvent<HTMLInputElement>) => {
updateParamAndResetPage('mac', event.target.value || undefined);
},
[updateParamAndResetPage],
);
const handleIpChange = useCallback(
(event: ChangeEvent<HTMLInputElement>) => {
updateParamAndResetPage('ip', event.target.value || undefined);
},
[updateParamAndResetPage],
);
const handleChangeModels = useCallback(
(value: ModelSelectValue[]) => {
updateParamAndResetPage('modelIds', value.map(String));
},
[updateParamAndResetPage],
);
const handleLocationFiltersToggle = useCallback(() => {
setIsLocationFiltersOpen((prev) => !prev);
}, []);
const resetFilters = useCallback(() => {
updateSearchParams({
queryObject: { ...RouterConsts.AUDIT_DEFAULT_QUERY },
});
smartRoomFilter.resetSmartRoomId();
}, [smartRoomFilter, updateSearchParams]);
const refetchQuery = useCallback(() => {
refetch();
}, [refetch]);
return {
searchParams,
isLocationFiltersOpen,
...auditOptions,
...smartRoomFilter,
...locationFilters,
modelIds: searchParams.modelIds,
refetchQuery,
resetFilters,
handleChange,
handleChangeUser,
handleChangeTimeFrom,
handleChangeTimeMax,
handleMacChange,
handleIpChange,
handleLocationFiltersToggle,
handleChangeModels,
};
};
import { Helpers } from '@/shared/lib';
import { ChangeEvent, useCallback, useEffect, useState } from 'react';
type Params = {
initialValue?: string;
updateParamAndResetPage: (paramName: string, value: unknown) => void;
};
export const useSmartRoomIdFilter = ({
initialValue,
updateParamAndResetPage,
}: Params) => {
const [smartRoomId, setSmartRoomId] = useState(initialValue || '');
const [smartRoomIdError, setSmartRoomIdError] = useState(false);
useEffect(() => {
const valid = Helpers.isUUID(smartRoomId);
setSmartRoomIdError(smartRoomId !== '' && !valid);
updateParamAndResetPage(
'smartroomId',
valid || smartRoomId === '' ? smartRoomId : '',
);
}, [smartRoomId, updateParamAndResetPage]);
const handleSmartRoomIdChange = useCallback(
(event: ChangeEvent<HTMLInputElement>) => {
setSmartRoomId(event.target.value);
},
[],
);
const resetSmartRoomId = useCallback(() => {
setSmartRoomId('');
setSmartRoomIdError(false);
}, []);
return {
smartRoomId,
smartRoomIdError,
handleSmartRoomIdChange,
resetSmartRoomId,
};
};
import { LocationFilters } from '@/features/filters/LocationFilters/LocationFilters';
import { ModelSelect } from '@/features/Settings/selects';
import { EquipmentModel } from '@/features/Settings/selects/lib';
import { AppConsts } from '@/shared';
import {
Button,
Column,
DateTimeField,
IconButton,
Row,
Select,
TextField,
Typography,
} from '@/shared/components';
import { SearchSelect } from '@/shared/components/SearchSelect';
import { ChevronsDownIcon, ChevronsUpIcon } from '@sber-friend/flamingo-icons';
import { useTranslation } from 'react-i18next';
import { useAuditSideFilter } from '../controller';
export const AuditSideFilter = () => {
const { t } = useTranslation('common');
const {
searchParams,
isFetchingUsers,
userOptions,
typeOptions,
smartRoomId,
smartRoomIdError,
modelIds,
cityId,
floorId,
placeId,
regionId,
roomId,
locationIds,
isLocationFiltersOpen,
setSearchUser,
resetFilters,
refetchQuery,
handleChangeTimeFrom,
handleChange,
handleChangeTimeMax,
handleChangeUser,
handleSmartRoomIdChange,
handleLocationFiltersToggle,
handleChangeModels,
setRegionId,
setCityId,
setPlaceId,
setFloorId,
setRoomId,
changeLocationIds,
} = useAuditSideFilter();
return (
<Column minWidth={330} maxWidth={330} gridGap={16}>
<DateTimeField
label={t('timeFrom')}
onChange={handleChangeTimeFrom}
margin='none'
value={searchParams.tmMin && searchParams.tmMin * AppConsts.THOUSAND}
/>
<DateTimeField
label={t('timeTo')}
margin='none'
onChange={handleChangeTimeMax}
value={searchParams.tmMax && searchParams.tmMax * AppConsts.THOUSAND}
/>
<Select
label={t('type')}
onChange={handleChange('type')}
options={typeOptions}
value={searchParams.type}
/>
<SearchSelect
label={t('user')}
placeholder={t('user:placeholders.searchByFullName')}
loading={isFetchingUsers}
value={searchParams?.userId || ''}
setValue={setSearchUser}
options={userOptions}
onChangeCallback={handleChangeUser}
/>
<ModelSelect
multiple
isCreateButton={false}
type={EquipmentModel.device}
value={modelIds}
onChange={handleChangeModels}
/>
<Select
label={t('source')}
onChange={handleChange('source')}
value={searchParams.source}
options={[
{ label: t('all'), value: undefined },
{ label: t('monitoring'), value: 'smartroom-monitoring' },
{ label: t('authentication'), value: 'smartroom-auth' },
{
label: t('notifications:notifications'),
value: 'smartroom-notifications',
},
]}
/>
<TextField
label='Smartroom ID'
value={smartRoomId}
onChange={handleSmartRoomIdChange}
error={smartRoomIdError}
helperText={smartRoomIdError ? t('messages.invalidUUIDMessage') : ''}
/>
<Row justifyContent='space-between'>
<Typography variant='h4'>Поиск локации</Typography>
<IconButton onClick={handleLocationFiltersToggle}>
{isLocationFiltersOpen ? <ChevronsUpIcon /> : <ChevronsDownIcon />}
</IconButton>
</Row>
{isLocationFiltersOpen && (
<Column gridGap={16}>
<LocationFilters
isRooms
outputIdType='id'
regionId={regionId}
setRegionId={setRegionId}
cityId={cityId}
setCityId={setCityId}
placeId={placeId}
setPlaceId={setPlaceId}
floorId={floorId}
setFloorId={setFloorId}
roomId={roomId}
setRoomId={setRoomId}
ids={locationIds}
withDropdown={false}
onLocationIdsChange={changeLocationIds}
/>
</Column>
)}
<Column gridGap={8}>
<Button onClick={refetchQuery} color='primary'>
{t('buttons.update')}
</Button>
<Button onClick={resetFilters} color='error'>
{t('buttons.reset')}
</Button>
</Column>
</Column>
);
};