[AURORA-1057] - добавлен экспорт таблиц

This commit is contained in:
Veronika Tsygankova 2026-06-05 13:04:47 +03:00
parent b27b8f3143
commit e2bd55e5b1
8 changed files with 94 additions and 214 deletions

View File

@ -3,7 +3,13 @@ import api from './client';
export const FormsSheetApi = {
get(formId, sheetName, params) {
return api
.get(`/form/${formId}/sheet/${sheetName}`,params)
.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',
});
},
};

View File

@ -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}>

View File

@ -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>
</>

View File

@ -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': {

View File

@ -1,150 +0,0 @@
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>
);
};

View File

@ -20,7 +20,7 @@ import { PrimaryOutlinedButton } from '../../components/common/Buttons/Buttons';
import { FormsApi } from '../../api/forms';
import { ModalCreateProject } from './ModalCreateProject';
import { ExportWithTextButton } from '../../components/common/Buttons/ButtonsActions';
import { ExportFormModal } from '../../components/common/ExportFormModal';
import { exportSingleForm } from '../../utils/exportForm';
import TablesList from '../../components/common/TableList/TableList';
import { SHEET_NAME } from '../../constants';
@ -55,7 +55,6 @@ export default function TaskPage() {
const [query, setQuery] = useState('');
const [modalStageOpen, setModalStageOpen] = useState(false);
const [modalProjectOpen, setModalProjectOpen] = useState(false);
const [exportModalOpen, setExportModalOpen] = useState(false);
const [tempProjects, setTempProjects] = useState([]);
const [projects, setProjects] = useState([]);
@ -148,7 +147,9 @@ export default function TaskPage() {
onChange={setQuery}
/>
<Stack sx={{ flexDirection: 'row', spacing: '.5rem', justifyContent: 'end' }}>
<ExportWithTextButton onClick={() => setExportModalOpen(true)} />
<ExportWithTextButton
onClick={() => exportSingleForm(taskId, task?.title ?? 'form')}
/>
<PrimaryOutlinedButton
variant="outlined"
onClick={() => setModalStageOpen(true)}
@ -173,12 +174,6 @@ export default function TaskPage() {
/>
</Box>
</Box>
<ExportFormModal
open={exportModalOpen}
onClose={() => setExportModalOpen(false)}
taskId={taskId}
taskTitle={task?.title ?? 'form'}
/>
</>
);
}

View File

@ -31,7 +31,7 @@ import {
ExportExitButton,
ExportWithTextButton,
} from '../components/common/Buttons/ButtonsActions';
import { ExportFormModal } from '../components/common/ExportFormModal';
import { exportBulkForms, exportSingleForm } from '../utils/exportForm';
import { WhitePlus } from '../components/common/icons/icons';
export default function TasksPage() {
@ -60,13 +60,6 @@ export default function TasksPage() {
const [exportMode, setExportMode] = useState(false);
const [exportList, setExportList] = useState([]);
const [exportModal, setExportModal] = useState({
open: false,
taskId: null,
taskTitle: null,
formIds: null,
});
const openModal = () => {
setModalOpen(true);
};
@ -190,31 +183,13 @@ export default function TasksPage() {
}
};
const openBulkExportModal = () => {
setExportModal({
open: true,
taskId: null,
taskTitle: null,
formIds: exportList,
});
const handleBulkExport = async () => {
await exportBulkForms(exportList);
setExportMode(false);
};
const openSingleExportModal = (taskId, taskTitle) => {
setExportModal({
open: true,
taskId,
taskTitle,
formIds: null,
});
};
const closeExportModal = () => {
setExportModal({
open: false,
taskId: null,
taskTitle: null,
formIds: null,
});
const handleSingleExport = (taskId, taskTitle) => {
exportSingleForm(taskId, taskTitle);
};
// Calculate total pages
@ -256,7 +231,7 @@ export default function TasksPage() {
<>
<ExportWithTextButton
text={'Экспортировать'}
onClick={openBulkExportModal}
onClick={handleBulkExport}
disabled={exportList.length === 0}
/>
<ExportExitButton onClick={() => setExportMode(false)} />
@ -280,7 +255,7 @@ export default function TasksPage() {
onAddForExport={(checked) => {
handleChangeToExportList(t.id, checked);
}}
onExportClick={openSingleExportModal}
onExportClick={handleSingleExport}
/>
))}
</>
@ -461,19 +436,6 @@ export default function TasksPage() {
</FormControl>
</div>
</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}
/>
</>
);
}

View 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;
}
};