Merge branch 'test' into AURORA-1057

This commit is contained in:
Veronika Tsygankova 2026-06-05 12:03:41 +03:00
commit b27b8f3143
6 changed files with 113 additions and 33 deletions

View File

@ -7,9 +7,9 @@ export const UsersApi = {
getById(userId) {
return api.get(`/users/${userId}`).then((r) => r.data);
},
getAll() {
getAll(params = {}) {
return api
.get("/users/", { params: { limit: 1000, skip: 0, load_orgs: true } })
.get("/users/", { params: { limit: 1000, skip: 0, ...params } })
.then((r) => r.data);
},
update(idUser, data) {

View File

@ -22,9 +22,10 @@ import {
} from './constants/tableConfig';
import { useRealtime } from './contexts/RealtimeContext';
import { toast } from 'react-toastify';
import { CircularProgress } from '@mui/material';
const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
const { data, setData } = useRealtimeData(formId, sheetName, direction);
const { data, setData, isLoading: isTableLoading } = useRealtimeData(formId, sheetName, direction);
const [globalFilter, setGlobalFilter] = useState('');
const [showColumnFilters, setShowColumnFilters] = useState(false);
const [selectedColumn, setSelectedColumn] = useState();
@ -65,7 +66,7 @@ const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
const { headerPortalRef, containerRef } = useHeaderPortal();
const { sizeMult, setSizeMult, tableScaleStyle, tableWrapperStyle } =
useTableScale(containerRef);
useTableScale();
const [columnsConfig, setColumnsConfig] = useState(null);
@ -125,11 +126,19 @@ const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
columns,
data: data,
enableRowVirtualization: true,
enableColumnVirtualization: true,
rowVirtualizerOptions: {
overscan: 50,
overscan: 30,
scrollPaddingStart: 0,
scrollPaddingEnd: 0,
},
columnVirtualizerOptions: ({ table }) => ({
overscan: 5,
measureElement: (el) => {
const index = Number(el?.getAttribute?.('data-index'));
return table.getVisibleLeafColumns()[index]?.getSize() ?? 0;
},
}),
enableRowSelection: true,
onRowSelectionChange: setRowSelection,
onColumnSizingChange: setColumnSizing,
@ -141,20 +150,15 @@ const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
columnVisibility,
globalFilter,
showColumnFilters,
rowSelection
rowSelection,
},
initialState: {
expanded: true
},
meta: tableMeta,
muiTableHeadCellProps: ({ column }) => ({
sx: {
width: column.getSize(),
minWidth: column.getSize(),
maxWidth: column.getSize(),
boxSizing: 'border-box',
muiTableHeadCellProps: {
sx: { boxSizing: 'border-box' },
},
}),
muiTableBodyCellProps: getTableHeadCellStyles,
muiTableHeadProps: {
sx: {
@ -231,10 +235,6 @@ const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
setRowSelection({});
}, [rowSelection, table])
if (!data) {
return <div>Загрузка...</div>;
}
return (
<>
<SettingsPanel
@ -266,6 +266,22 @@ const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
>
<MaterialReactTable table={table} />
{isTableLoading && (
<div
style={{
position: 'absolute',
inset: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: 'rgba(255, 255, 255, 0.6)',
zIndex: 2,
}}
>
<CircularProgress />
</div>
)}
{headerPortalRef.current &&
createPortal(
<TableHead

View File

@ -6,15 +6,20 @@ import { UsersApi } from '../../../api/users';
import { toast } from 'react-toastify';
import { AutocompleteForChangeOptions } from '../components/AutocompleteForChangeOptions';
export const ModalChangeUsersInSsp = ({ open, onClose, ssp, onEdit }) => {
export const ModalChangeUsersInSsp = ({
open,
onClose,
ssp,
onUsersCountChange,
}) => {
const [users, setUsers] = useState([]);
const [usersDataForUpdate, setUsersDataForUpdate] = useState({
update: [],
delete: [],
});
const [allUsers, setAllUsers] = useState([]);
const [loadingUsers, setLoadingUsers] = useState(false);
useEffect(() => {
UsersApi.getAll()
.then((data) => {
@ -58,14 +63,66 @@ export const ModalChangeUsersInSsp = ({ open, onClose, ssp, onEdit }) => {
});
}, [open, ssp]);
const handleClickSave = () => {
onEdit?.(ssp, usersDataForUpdate);
const deleteUserFromSsp = async (user) => {
try {
const data = await SspApi.deleteManySsps(user.id, { ssp_ids: [ssp.id] });
if (!data.success) {
toast.error(data?.message || 'Ошибка удаления пользователя из ССП');
return false;
}
return true;
} catch (error) {
const msg =
error?.response?.data?.message || error?.response?.data?.detail;
toast.error(msg || 'Ошибка удаления пользователя из ССП');
return false;
}
};
const addUserToSsp = async (user) => {
try {
const data = await SspApi.putManySsps(user.id, { ssp_ids: [ssp.id] });
if (!data.success) {
toast.error(data?.message || 'Ошибка добавления пользователя');
return false;
}
return true;
} catch (error) {
const msg =
error?.response?.data?.message || error?.response?.data?.detail;
toast.error(msg || 'Ошибка добавления пользователя');
return false;
}
};
const handleClickSave = async () => {
let added = 0;
let removed = 0;
for (const user of usersDataForUpdate.update) {
if (await addUserToSsp(user)) {
added += 1;
}
}
for (const user of usersDataForUpdate.delete) {
if (await deleteUserFromSsp(user)) {
removed += 1;
}
}
const delta = added - removed;
if (delta !== 0) {
onUsersCountChange?.(ssp.id, delta);
}
onClose?.();
};
const handleChangeUsersList = (dataForUpdate) => {
setUsersDataForUpdate(dataForUpdate);
const handleChangeUsersList = (updater) => {
setUsersDataForUpdate(updater);
};
if (!ssp) return;
return (
<Modal

View File

@ -376,7 +376,7 @@ const SspsPage = () => {
open={openModalUsersinSspChange}
onClose={handleCloseModalUsersInSspChange}
ssp={sspForChangeUsers}
onEdit={handleUpdateUsers}
onUsersCountChange={adjustSspUsersCount}
/>
<ModalEditSsp
open={openModalEdit}

View File

@ -34,7 +34,7 @@ const UsersPage = () => {
const loadUsers = async () => {
setLoading(true);
try {
const data = await UsersApi.getAll();
const data = await UsersApi.getAll({ load_orgs: true });
if (!data.success) {
toast.error('Ошибка получения пользователей ' + data.message);
return;

View File

@ -37,7 +37,7 @@ export const AutocompleteForChangeOptions = ({
if (value) {
const newSelected = [...selected, value];
setSelected(newSelected);
onChange?.((prev) => ({ ...prev, update: newSelected }));
onChange?.((prev) => ({ ...prev, update: [...prev.update, value] }));
const newAvailable = available.filter((item) => item.id !== value.id);
setAvailable(newAvailable);
@ -49,16 +49,23 @@ export const AutocompleteForChangeOptions = ({
const handleRemove = (id) => {
const itemToRemove = selected.find((s) => s.id === id);
if (itemToRemove) {
if (!itemToRemove) return;
const newSelected = selected.filter((s) => s.id !== id);
setSelected(newSelected);
setAvailable([...available, itemToRemove]);
const wasInitial = values.some((v) => v.id === id);
if (wasInitial) {
onChange?.((prev) => ({
...prev,
delete: [...prev.delete, itemToRemove],
update: [...newSelected],
}));
} else {
onChange?.((prev) => ({
...prev,
update: prev.update.filter((u) => u.id !== id),
}));
}
};
@ -130,7 +137,7 @@ export const AutocompleteForChangeOptions = ({
/>
)}
renderOption={(props, option) => (
<li {...props} style={{ padding: '4px 12px', minHeight: '36px' }}>
<li {...props} style={{ padding: '4px 12px', minHeight: '36px' }} key={option.id}>
<Box
sx={{
display: 'flex',