[AURORA-1057] - добавлен экспорт таблиц
This commit is contained in:
parent
b27b8f3143
commit
e2bd55e5b1
@ -6,4 +6,10 @@ export const FormsSheetApi = {
|
|||||||
.get(`/form/${formId}/sheet/${sheetName}`, params)
|
.get(`/form/${formId}/sheet/${sheetName}`, params)
|
||||||
.then((r) => r.data);
|
.then((r) => r.data);
|
||||||
},
|
},
|
||||||
|
export(formId, sheetName, params = {}) {
|
||||||
|
return api.get(`export/form/${formId}/sheet/${sheetName}`, {
|
||||||
|
params,
|
||||||
|
responseType: 'blob',
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -251,6 +251,9 @@ const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
|
|||||||
onAddRow={handleAddRow}
|
onAddRow={handleAddRow}
|
||||||
onDeleteRow={handleDeleteRow}
|
onDeleteRow={handleDeleteRow}
|
||||||
isLoadingData={isLoadingData}
|
isLoadingData={isLoadingData}
|
||||||
|
formId={formId}
|
||||||
|
sheetName={sheetName}
|
||||||
|
direction={direction}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div style={tableWrapperStyle}>
|
<div style={tableWrapperStyle}>
|
||||||
|
|||||||
@ -17,6 +17,8 @@ import {
|
|||||||
import CollapsibleTree from './CollapsibleTree';
|
import CollapsibleTree from './CollapsibleTree';
|
||||||
import ZoomSlider from './ZoomSlider';
|
import ZoomSlider from './ZoomSlider';
|
||||||
import SearchComponent from '../../common/SearchComponent';
|
import SearchComponent from '../../common/SearchComponent';
|
||||||
|
import { ExportDefaultButton } from '../../common/Buttons/ButtonsActions';
|
||||||
|
import { exportSheet } from '../../../utils/exportForm';
|
||||||
|
|
||||||
const SettingPanel = ({
|
const SettingPanel = ({
|
||||||
selectedColumn,
|
selectedColumn,
|
||||||
@ -32,8 +34,12 @@ const SettingPanel = ({
|
|||||||
onAddRow,
|
onAddRow,
|
||||||
onDeleteRow,
|
onDeleteRow,
|
||||||
isLoadingData,
|
isLoadingData,
|
||||||
|
formId,
|
||||||
|
sheetName,
|
||||||
|
direction,
|
||||||
}) => {
|
}) => {
|
||||||
const [isPinned, setIsPinned] = useState(false);
|
const [isPinned, setIsPinned] = useState(false);
|
||||||
|
const [isExporting, setIsExporting] = useState(false);
|
||||||
const [columnTree, setColumnTree] = useState();
|
const [columnTree, setColumnTree] = useState();
|
||||||
const [selectedColumnIds, setSelectedColumnIds] = useState();
|
const [selectedColumnIds, setSelectedColumnIds] = useState();
|
||||||
const [showColumnFilters, setShowColumnFilters] = useState(false);
|
const [showColumnFilters, setShowColumnFilters] = useState(false);
|
||||||
@ -70,6 +76,13 @@ const SettingPanel = ({
|
|||||||
|
|
||||||
const handleClickDeleteFormula = () => { };
|
const handleClickDeleteFormula = () => { };
|
||||||
|
|
||||||
|
const handleExport = async () => {
|
||||||
|
if (!formId || !sheetName || isExporting) return;
|
||||||
|
setIsExporting(true);
|
||||||
|
await exportSheet(formId, sheetName, direction);
|
||||||
|
setIsExporting(false);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Panel>
|
<Panel>
|
||||||
@ -156,6 +169,14 @@ const SettingPanel = ({
|
|||||||
</GroupByObject>
|
</GroupByObject>
|
||||||
|
|
||||||
<Divider orientation="vertical" flexItem />
|
<Divider orientation="vertical" flexItem />
|
||||||
|
|
||||||
|
<GroupByObject title="Экспорт">
|
||||||
|
<ExportDefaultButton
|
||||||
|
onClick={handleExport}
|
||||||
|
disabled={!formId || !sheetName || isExporting}
|
||||||
|
text={isExporting ? 'Экспорт...' : 'Экспорт в Excel'}
|
||||||
|
/>
|
||||||
|
</GroupByObject>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Panel>
|
</Panel>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -115,16 +115,11 @@ export const DefaultOutlinedButton = styled(Button)(({ width, height }) => ({
|
|||||||
color: '#323031',
|
color: '#323031',
|
||||||
|
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
background: '#4a5565',
|
background: '#f3f3f5',
|
||||||
color: 'white',
|
borderColor: 'rgba(0, 0, 0, 0.2)',
|
||||||
'& svg path': {
|
|
||||||
color: 'white !important',
|
|
||||||
fill: 'white !important',
|
|
||||||
stroke: 'white !important',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
'&:active': {
|
'&:active': {
|
||||||
background: '#f3f3f5',
|
background: '#e7e7ea',
|
||||||
},
|
},
|
||||||
|
|
||||||
'&.Mui-disabled': {
|
'&.Mui-disabled': {
|
||||||
|
|||||||
@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -20,7 +20,7 @@ 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 { ExportWithTextButton } from '../../components/common/Buttons/ButtonsActions';
|
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 TablesList from '../../components/common/TableList/TableList';
|
||||||
import { SHEET_NAME } from '../../constants';
|
import { SHEET_NAME } from '../../constants';
|
||||||
|
|
||||||
@ -55,7 +55,6 @@ 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([]);
|
||||||
|
|
||||||
@ -148,7 +147,9 @@ 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={() => setExportModalOpen(true)} />
|
<ExportWithTextButton
|
||||||
|
onClick={() => exportSingleForm(taskId, task?.title ?? 'form')}
|
||||||
|
/>
|
||||||
<PrimaryOutlinedButton
|
<PrimaryOutlinedButton
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onClick={() => setModalStageOpen(true)}
|
onClick={() => setModalStageOpen(true)}
|
||||||
@ -173,12 +174,6 @@ export default function TaskPage() {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<ExportFormModal
|
|
||||||
open={exportModalOpen}
|
|
||||||
onClose={() => setExportModalOpen(false)}
|
|
||||||
taskId={taskId}
|
|
||||||
taskTitle={task?.title ?? 'form'}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -31,7 +31,7 @@ import {
|
|||||||
ExportExitButton,
|
ExportExitButton,
|
||||||
ExportWithTextButton,
|
ExportWithTextButton,
|
||||||
} from '../components/common/Buttons/ButtonsActions';
|
} from '../components/common/Buttons/ButtonsActions';
|
||||||
import { ExportFormModal } from '../components/common/ExportFormModal';
|
import { exportBulkForms, exportSingleForm } from '../utils/exportForm';
|
||||||
import { WhitePlus } from '../components/common/icons/icons';
|
import { WhitePlus } from '../components/common/icons/icons';
|
||||||
|
|
||||||
export default function TasksPage() {
|
export default function TasksPage() {
|
||||||
@ -60,13 +60,6 @@ export default function TasksPage() {
|
|||||||
|
|
||||||
const [exportMode, setExportMode] = useState(false);
|
const [exportMode, setExportMode] = useState(false);
|
||||||
const [exportList, setExportList] = useState([]);
|
const [exportList, setExportList] = useState([]);
|
||||||
const [exportModal, setExportModal] = useState({
|
|
||||||
open: false,
|
|
||||||
taskId: null,
|
|
||||||
taskTitle: null,
|
|
||||||
formIds: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
const openModal = () => {
|
const openModal = () => {
|
||||||
setModalOpen(true);
|
setModalOpen(true);
|
||||||
};
|
};
|
||||||
@ -190,31 +183,13 @@ export default function TasksPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const openBulkExportModal = () => {
|
const handleBulkExport = async () => {
|
||||||
setExportModal({
|
await exportBulkForms(exportList);
|
||||||
open: true,
|
setExportMode(false);
|
||||||
taskId: null,
|
|
||||||
taskTitle: null,
|
|
||||||
formIds: exportList,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const openSingleExportModal = (taskId, taskTitle) => {
|
const handleSingleExport = (taskId, taskTitle) => {
|
||||||
setExportModal({
|
exportSingleForm(taskId, taskTitle);
|
||||||
open: true,
|
|
||||||
taskId,
|
|
||||||
taskTitle,
|
|
||||||
formIds: null,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const closeExportModal = () => {
|
|
||||||
setExportModal({
|
|
||||||
open: false,
|
|
||||||
taskId: null,
|
|
||||||
taskTitle: null,
|
|
||||||
formIds: null,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Calculate total pages
|
// Calculate total pages
|
||||||
@ -256,7 +231,7 @@ export default function TasksPage() {
|
|||||||
<>
|
<>
|
||||||
<ExportWithTextButton
|
<ExportWithTextButton
|
||||||
text={'Экспортировать'}
|
text={'Экспортировать'}
|
||||||
onClick={openBulkExportModal}
|
onClick={handleBulkExport}
|
||||||
disabled={exportList.length === 0}
|
disabled={exportList.length === 0}
|
||||||
/>
|
/>
|
||||||
<ExportExitButton onClick={() => setExportMode(false)} />
|
<ExportExitButton onClick={() => setExportMode(false)} />
|
||||||
@ -280,7 +255,7 @@ export default function TasksPage() {
|
|||||||
onAddForExport={(checked) => {
|
onAddForExport={(checked) => {
|
||||||
handleChangeToExportList(t.id, checked);
|
handleChangeToExportList(t.id, checked);
|
||||||
}}
|
}}
|
||||||
onExportClick={openSingleExportModal}
|
onExportClick={handleSingleExport}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
@ -461,19 +436,6 @@ 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}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user