Merge pull request '[AURORA-1057] - экспорт форм' (#36) from AURORA-1057 into test
Reviewed-on: #36
This commit is contained in:
commit
ee6fafb84f
@ -6,4 +6,10 @@ export const FormsSheetApi = {
|
||||
.get(`/form/${formId}/sheet/${sheetName}`, params)
|
||||
.then((r) => r.data);
|
||||
},
|
||||
export(formId, sheetName, params = {}) {
|
||||
return api.get(`export/form/${formId}/sheet/${sheetName}`, {
|
||||
params,
|
||||
responseType: 'blob',
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
@ -33,13 +33,15 @@ export const TasksApi = {
|
||||
createProjectTable(taskId, payload) {
|
||||
return api.post(`/tasks/${taskId}/projects`, payload).then((r) => r.data);
|
||||
},
|
||||
exportTask(taskId) {
|
||||
return api.get(`export/task/${taskId}`, {
|
||||
exportTask(taskId, params = {}) {
|
||||
return api.get(`export/form/${taskId}`, {
|
||||
params,
|
||||
responseType: "blob",
|
||||
});
|
||||
},
|
||||
exportTasks(payload) {
|
||||
exportTasks(payload, params = {}) {
|
||||
return api.post(`export/bulk`, payload, {
|
||||
params,
|
||||
responseType: "blob",
|
||||
});
|
||||
},
|
||||
|
||||
@ -251,6 +251,9 @@ const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
|
||||
onAddRow={handleAddRow}
|
||||
onDeleteRow={handleDeleteRow}
|
||||
isLoadingData={isLoadingData}
|
||||
formId={formId}
|
||||
sheetName={sheetName}
|
||||
direction={direction}
|
||||
/>
|
||||
|
||||
<div style={tableWrapperStyle}>
|
||||
|
||||
@ -17,6 +17,8 @@ import {
|
||||
import CollapsibleTree from './CollapsibleTree';
|
||||
import ZoomSlider from './ZoomSlider';
|
||||
import SearchComponent from '../../common/SearchComponent';
|
||||
import { ExportDefaultButton } from '../../common/Buttons/ButtonsActions';
|
||||
import { exportSheet } from '../../../utils/exportForm';
|
||||
|
||||
const SettingPanel = ({
|
||||
selectedColumn,
|
||||
@ -32,8 +34,12 @@ const SettingPanel = ({
|
||||
onAddRow,
|
||||
onDeleteRow,
|
||||
isLoadingData,
|
||||
formId,
|
||||
sheetName,
|
||||
direction,
|
||||
}) => {
|
||||
const [isPinned, setIsPinned] = useState(false);
|
||||
const [isExporting, setIsExporting] = useState(false);
|
||||
const [columnTree, setColumnTree] = useState();
|
||||
const [selectedColumnIds, setSelectedColumnIds] = useState();
|
||||
const [showColumnFilters, setShowColumnFilters] = useState(false);
|
||||
@ -70,6 +76,13 @@ const SettingPanel = ({
|
||||
|
||||
const handleClickDeleteFormula = () => { };
|
||||
|
||||
const handleExport = async () => {
|
||||
if (!formId || !sheetName || isExporting) return;
|
||||
setIsExporting(true);
|
||||
await exportSheet(formId, sheetName, direction);
|
||||
setIsExporting(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Panel>
|
||||
@ -156,6 +169,14 @@ const SettingPanel = ({
|
||||
</GroupByObject>
|
||||
|
||||
<Divider orientation="vertical" flexItem />
|
||||
|
||||
<GroupByObject title="Экспорт">
|
||||
<ExportDefaultButton
|
||||
onClick={handleExport}
|
||||
disabled={!formId || !sheetName || isExporting}
|
||||
text={isExporting ? 'Экспорт...' : 'Экспорт в Excel'}
|
||||
/>
|
||||
</GroupByObject>
|
||||
</Stack>
|
||||
</Panel>
|
||||
</>
|
||||
|
||||
@ -115,16 +115,11 @@ export const DefaultOutlinedButton = styled(Button)(({ width, height }) => ({
|
||||
color: '#323031',
|
||||
|
||||
'&:hover': {
|
||||
background: '#4a5565',
|
||||
color: 'white',
|
||||
'& svg path': {
|
||||
color: 'white !important',
|
||||
fill: 'white !important',
|
||||
stroke: 'white !important',
|
||||
},
|
||||
background: '#f3f3f5',
|
||||
borderColor: 'rgba(0, 0, 0, 0.2)',
|
||||
},
|
||||
'&:active': {
|
||||
background: '#f3f3f5',
|
||||
background: '#e7e7ea',
|
||||
},
|
||||
|
||||
'&.Mui-disabled': {
|
||||
|
||||
@ -26,7 +26,6 @@ import { formatDate } from '../../utils/formatDate';
|
||||
import { IconWithContent } from './IconWithContent';
|
||||
import { Chip } from '../styles/StyledChip';
|
||||
import { ExportButton } from '../common/Buttons/ButtonsActions';
|
||||
import { downloadFile } from '../../utils/downloadFile';
|
||||
|
||||
const TaskCard = styled.div`
|
||||
width: 26.8rem;
|
||||
@ -93,6 +92,7 @@ export const TaskCardComponent = ({
|
||||
exportMode = false,
|
||||
isLoadingFile = false,
|
||||
onAddForExport,
|
||||
onExportClick,
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
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();
|
||||
try {
|
||||
const response = await TasksApi.exportTask(id);
|
||||
await downloadFile(response, title, 'xlsx');
|
||||
} catch (error) {
|
||||
toast.error('Ошибка при скачивании файла');
|
||||
}
|
||||
onExportClick?.(id, title);
|
||||
};
|
||||
|
||||
const updateTask = async (nextTitle) => {
|
||||
|
||||
@ -19,8 +19,8 @@ import { SettingModal as SettingStageModal } from '../../components/Stages/Setti
|
||||
import { PrimaryOutlinedButton } from '../../components/common/Buttons/Buttons';
|
||||
import { FormsApi } from '../../api/forms';
|
||||
import { ModalCreateProject } from './ModalCreateProject';
|
||||
import { downloadFile } from '../../utils/downloadFile';
|
||||
import { ExportWithTextButton } from '../../components/common/Buttons/ButtonsActions';
|
||||
import { exportSingleForm } from '../../utils/exportForm';
|
||||
import TablesList from '../../components/common/TableList/TableList';
|
||||
import { SHEET_NAME } from '../../constants';
|
||||
|
||||
@ -120,15 +120,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 (
|
||||
<>
|
||||
<Box
|
||||
@ -156,7 +147,9 @@ export default function TaskPage() {
|
||||
onChange={setQuery}
|
||||
/>
|
||||
<Stack sx={{ flexDirection: 'row', spacing: '.5rem', justifyContent: 'end' }}>
|
||||
<ExportWithTextButton onClick={handleExport} />
|
||||
<ExportWithTextButton
|
||||
onClick={() => exportSingleForm(taskId, task?.title ?? 'form')}
|
||||
/>
|
||||
<PrimaryOutlinedButton
|
||||
variant="outlined"
|
||||
onClick={() => setModalStageOpen(true)}
|
||||
|
||||
@ -27,11 +27,11 @@ import { ROLES_NAME_ID } from '../constants';
|
||||
import { useAuth } from '../app/context/AuthProvider';
|
||||
import { SspApi } from '../api/ssp';
|
||||
import { PrimaryButton } from '../components/common/Buttons/Buttons';
|
||||
import { downloadFile } from '../utils/downloadFile';
|
||||
import {
|
||||
ExportExitButton,
|
||||
ExportWithTextButton,
|
||||
} from '../components/common/Buttons/ButtonsActions';
|
||||
import { exportBulkForms, exportSingleForm } from '../utils/exportForm';
|
||||
import { WhitePlus } from '../components/common/icons/icons';
|
||||
|
||||
export default function TasksPage() {
|
||||
@ -60,8 +60,6 @@ export default function TasksPage() {
|
||||
|
||||
const [exportMode, setExportMode] = useState(false);
|
||||
const [exportList, setExportList] = useState([]);
|
||||
const [isLoadingFile, setIsLoadingFile] = useState(false);
|
||||
|
||||
const openModal = () => {
|
||||
setModalOpen(true);
|
||||
};
|
||||
@ -185,16 +183,13 @@ export default function TasksPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleExport = async () => {
|
||||
setIsLoadingFile(true);
|
||||
try {
|
||||
const response = await TasksApi.exportTasks({ task_ids: exportList });
|
||||
await downloadFile(response, 'Задачи', 'xlsx');
|
||||
const handleBulkExport = async () => {
|
||||
await exportBulkForms(exportList);
|
||||
setExportMode(false);
|
||||
} catch (error) {
|
||||
toast.error('Ошибка при скачивании файла');
|
||||
}
|
||||
setIsLoadingFile(false);
|
||||
};
|
||||
|
||||
const handleSingleExport = (taskId, taskTitle) => {
|
||||
exportSingleForm(taskId, taskTitle);
|
||||
};
|
||||
|
||||
// Calculate total pages
|
||||
@ -236,13 +231,10 @@ export default function TasksPage() {
|
||||
<>
|
||||
<ExportWithTextButton
|
||||
text={'Экспортировать'}
|
||||
onClick={handleExport}
|
||||
disabled={isLoadingFile}
|
||||
/>
|
||||
<ExportExitButton
|
||||
onClick={() => setExportMode(false)}
|
||||
disabled={isLoadingFile}
|
||||
onClick={handleBulkExport}
|
||||
disabled={exportList.length === 0}
|
||||
/>
|
||||
<ExportExitButton onClick={() => setExportMode(false)} />
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
@ -258,12 +250,12 @@ export default function TasksPage() {
|
||||
{items.map((t) => (
|
||||
<TaskCardComponent
|
||||
exportMode={exportMode}
|
||||
isLoadingFile={exportMode ? isLoadingFile : false}
|
||||
taskForm={t}
|
||||
key={t.id + '-task-id'}
|
||||
onAddForExport={(checked) => {
|
||||
handleChangeToExportList(t.id, checked);
|
||||
}}
|
||||
onExportClick={handleSingleExport}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
|
||||
48
web/src/utils/exportForm.js
Normal file
48
web/src/utils/exportForm.js
Normal file
@ -0,0 +1,48 @@
|
||||
import { FormsSheetApi } from '../api/form_sheet';
|
||||
import { TasksApi } from '../api/tasks';
|
||||
import { downloadFile } from './downloadFile';
|
||||
import { getApiErrorMessage } from './getApiErrorMessage';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
export const exportSingleForm = async (taskId, taskTitle) => {
|
||||
try {
|
||||
const response = await TasksApi.exportTask(taskId);
|
||||
await downloadFile(response, taskTitle, 'xlsx');
|
||||
} catch (error) {
|
||||
const message = await getApiErrorMessage(
|
||||
error,
|
||||
'Ошибка при скачивании файла',
|
||||
);
|
||||
toast.error(message);
|
||||
}
|
||||
};
|
||||
|
||||
export const exportSheet = async (formId, sheetName, direction) => {
|
||||
try {
|
||||
const normalizedDirection =
|
||||
direction && direction !== 'null' ? direction : undefined;
|
||||
const params = normalizedDirection ? { direction: normalizedDirection } : {};
|
||||
const response = await FormsSheetApi.export(formId, sheetName, params);
|
||||
await downloadFile(response, sheetName, 'xlsx');
|
||||
} catch (error) {
|
||||
const message = await getApiErrorMessage(
|
||||
error,
|
||||
'Ошибка при скачивании файла',
|
||||
);
|
||||
toast.error(message);
|
||||
}
|
||||
};
|
||||
|
||||
export const exportBulkForms = async (formIds, fileName = 'Задачи') => {
|
||||
try {
|
||||
const response = await TasksApi.exportTasks({ form_ids: formIds });
|
||||
await downloadFile(response, fileName, 'xlsx');
|
||||
} catch (error) {
|
||||
const message = await getApiErrorMessage(
|
||||
error,
|
||||
'Ошибка при скачивании файла',
|
||||
);
|
||||
toast.error(message);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
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