Merge pull request 'fix-stages: поправлены ручки по этапам и отображение данных' (#37) from fix-stages into test

Reviewed-on: #37
This commit is contained in:
Veronika 2026-06-05 17:00:24 +03:00
commit a206bb7191
19 changed files with 226 additions and 268 deletions

View File

@ -2,20 +2,25 @@ import api from './client';
export const StagesApi = {
createStage(formId, payload) {
return api.put(`/forms/${formId}/stages`, [payload]).then((r) => r.data);
return api.post(`/stages/form/${formId}`, [payload]).then((r) => r.data);
},
updateStage(formId, stage) {
updateStage(payload) {
const { budget_form_id, sheet, phase_code, opens_at, closes_at } = payload;
return api
.patch(`/forms/${formId}/stages/${stage.id}`, stage)
.patch(`/stages/form/${budget_form_id}/${sheet}/${phase_code}`, {
opens_at,
closes_at,
})
.then((r) => r.data);
},
deleteStage(formId, stage) {
deleteStage(formId, sheet, phaseCode) {
return api
.delete(`/forms/${formId}/stages/${stage.id}`)
.delete(`/stages/form/${formId}/${sheet}/${phaseCode}`)
.then((r) => r.data);
},
getStages(formId) {
return api.get(`/forms/${formId}/stages`).then((r) => r.data);
return api.get(`/stages/form/${formId}`).then((r) => r.data);
},
createStageAccessHeader(tableId, stageId, payload) {
return api

View File

@ -13,9 +13,6 @@ export const TasksApi = {
getById(formId) {
return api.get(`/form/${formId}`).then((r) => r.data);
},
getTaskStages(taskId) {
return api.get(`/tasks/${taskId}/stages`).then((r) => r.data);
},
create(payload) {
return api.put("/tasks/", payload).then((r) => r.data);
},

View File

@ -30,42 +30,40 @@ export const AddStagesModal = ({
isTaskMode = false,
isSaving = false,
}) => {
const [stageName, setStageName] = useState('');
const [startDate, setStartDate] = useState(null);
const [endDate, setEndDate] = useState(null);
useEffect(() => {
if (stage !== null) {
setStageName(stage.name);
setStartDate(stage.period_start || null);
setEndDate(stage.period_end || null);
setStartDate(stage.opens_at ?? null);
setEndDate(stage.closes_at ?? null);
} else {
setStageName('');
setStartDate(null);
setEndDate(null);
}
}, [stage, isOpen]);
const handleSave = () => {
const periodStart = formatDateForApi(startDate);
const periodEnd = formatDateForApi(endDate);
const opensAt = formatDateForApi(startDate);
const closesAt = formatDateForApi(endDate);
if (stage === null) {
createStage(stageName, periodStart, periodEnd)?.then(() => {
if (!createStage) return;
createStage(opensAt, closesAt)?.then(() => {
handleCancel();
});
} else {
stage.name = stageName;
stage.period_start = periodStart;
stage.period_end = periodEnd;
updateStage(stage)?.then(() => {
updateStage({
...stage,
opens_at: opensAt,
closes_at: closesAt,
})?.then(() => {
handleCancel();
});
}
};
const handleCancel = () => {
setStageName('');
setStartDate(null);
setEndDate(null);
onClose();
@ -77,7 +75,7 @@ export const AddStagesModal = ({
}
};
const isFormValid = stageName && startDate && endDate;
const isFormValid = startDate && endDate;
if (!isOpen) return null;
@ -88,7 +86,7 @@ export const AddStagesModal = ({
<ModalBackdrop isOpen={isOpen} onClick={handleBackdropClick}>
<ModalContainer>
<HeaderContainer>
<Stack direction="row" spacing={2} alignItems="center">
<Stack direction="row" spacing={2} sx={{ alignItems: 'center' }}>
<ModalTitle>
{stage ? 'Редактирование этапа' : 'Добавление этапа'}
</ModalTitle>
@ -108,22 +106,6 @@ export const AddStagesModal = ({
<Box>
<Divider sx={{ marginBottom: '0.75rem' }} />
<Stack
direction="column"
spacing={0.5}
sx={{ marginBottom: '0.75rem' }}
>
<FormControl fullWidth>
<FormLabel>Название этапа</FormLabel>
<TextField
value={stageName}
onChange={(e) => setStageName(e.target.value)}
placeholder="Введите название этапа"
fullWidth
size="small"
/>
</FormControl>
</Stack>
<Stack
direction="row"

View File

@ -6,97 +6,74 @@ import {
CalendarDay,
CalendarWeekday,
} from './Calendar.styles';
import { YearTabs } from './YearTabs';
import { CalendarHeader } from './CalendarHeader';
import { weekdayNames } from './constants';
import { convertYearFromBackend } from '../StagesTable/utils';
import { isPastDate } from '../utils';
export const Calendar = ({ value, onChange, onClose, isTaskMode = false }) => {
const currentYearValue = new Date().getFullYear();
const getToday = () => {
const today = new Date();
today.setHours(0, 0, 0, 0);
return today;
};
const initialDate = isTaskMode
? value
? new Date(value)
: null
: convertYearFromBackend(value);
const initialDisplayYear = initialDate
? initialDate.getFullYear()
: currentYearValue;
const parseValue = (dateString) => {
if (!dateString) return null;
const date = new Date(dateString);
return Number.isNaN(date.getTime()) ? null : date;
};
const [selectedYear, setSelectedYear] = useState(initialDisplayYear);
export const Calendar = ({ value, onChange, onClose }) => {
const initialDate = parseValue(value);
const [currentDate, setCurrentDate] = useState(initialDate || new Date());
const [selectedDate, setSelectedDate] = useState(initialDate);
useEffect(() => {
if (value) {
const date = isTaskMode ? new Date(value) : convertYearFromBackend(value);
const date = parseValue(value);
if (date) {
setCurrentDate(date);
setSelectedDate(date);
setSelectedYear(date.getFullYear());
}
}
}, [value, isTaskMode]);
const handleYearChange = (newYear) => {
setSelectedYear(newYear);
setCurrentDate(new Date(newYear, currentDate.getMonth(), 1));
};
}, [value]);
const year = currentDate.getFullYear();
const month = currentDate.getMonth();
const firstDayOfMonth = new Date(year, month, 1);
const lastDayOfMonth = new Date(year, month + 1, 0);
const firstDayOfWeek = (firstDayOfMonth.getDay() + 6) % 7; // Понедельник = 0
const firstDayOfWeek = (firstDayOfMonth.getDay() + 6) % 7;
const daysInMonth = lastDayOfMonth.getDate();
// Нельзя листать раньше января текущего года
const isPrevMonthDisabled = year === currentYearValue && month === 0;
// Нельзя листать дальше декабря следующего года
const isNextMonthDisabled = year === currentYearValue + 1 && month === 11;
const today = getToday();
const isPrevMonthDisabled =
year < today.getFullYear() ||
(year === today.getFullYear() && month <= today.getMonth());
const handlePrevMonth = () => {
if (!isPrevMonthDisabled) {
if (isPrevMonthDisabled) return;
setCurrentDate(new Date(year, month - 1, 1));
}
};
const handleNextMonth = () => {
if (!isNextMonthDisabled) {
setCurrentDate(new Date(year, month + 1, 1));
}
};
const handleDateClick = (day) => {
const newDate = new Date(year, month, day);
if (isPastDate(newDate)) return;
setSelectedDate(newDate);
// Преобразуем год для бэкенда: текущий - 1970, следующий - 1971
// Для задач используем реальный год
let yearForBackend = year;
if (!isTaskMode) {
if (selectedYear === currentYearValue) {
yearForBackend = 1970;
} else if (selectedYear === currentYearValue + 1) {
yearForBackend = 1971;
}
}
const formattedDate = `${yearForBackend}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
const formattedDate = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
onChange(formattedDate);
onClose();
};
const isToday = (day) => {
const today = new Date();
return (
const isToday = (day) =>
day === today.getDate() &&
month === today.getMonth() &&
year === today.getFullYear()
);
};
year === today.getFullYear();
const isDayPast = (day) => isPastDate(new Date(year, month, day));
const isSelected = (day) => {
if (!selectedDate) return false;
@ -119,14 +96,12 @@ export const Calendar = ({ value, onChange, onClose, isTaskMode = false }) => {
return (
<CalendarContainer>
<YearTabs selectedYear={selectedYear} onYearChange={handleYearChange} />
<CalendarHeader
month={month}
year={year}
onPrevMonth={handlePrevMonth}
onNextMonth={handleNextMonth}
isPrevMonthDisabled={isPrevMonthDisabled}
isNextMonthDisabled={isNextMonthDisabled}
/>
<Box>
@ -143,6 +118,7 @@ export const Calendar = ({ value, onChange, onClose, isTaskMode = false }) => {
onClick={() => handleDateClick(day)}
isToday={isToday(day)}
isSelected={isSelected(day)}
isDisabled={isDayPast(day)}
>
{day}
</CalendarDay>

View File

@ -37,7 +37,8 @@ export const CalendarWeekday = styled(Box)`
`;
export const CalendarDay = styled(Box, {
shouldForwardProp: (prop) => prop !== 'isToday' && prop !== 'isSelected',
shouldForwardProp: (prop) =>
prop !== 'isToday' && prop !== 'isSelected' && prop !== 'isDisabled',
})`
display: flex;
align-items: center;
@ -70,4 +71,16 @@ export const CalendarDay = styled(Box, {
background: #258141;
}
`}
${(props) =>
props.isDisabled &&
`
color: #d1d5db;
cursor: not-allowed;
pointer-events: none;
&:hover {
background: transparent;
}
`}
`;

View File

@ -6,10 +6,10 @@ import { monthNames } from './constants';
export const CalendarHeader = ({
month,
year,
onPrevMonth,
onNextMonth,
isPrevMonthDisabled,
isNextMonthDisabled,
}) => {
return (
<StyledCalendarHeader>
@ -20,12 +20,10 @@ export const CalendarHeader = ({
>
<ChevronLeftIcon />
</IconButton>
<Typography variant="body1">{monthNames[month]}</Typography>
<IconButton
size="small"
onClick={onNextMonth}
disabled={isNextMonthDisabled}
>
<Typography variant="body1">
{monthNames[month]} {year}
</Typography>
<IconButton size="small" onClick={onNextMonth}>
<ChevronRightIcon />
</IconButton>
</StyledCalendarHeader>

View File

@ -9,35 +9,13 @@ import {
} from '@mui/material';
import { CalendarIcon } from '../../common/icons/icons';
import { Calendar } from './Calendar';
import { formatDate } from '../../../utils/formatDate';
export const CalendarInput = ({ value, onChange, isTaskMode = false }) => {
const [isOpen, setIsOpen] = useState(false);
const containerRef = useRef(null);
const [anchorPosition, setAnchorPosition] = useState(null);
const formatDateForDisplay = (dateString) => {
if (!dateString) return '';
const date = new Date(dateString);
const currentYear = new Date().getFullYear();
const backendYear = date.getFullYear();
const day = String(date.getDate()).padStart(2, '0');
const month = String(date.getMonth() + 1).padStart(2, '0');
if (isTaskMode) {
return `${day}.${month}.${backendYear}`;
}
if (backendYear === 1970 || backendYear === currentYear) {
return `Текущий год: ${day}.${month}`;
} else if (backendYear === 1971 || backendYear === currentYear + 1) {
return `Следующий год: ${day}.${month}`;
} else {
return `${day}.${month}.${backendYear}`;
}
};
const handleOpen = () => {
if (containerRef.current) {
const rect = containerRef.current.getBoundingClientRect();
@ -58,7 +36,7 @@ export const CalendarInput = ({ value, onChange, isTaskMode = false }) => {
<>
<Box sx={{ position: 'relative' }} ref={containerRef}>
<TextField
value={formatDateForDisplay(value)}
value={value ? formatDate(value) : ''}
onClick={handleOpen}
slotProps={{
input: {
@ -131,7 +109,6 @@ export const CalendarInput = ({ value, onChange, isTaskMode = false }) => {
value={value}
onChange={onChange}
onClose={handleClose}
isTaskMode={isTaskMode}
/>
</Box>
</ClickAwayListener>

View File

@ -23,7 +23,7 @@ export const ModalContainer = styled.div`
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-end;
align-items: stretch;
gap: 1rem;
padding: 1.25rem;
box-sizing: border-box;

View File

@ -10,7 +10,14 @@ import { useTaskStages } from '../../hooks/useTaskStages';
import { AddStagesModal } from './AddStagesModal';
import { DeleteStageModal } from './DeleteStageModal';
import { WhitePlus } from '../common/icons/icons';
import { Button, IconButton, Stack, Typography } from '@mui/material';
import {
Box,
Button,
CircularProgress,
IconButton,
Stack,
Typography,
} from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
import { StagesTable } from './StagesTable/StagesTable';
@ -37,10 +44,10 @@ export const SettingModal = ({
const entityId = mode === 'task' ? taskId : formId;
useEffect(() => {
if (entityId) {
if (isOpen && entityId) {
getStages();
}
}, [entityId, getStages]);
}, [isOpen, entityId, getStages]);
const [isAddEditModalOpen, setIsAddEditModalOpen] = useState(false);
const [stageForEdit, setStageForEdit] = useState(null);
@ -76,9 +83,12 @@ export const SettingModal = ({
isOpen={isOpen}
onClose={onClose}
children={
<ModalContainer style={{ position: 'relative' }}>
<ModalContainer>
<ModalContent>
<Stack direction="row" justifyContent="flex-end" width="100%">
<Stack
direction="row"
sx={{ width: '100%', justifyContent: 'flex-end' }}
>
<IconButton size="small" onClick={onClose}>
<CloseIcon />
</IconButton>
@ -87,8 +97,7 @@ export const SettingModal = ({
<EditContainer>
<Stack
direction="row"
justifyContent="space-between"
alignItems="center"
sx={{ width: '100%', justifyContent: 'space-between' }}
>
<Typography fontWeight="bold">Этапы</Typography>
{!isTaskMode && (
@ -102,11 +111,23 @@ export const SettingModal = ({
)}
</Stack>
{stagesInfo.isLoading ? (
<Box
sx={{
display: 'flex',
justifyContent: 'center',
py: 4,
}}
>
<CircularProgress />
</Box>
) : (
<StagesTable
stages={stagesInfo.stages || []}
onEdit={handleEditStage}
onDelete={!isTaskMode ? handleDeleteStage : undefined}
/>
)}
<AddStagesModal
isOpen={isAddEditModalOpen}
onClose={() => {

View File

@ -17,13 +17,10 @@ import {
StyledTableHeadRow,
StyledTableRow,
} from './StagesTable.style';
import {
getStageStatus,
getStatusColors,
convertYearForDisplay,
} from './utils';
import { getStageStatus, getStatusColors } from './utils';
import { formatDate } from '../../../utils/formatDate';
import { StageStatusChip } from '../StageStatusChip';
import { ROLES_NAME_ID } from '../../../constants';
import { ROLES_NAME_ID, STAGE_ROLE_RUSSIAN_NAME } from '../../../constants';
import { useAuth } from '../../../app/context/AuthProvider';
export const StagesTable = ({ stages, onEdit, onDelete }) => {
@ -63,7 +60,7 @@ export const StagesTable = ({ stages, onEdit, onDelete }) => {
<StyledTable>
<StyledTableHead>
<StyledTableHeadRow>
<TableCell>Название</TableCell>
<TableCell>Название таблицы</TableCell>
<TableCell>Начало</TableCell>
<TableCell>Окончание</TableCell>
<TableCell>Статус</TableCell>
@ -81,22 +78,30 @@ export const StagesTable = ({ stages, onEdit, onDelete }) => {
</TableCell>
</TableRow>
) : (
stages.map((stage) => {
stages.map((stage, index) => {
const status = getStageStatus(stage);
const colors = getStatusColors(status);
const rowKey = `${stage.sheet}-${stage.phase_code}-${stage.role}-${index}`;
return (
<StyledTableRow key={stage.id}>
<StyledTableRow key={rowKey}>
<TableCell>
<Typography variant="body2">{stage.name}</Typography>
</TableCell>
<TableCell>
<Typography variant="body2">
{convertYearForDisplay(stage.period_start)}
<Typography variant="body2">{stage.sheet}</Typography>
<Typography
variant="caption"
color="text.secondary"
sx={{ fontSize: '0.65rem' }}
>
{STAGE_ROLE_RUSSIAN_NAME[stage.role] ?? stage.role}
</Typography>
</TableCell>
<TableCell>
<Typography variant="body2">
{convertYearForDisplay(stage.period_end)}
{formatDate(stage.opens_at)}
</Typography>
</TableCell>
<TableCell>
<Typography variant="body2">
{formatDate(stage.closes_at)}
</Typography>
</TableCell>
<TableCell>

View File

@ -1,59 +1,26 @@
// Конвертации года из бэкенда (1970/1971) в реальный год
export const convertYearFromBackend = (dateString) => {
if (!dateString) return null;
const date = new Date(dateString);
const backendYear = date.getFullYear();
const currentYear = new Date().getFullYear();
let realYear = backendYear;
if (backendYear === 1970) {
realYear = currentYear;
} else if (backendYear === 1971) {
realYear = currentYear + 1;
}
return new Date(realYear, date.getMonth(), date.getDate());
};
// Форматирование даты для отображения
export const convertYearForDisplay = (dateString) => {
if (!dateString) return '';
const date = convertYearFromBackend(dateString);
if (!date) return '';
const day = String(date.getDate()).padStart(2, '0');
const month = String(date.getMonth() + 1).padStart(2, '0');
const year = date.getFullYear();
return `${day}.${month}.${year}`;
};
export const getStageStatus = (stage) => {
if (!stage.period_start || !stage.period_end) {
const start = stage.opens_at;
const end = stage.closes_at;
if (!start || !end) {
return 'Не определен';
}
const now = new Date();
now.setHours(0, 0, 0, 0);
const startDate = convertYearFromBackend(stage.period_start);
const endDate = convertYearFromBackend(stage.period_end);
if (!startDate || !endDate) {
return 'Не определен';
}
const startDate = new Date(start);
const endDate = new Date(end);
startDate.setHours(0, 0, 0, 0);
endDate.setHours(0, 0, 0, 0);
if (now < startDate) {
return 'Запланирован';
} else if (now >= startDate && now < endDate) {
return 'Активен';
} else {
return 'Завершен';
}
if (now >= startDate && now < endDate) {
return 'Активен';
}
return 'Завершен';
};
export const getStatusColors = (status) => {

View File

@ -6,3 +6,12 @@ export const formatDateForApi = (value) => {
const day = String(d.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
};
export const isPastDate = (value) => {
if (!value) return false;
const date = new Date(value);
date.setHours(0, 0, 0, 0);
const today = new Date();
today.setHours(0, 0, 0, 0);
return date < today;
};

View File

@ -10,7 +10,7 @@ const ModalContainer = styled.div`
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-end;
align-items: stretch;
padding: 1.25rem;
box-sizing: border-box;

View File

@ -1,9 +0,0 @@
// TODO: есть уже похожая функция в utils, может модернизировать ее
export const formatToDDMMYYYY = (dateString) => {
const date = new Date(dateString);
const day = String(date.getDate()).padStart(2, '0');
const month = String(date.getMonth() + 1).padStart(2, '0');
//у нас год только текущий и следующий, поэтому 1970 считается текущим
const year = date.getFullYear() == 1970 ? 'y' : 'y+1';
return `${day}.${month}.${year}`;
};

View File

@ -18,6 +18,12 @@ export const ROLES_ID_RUSSIAN_NAME_ARRAY = [
{ id: 3, role: 'Исполнитель' },
];
export const STAGE_ROLE_RUSSIAN_NAME = {
ADMIN: 'Администратор',
DFIP: 'Исполнитель ДФИП',
EXECUTOR_RF: 'Исполнитель',
};
export const TYPES_CELLS = { numeric: 'число', text: 'текст' };
export const EDIT_ACCESS = { 1: 'RESTRICT', 2: 'VIEW', 3: 'EDIT' };

View File

@ -12,15 +12,17 @@ export const useStages = (formId) => {
const getStages = useCallback(() => {
if (!formId) return;
setStagesInfo((prev) => ({ ...prev, isLoading: true }));
StagesApi.getStages(formId)
.then((data) => {
setStagesInfo((prev) => ({
...prev,
stages: data.result,
isLoading: true,
}));
setStagesInfo({
stages: data.result ?? [],
isLoading: false,
});
})
.catch((error) => {
setStagesInfo((prev) => ({ ...prev, isLoading: false }));
toast.error(
error?.response?.data?.detail || 'Ошибка при получении этапов',
);
@ -28,12 +30,11 @@ export const useStages = (formId) => {
}, [formId]);
const createStage = useCallback(
(stageName, periodStart, periodEnd) => {
(periodStart, periodEnd) => {
if (!formId) return Promise.reject('Нет formId');
setIsSaving(true);
const payload = {
name: stageName,
period_start: periodStart,
period_end: periodEnd,
};
@ -65,17 +66,18 @@ export const useStages = (formId) => {
if (!formId) return Promise.reject('Нет formId');
setIsSaving(true);
return StagesApi.updateStage(formId, stage)
return StagesApi.updateStage(stage)
.then((data) => {
setStagesInfo((prev) => {
const filterStages = prev.stages.filter(
(curStage) => curStage.id !== stage.id,
);
const allStages = [...filterStages, data.result].sort(
(a, b) => a.id - b.id,
);
return { ...prev, stages: allStages };
});
setStagesInfo((prev) => ({
...prev,
stages: prev.stages.map((curStage) =>
curStage.sheet === stage.sheet &&
curStage.phase_code === stage.phase_code
? data.result
: curStage,
),
}));
toast.success('Данные об этапе обновлены');
return data;
})
@ -94,16 +96,21 @@ export const useStages = (formId) => {
const deleteStage = useCallback(
(stage) => {
if (!formId) return;
const { sheet, phase_code, budget_form_id } = stage;
if (!budget_form_id || !sheet || !phase_code) return;
StagesApi.deleteStage(formId, stage)
.then((data) => {
setStagesInfo((prev) => {
const filterStages = prev.stages.filter(
(curStage) => curStage.id !== stage.id,
);
return { ...prev, stages: filterStages };
});
StagesApi.deleteStage(budget_form_id, sheet, phase_code)
.then(() => {
setStagesInfo((prev) => ({
...prev,
stages: prev.stages.filter(
(curStage) =>
!(
curStage.sheet === sheet &&
curStage.phase_code === phase_code
),
),
}));
toast.success('Этап успешно удален');
})
.catch((error) => {

View File

@ -1,6 +1,6 @@
import { useState, useCallback } from 'react';
import { TasksApi } from '../api/tasks';
import { toast } from 'react-toastify';
import { StagesApi } from '../api/stages';
export const useTaskStages = (taskId) => {
const [stagesInfo, setStagesInfo] = useState({
@ -12,17 +12,17 @@ export const useTaskStages = (taskId) => {
const getStages = useCallback(() => {
if (!taskId) return;
TasksApi.getTaskStages(taskId)
setStagesInfo((prev) => ({ ...prev, isLoading: true }));
StagesApi.getStages(taskId)
.then((data) => {
if (data.result) {
setStagesInfo((prev) => ({
...prev,
stages: data.result,
isLoading: true,
}));
}
setStagesInfo({
stages: data.result ?? [],
isLoading: false,
});
})
.catch((error) => {
setStagesInfo((prev) => ({ ...prev, isLoading: false }));
toast.error(
error?.response?.data?.detail || 'Ошибка при получении этапов',
);
@ -34,17 +34,18 @@ export const useTaskStages = (taskId) => {
if (!taskId) return Promise.reject('Нет taskId');
setIsSaving(true);
return TasksApi.editTaskStages(taskId, stage.id, stage)
return StagesApi.updateStage(stage)
.then((data) => {
setStagesInfo((prev) => {
const filterStages = prev.stages.filter(
(curStage) => curStage.id !== stage.id,
);
const allStages = [...filterStages, data.result].sort(
(a, b) => a.id - b.id,
);
return { ...prev, stages: allStages };
});
setStagesInfo((prev) => ({
...prev,
stages: prev.stages.map((curStage) =>
curStage.sheet === stage.sheet &&
curStage.phase_code === stage.phase_code
? data.result
: curStage,
),
}));
toast.success('Данные об этапе обновлены');
return data;
})

View File

@ -10,6 +10,7 @@ import theme from './app/theme/theme';
import './index.css';
import App from './App';
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
const baseName = (process.env.REACT_APP_ROOT_PATH || '/').replace(/\/$/, '');
@ -31,7 +32,7 @@ createRoot(document.getElementById('root')).render(
}}
>
<App />
<ToastContainer autoClose={2000} />
<ToastContainer autoClose={2000} style={{ zIndex: 10000 }} />
</BrowserRouter>
</AuthProvider>
</LocalizationProvider>

View File

@ -146,7 +146,7 @@ export default function TaskPage() {
value={query}
onChange={setQuery}
/>
<Stack sx={{ flexDirection: 'row', spacing: '.5rem', justifyContent: 'end' }}>
<Stack sx={{ flexDirection: 'row', spacing: '.5rem', justifyContent: 'end', gap: '.5rem' }}>
<ExportWithTextButton
onClick={() => exportSingleForm(taskId, task?.title ?? 'form')}
/>
@ -156,12 +156,14 @@ export default function TaskPage() {
>
<span>Этапы</span>
</PrimaryOutlinedButton>
{modalStageOpen && (
<SettingStageModal
isOpen={modalStageOpen}
onClose={() => setModalStageOpen(false)}
taskId={taskId}
mode="task"
/>
)}
</Stack>
</Box>