[AURORA-1057] - Ошибка экспорта задач
This commit is contained in:
parent
e47200733e
commit
9fc054acd4
@ -33,13 +33,15 @@ export const TasksApi = {
|
|||||||
createProjectTable(taskId, payload) {
|
createProjectTable(taskId, payload) {
|
||||||
return api.post(`/tasks/${taskId}/projects`, payload).then((r) => r.data);
|
return api.post(`/tasks/${taskId}/projects`, payload).then((r) => r.data);
|
||||||
},
|
},
|
||||||
exportTask(taskId) {
|
exportTask(taskId, params = {}) {
|
||||||
return api.get(`export/task/${taskId}`, {
|
return api.get(`export/form/${taskId}`, {
|
||||||
|
params,
|
||||||
responseType: "blob",
|
responseType: "blob",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
exportTasks(payload) {
|
exportTasks(payload, params = {}) {
|
||||||
return api.post(`export/bulk`, payload, {
|
return api.post(`export/bulk`, payload, {
|
||||||
|
params,
|
||||||
responseType: "blob",
|
responseType: "blob",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
150
web/src/components/common/ExportFormModal.jsx
Normal file
150
web/src/components/common/ExportFormModal.jsx
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
FormControl,
|
||||||
|
FormLabel,
|
||||||
|
MenuItem,
|
||||||
|
Select,
|
||||||
|
} from '@mui/material';
|
||||||
|
import Modal from './Modal/Modal';
|
||||||
|
import { TasksApi } from '../../api/tasks';
|
||||||
|
import { downloadFile } from '../../utils/downloadFile';
|
||||||
|
import { getApiErrorMessage } from '../../utils/getApiErrorMessage';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
|
const DIRECTION_OPTIONS = [
|
||||||
|
{ value: 'Development', label: 'Development' },
|
||||||
|
{ value: 'Support', label: 'Support' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const ExportFormModal = ({
|
||||||
|
open,
|
||||||
|
onClose,
|
||||||
|
onSuccess,
|
||||||
|
taskId,
|
||||||
|
taskTitle = 'form',
|
||||||
|
formIds,
|
||||||
|
fileName = 'Задачи',
|
||||||
|
}) => {
|
||||||
|
const isBulk = Boolean(formIds?.length);
|
||||||
|
const [direction, setDirection] = useState('');
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open) {
|
||||||
|
setDirection('');
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}, [open]);
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
if (!loading) {
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleExport = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const params = direction ? { direction } : {};
|
||||||
|
|
||||||
|
if (isBulk) {
|
||||||
|
const response = await TasksApi.exportTasks(
|
||||||
|
{ form_ids: formIds },
|
||||||
|
params,
|
||||||
|
);
|
||||||
|
await downloadFile(response, fileName, 'xlsx');
|
||||||
|
} else if (taskId) {
|
||||||
|
const response = await TasksApi.exportTask(taskId, params);
|
||||||
|
await downloadFile(response, taskTitle, 'xlsx');
|
||||||
|
}
|
||||||
|
|
||||||
|
onSuccess?.();
|
||||||
|
onClose();
|
||||||
|
} catch (error) {
|
||||||
|
const message = await getApiErrorMessage(
|
||||||
|
error,
|
||||||
|
'Ошибка при скачивании файла',
|
||||||
|
);
|
||||||
|
toast.error(message);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title="Экспорт формы"
|
||||||
|
open={open}
|
||||||
|
onClose={handleClose}
|
||||||
|
customSize={31.25}
|
||||||
|
actions={
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
variant="grey"
|
||||||
|
onClick={handleClose}
|
||||||
|
disabled={loading}
|
||||||
|
style={{ height: '2.5rem' }}
|
||||||
|
>
|
||||||
|
Отменить
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
onClick={handleExport}
|
||||||
|
disabled={loading}
|
||||||
|
style={{ height: '2.5rem' }}
|
||||||
|
>
|
||||||
|
{loading ? 'Экспорт...' : 'Экспортировать'}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
|
||||||
|
<FormLabel
|
||||||
|
sx={{
|
||||||
|
fontWeight: 400,
|
||||||
|
fontSize: '0.75rem',
|
||||||
|
color: '#4a5565',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
direction
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl fullWidth disabled={loading}>
|
||||||
|
<Select
|
||||||
|
value={direction}
|
||||||
|
displayEmpty
|
||||||
|
onChange={(e) => setDirection(e.target.value)}
|
||||||
|
renderValue={(selected) =>
|
||||||
|
selected ? (
|
||||||
|
DIRECTION_OPTIONS.find((o) => o.value === selected)?.label
|
||||||
|
) : (
|
||||||
|
<Box component="span" sx={{ color: 'rgba(0,0,0,0.4)' }}>
|
||||||
|
Выберите direction
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
MenuProps={{
|
||||||
|
style: { zIndex: 1301 },
|
||||||
|
PaperProps: { sx: { zIndex: 1301 } },
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
height: '2rem',
|
||||||
|
fontSize: '0.875rem',
|
||||||
|
'& .MuiOutlinedInput-notchedOutline': {
|
||||||
|
borderRadius: '0.5rem',
|
||||||
|
borderColor: 'rgba(0, 0, 0, 0.1)',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{DIRECTION_OPTIONS.map((option) => (
|
||||||
|
<MenuItem key={option.value} value={option.value}>
|
||||||
|
{option.label}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -26,7 +26,6 @@ import { formatDate } from '../../utils/formatDate';
|
|||||||
import { IconWithContent } from './IconWithContent';
|
import { IconWithContent } from './IconWithContent';
|
||||||
import { Chip } from '../styles/StyledChip';
|
import { Chip } from '../styles/StyledChip';
|
||||||
import { ExportButton } from '../common/Buttons/ButtonsActions';
|
import { ExportButton } from '../common/Buttons/ButtonsActions';
|
||||||
import { downloadFile } from '../../utils/downloadFile';
|
|
||||||
|
|
||||||
const TaskCard = styled.div`
|
const TaskCard = styled.div`
|
||||||
width: 26.8rem;
|
width: 26.8rem;
|
||||||
@ -93,6 +92,7 @@ export const TaskCardComponent = ({
|
|||||||
exportMode = false,
|
exportMode = false,
|
||||||
isLoadingFile = false,
|
isLoadingFile = false,
|
||||||
onAddForExport,
|
onAddForExport,
|
||||||
|
onExportClick,
|
||||||
}) => {
|
}) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { id, title, name, start_date, end_date, tables_count, org_unit, form_type_code } = taskForm;
|
const { id, title, name, start_date, end_date, tables_count, org_unit, form_type_code } = taskForm;
|
||||||
@ -132,14 +132,9 @@ export const TaskCardComponent = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleExportTask = async (event) => {
|
const handleExportTask = (event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
try {
|
onExportClick?.(id, title);
|
||||||
const response = await TasksApi.exportTask(id);
|
|
||||||
await downloadFile(response, title, 'xlsx');
|
|
||||||
} catch (error) {
|
|
||||||
toast.error('Ошибка при скачивании файла');
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateTask = async (nextTitle) => {
|
const updateTask = async (nextTitle) => {
|
||||||
|
|||||||
@ -19,8 +19,8 @@ import { SettingModal as SettingStageModal } from '../../components/Stages/Setti
|
|||||||
import { PrimaryOutlinedButton } from '../../components/common/Buttons/Buttons';
|
import { PrimaryOutlinedButton } from '../../components/common/Buttons/Buttons';
|
||||||
import { FormsApi } from '../../api/forms';
|
import { FormsApi } from '../../api/forms';
|
||||||
import { ModalCreateProject } from './ModalCreateProject';
|
import { ModalCreateProject } from './ModalCreateProject';
|
||||||
import { downloadFile } from '../../utils/downloadFile';
|
|
||||||
import { ExportWithTextButton } from '../../components/common/Buttons/ButtonsActions';
|
import { ExportWithTextButton } from '../../components/common/Buttons/ButtonsActions';
|
||||||
|
import { ExportFormModal } from '../../components/common/ExportFormModal';
|
||||||
import TablesList from '../../components/common/TableList/TableList';
|
import TablesList from '../../components/common/TableList/TableList';
|
||||||
import { SHEET_NAME } from '../../constants';
|
import { SHEET_NAME } from '../../constants';
|
||||||
|
|
||||||
@ -55,6 +55,7 @@ export default function TaskPage() {
|
|||||||
const [query, setQuery] = useState('');
|
const [query, setQuery] = useState('');
|
||||||
const [modalStageOpen, setModalStageOpen] = useState(false);
|
const [modalStageOpen, setModalStageOpen] = useState(false);
|
||||||
const [modalProjectOpen, setModalProjectOpen] = useState(false);
|
const [modalProjectOpen, setModalProjectOpen] = useState(false);
|
||||||
|
const [exportModalOpen, setExportModalOpen] = useState(false);
|
||||||
const [tempProjects, setTempProjects] = useState([]);
|
const [tempProjects, setTempProjects] = useState([]);
|
||||||
const [projects, setProjects] = useState([]);
|
const [projects, setProjects] = useState([]);
|
||||||
|
|
||||||
@ -120,15 +121,6 @@ export default function TaskPage() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleExport = async () => {
|
|
||||||
try {
|
|
||||||
const response = await TasksApi.exportTask(taskId);
|
|
||||||
await downloadFile(response, task.title, 'xlsx');
|
|
||||||
} catch (error) {
|
|
||||||
toast.error('Ошибка при скачивании файла');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box
|
<Box
|
||||||
@ -156,7 +148,7 @@ export default function TaskPage() {
|
|||||||
onChange={setQuery}
|
onChange={setQuery}
|
||||||
/>
|
/>
|
||||||
<Stack sx={{ flexDirection: 'row', spacing: '.5rem', justifyContent: 'end' }}>
|
<Stack sx={{ flexDirection: 'row', spacing: '.5rem', justifyContent: 'end' }}>
|
||||||
<ExportWithTextButton onClick={handleExport} />
|
<ExportWithTextButton onClick={() => setExportModalOpen(true)} />
|
||||||
<PrimaryOutlinedButton
|
<PrimaryOutlinedButton
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onClick={() => setModalStageOpen(true)}
|
onClick={() => setModalStageOpen(true)}
|
||||||
@ -181,6 +173,12 @@ export default function TaskPage() {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
<ExportFormModal
|
||||||
|
open={exportModalOpen}
|
||||||
|
onClose={() => setExportModalOpen(false)}
|
||||||
|
taskId={taskId}
|
||||||
|
taskTitle={task?.title ?? 'form'}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -27,11 +27,11 @@ import { ROLES_NAME_ID } from '../constants';
|
|||||||
import { useAuth } from '../app/context/AuthProvider';
|
import { useAuth } from '../app/context/AuthProvider';
|
||||||
import { SspApi } from '../api/ssp';
|
import { SspApi } from '../api/ssp';
|
||||||
import { PrimaryButton } from '../components/common/Buttons/Buttons';
|
import { PrimaryButton } from '../components/common/Buttons/Buttons';
|
||||||
import { downloadFile } from '../utils/downloadFile';
|
|
||||||
import {
|
import {
|
||||||
ExportExitButton,
|
ExportExitButton,
|
||||||
ExportWithTextButton,
|
ExportWithTextButton,
|
||||||
} from '../components/common/Buttons/ButtonsActions';
|
} from '../components/common/Buttons/ButtonsActions';
|
||||||
|
import { ExportFormModal } from '../components/common/ExportFormModal';
|
||||||
import { WhitePlus } from '../components/common/icons/icons';
|
import { WhitePlus } from '../components/common/icons/icons';
|
||||||
|
|
||||||
export default function TasksPage() {
|
export default function TasksPage() {
|
||||||
@ -60,7 +60,12 @@ export default function TasksPage() {
|
|||||||
|
|
||||||
const [exportMode, setExportMode] = useState(false);
|
const [exportMode, setExportMode] = useState(false);
|
||||||
const [exportList, setExportList] = useState([]);
|
const [exportList, setExportList] = useState([]);
|
||||||
const [isLoadingFile, setIsLoadingFile] = useState(false);
|
const [exportModal, setExportModal] = useState({
|
||||||
|
open: false,
|
||||||
|
taskId: null,
|
||||||
|
taskTitle: null,
|
||||||
|
formIds: null,
|
||||||
|
});
|
||||||
|
|
||||||
const openModal = () => {
|
const openModal = () => {
|
||||||
setModalOpen(true);
|
setModalOpen(true);
|
||||||
@ -185,16 +190,31 @@ export default function TasksPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleExport = async () => {
|
const openBulkExportModal = () => {
|
||||||
setIsLoadingFile(true);
|
setExportModal({
|
||||||
try {
|
open: true,
|
||||||
const response = await TasksApi.exportTasks({ task_ids: exportList });
|
taskId: null,
|
||||||
await downloadFile(response, 'Задачи', 'xlsx');
|
taskTitle: null,
|
||||||
setExportMode(false);
|
formIds: exportList,
|
||||||
} catch (error) {
|
});
|
||||||
toast.error('Ошибка при скачивании файла');
|
};
|
||||||
}
|
|
||||||
setIsLoadingFile(false);
|
const openSingleExportModal = (taskId, taskTitle) => {
|
||||||
|
setExportModal({
|
||||||
|
open: true,
|
||||||
|
taskId,
|
||||||
|
taskTitle,
|
||||||
|
formIds: null,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeExportModal = () => {
|
||||||
|
setExportModal({
|
||||||
|
open: false,
|
||||||
|
taskId: null,
|
||||||
|
taskTitle: null,
|
||||||
|
formIds: null,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Calculate total pages
|
// Calculate total pages
|
||||||
@ -236,13 +256,10 @@ export default function TasksPage() {
|
|||||||
<>
|
<>
|
||||||
<ExportWithTextButton
|
<ExportWithTextButton
|
||||||
text={'Экспортировать'}
|
text={'Экспортировать'}
|
||||||
onClick={handleExport}
|
onClick={openBulkExportModal}
|
||||||
disabled={isLoadingFile}
|
disabled={exportList.length === 0}
|
||||||
/>
|
|
||||||
<ExportExitButton
|
|
||||||
onClick={() => setExportMode(false)}
|
|
||||||
disabled={isLoadingFile}
|
|
||||||
/>
|
/>
|
||||||
|
<ExportExitButton onClick={() => setExportMode(false)} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
@ -258,12 +275,12 @@ export default function TasksPage() {
|
|||||||
{items.map((t) => (
|
{items.map((t) => (
|
||||||
<TaskCardComponent
|
<TaskCardComponent
|
||||||
exportMode={exportMode}
|
exportMode={exportMode}
|
||||||
isLoadingFile={exportMode ? isLoadingFile : false}
|
|
||||||
taskForm={t}
|
taskForm={t}
|
||||||
key={t.id + '-task-id'}
|
key={t.id + '-task-id'}
|
||||||
onAddForExport={(checked) => {
|
onAddForExport={(checked) => {
|
||||||
handleChangeToExportList(t.id, checked);
|
handleChangeToExportList(t.id, checked);
|
||||||
}}
|
}}
|
||||||
|
onExportClick={openSingleExportModal}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
@ -444,6 +461,19 @@ export default function TasksPage() {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
<ExportFormModal
|
||||||
|
open={exportModal.open}
|
||||||
|
onClose={closeExportModal}
|
||||||
|
onSuccess={() => {
|
||||||
|
if (exportModal.formIds?.length) {
|
||||||
|
setExportMode(false);
|
||||||
|
}
|
||||||
|
closeExportModal();
|
||||||
|
}}
|
||||||
|
taskId={exportModal.taskId}
|
||||||
|
taskTitle={exportModal.taskTitle ?? 'form'}
|
||||||
|
formIds={exportModal.formIds}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
26
web/src/utils/getApiErrorMessage.js
Normal file
26
web/src/utils/getApiErrorMessage.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
export const getApiErrorMessage = async (
|
||||||
|
error,
|
||||||
|
fallback = 'Произошла ошибка',
|
||||||
|
) => {
|
||||||
|
const data = error?.response?.data;
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return error?.message || fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data instanceof Blob) {
|
||||||
|
try {
|
||||||
|
const text = await data.text();
|
||||||
|
const json = JSON.parse(text);
|
||||||
|
return json.message || json.detail || text || fallback;
|
||||||
|
} catch {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof data === 'string') {
|
||||||
|
return data || fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.message || data.detail || fallback;
|
||||||
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user