378 lines
11 KiB
JavaScript
378 lines
11 KiB
JavaScript
import { useEffect, useState } from 'react';
|
||
import { useParams, useNavigate } from 'react-router-dom';
|
||
import { createPortal } from 'react-dom';
|
||
import { TablesApi } from '../api/tables';
|
||
import {
|
||
TaskInfoContainer,
|
||
NameTask,
|
||
} from '../components/common/SwitchFormTask/SwitchFormTask.style';
|
||
import {
|
||
Box,
|
||
Button,
|
||
CircularProgress,
|
||
FormControl,
|
||
FormLabel,
|
||
TextField,
|
||
Typography,
|
||
} from '@mui/material';
|
||
import { Chip } from '../components/styles/StyledChip';
|
||
import { IconWithContent } from '../components/common/IconWithContent';
|
||
import { TableIcon } from '../components/common/icons/icons';
|
||
import Modal from '../components/common/Modal/Modal';
|
||
import { toast } from 'react-toastify';
|
||
import { FormsApi } from '../api/forms';
|
||
import SearchComponent from '../components/common/SearchComponent';
|
||
import { SettingModal as SettingStageModal } from '../components/Stages/SettingModal';
|
||
import { BackButton } from '../components/common/Buttons/BackButton';
|
||
import { CustomCheckbox } from '../components/common/StyledCheckbox';
|
||
import TableList from '../components/common/TableList/TableList';
|
||
|
||
const FormInfo = ({ form }) => {
|
||
const portalContent = (
|
||
<TaskInfoContainer>
|
||
<BackButton to="/forms" />
|
||
<NameTask>{form.title}</NameTask>
|
||
</TaskInfoContainer>
|
||
);
|
||
|
||
const targetElement = document.getElementById('TableInfo');
|
||
if (targetElement) {
|
||
return createPortal(portalContent, targetElement);
|
||
}
|
||
};
|
||
|
||
export default function FormPage() {
|
||
const { formId } = useParams();
|
||
const id = Number(formId);
|
||
const [form, setForm] = useState(null);
|
||
const [tables, setTables] = useState([]);
|
||
const [newTableName, setNewTableName] = useState('');
|
||
const [isLoading, setIsLoading] = useState(false);
|
||
const [query, setQuery] = useState('');
|
||
const [modalOpen, setModalOpen] = useState(false);
|
||
const [newTableIsProject, setNewTableIsProject] = useState(false);
|
||
const [modalStageOpen, setModalStageOpen] = useState(false);
|
||
const [projects, setProjects] = useState([]);
|
||
|
||
useEffect(() => {
|
||
if (!formId) return;
|
||
|
||
const fetchProjects = async () => {
|
||
try {
|
||
const { success, result } = await FormsApi.getProjects(formId);
|
||
success ? setProjects(result) : toast.error('Ошибка получения проекта');
|
||
} catch {
|
||
toast.error('Ошибка получения проекта');
|
||
}
|
||
};
|
||
|
||
fetchProjects();
|
||
}, [formId]);
|
||
|
||
const handleClickCreate = () => {
|
||
setModalOpen(true);
|
||
};
|
||
|
||
const handleCancel = () => {
|
||
setModalOpen(false);
|
||
setNewTableName('');
|
||
};
|
||
|
||
const handleCreateTable = async () => {
|
||
const trimmedName = newTableName.trim();
|
||
|
||
if (!trimmedName) {
|
||
toast.error('Название не может быть пустым');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
let result;
|
||
|
||
if (newTableIsProject) {
|
||
result = await createProjectTable(trimmedName);
|
||
} else {
|
||
result = await TablesApi.createTemp({
|
||
name: trimmedName,
|
||
form_id: id,
|
||
});
|
||
}
|
||
|
||
setTables((prevTables) => [...prevTables, result.result]);
|
||
|
||
setNewTableName('');
|
||
setModalOpen(false);
|
||
} catch (error) {
|
||
const errorMessage =
|
||
error.response?.data?.detail ||
|
||
error.message ||
|
||
'Не удалось создать таблицу';
|
||
|
||
toast.error(errorMessage);
|
||
}
|
||
};
|
||
|
||
const createProjectTable = async (tableName) => {
|
||
let project = projects[0] || null;
|
||
|
||
if (!project) {
|
||
const result = await createDefaultProject();
|
||
if (result && result.success) {
|
||
setProjects([result.result]);
|
||
project = result.result;
|
||
} else {
|
||
throw new Error('Не удалось создать проект');
|
||
}
|
||
}
|
||
|
||
return await FormsApi.createProjectTable(formId, project.id, {
|
||
name: tableName,
|
||
});
|
||
};
|
||
|
||
const createDefaultProject = async () => {
|
||
const defaultProjectData = {
|
||
name: 'Проектные таблицы',
|
||
// ToDo: надо уточнить что сюда вообще пишется
|
||
add_projects_data: {
|
||
'Тип проекта развития': {
|
||
type: 'multiselect',
|
||
options: [
|
||
'Открытие ВСП',
|
||
'Закрытие ВСП',
|
||
'Переезд ВСП',
|
||
'Реновация РФ',
|
||
'Открытие УРМ',
|
||
],
|
||
placeholder: '',
|
||
defaultValue: null,
|
||
},
|
||
'Название проекта': {
|
||
type: 'text',
|
||
placeholder: 'Введите название проекта',
|
||
defaultValue: '',
|
||
},
|
||
'Формат ВСП': {
|
||
type: 'multiselect',
|
||
options: [
|
||
'Фланговый',
|
||
'Типовой',
|
||
'Розничный',
|
||
'МСБ',
|
||
'Лёгкий',
|
||
'Мини',
|
||
'Розничный-киоск',
|
||
'МБО',
|
||
'Офис самообслуживания',
|
||
'Другое',
|
||
],
|
||
placeholder: '',
|
||
defaultValue: null,
|
||
},
|
||
'Адрес объекта': {
|
||
type: 'text',
|
||
placeholder: 'Введите адрес объекта',
|
||
defaultValue: '',
|
||
},
|
||
'Целевая численность, чел.': {
|
||
type: 'number',
|
||
placeholder: 'Введите число',
|
||
defaultValue: 0,
|
||
min: 0,
|
||
// max: 1000000000,
|
||
step: 1,
|
||
},
|
||
'Тип размещения': {
|
||
type: 'multiselect',
|
||
options: ['Собственность', 'Аренда', 'Субаренда'],
|
||
placeholder: '',
|
||
defaultValue: null,
|
||
},
|
||
'Площадь размещения, м2': {
|
||
type: 'number',
|
||
placeholder: 'Введите число',
|
||
defaultValue: 0,
|
||
min: 0,
|
||
// max: 1000000000,
|
||
step: 1,
|
||
},
|
||
},
|
||
};
|
||
|
||
return await FormsApi.createProjects(formId, defaultProjectData);
|
||
};
|
||
|
||
const loadFormTemplates = async () => {
|
||
setIsLoading(true);
|
||
try {
|
||
const data = await FormsApi.formTablesList(id, true);
|
||
setTables(data.result);
|
||
} catch (error) {
|
||
toast.error(error?.response?.data?.detail || 'Ошибка загрузки таблиц');
|
||
} finally {
|
||
setIsLoading(false);
|
||
}
|
||
};
|
||
|
||
const getFormInfo = async () => {
|
||
setIsLoading(true);
|
||
try {
|
||
const data = await FormsApi.getForm(id);
|
||
setForm(data.result);
|
||
} catch (error) {
|
||
toast.error(error?.response?.data?.detail || 'Ошибка загрузки таблиц');
|
||
} finally {
|
||
setIsLoading(false);
|
||
}
|
||
};
|
||
|
||
useEffect(() => {
|
||
if (Number.isFinite(id)) {
|
||
getFormInfo();
|
||
}
|
||
}, [id]);
|
||
|
||
useEffect(() => {
|
||
if (form) {
|
||
loadFormTemplates();
|
||
}
|
||
}, [form]);
|
||
|
||
// Фильтруем таблицы по query
|
||
const filteredTables = tables.filter((item) =>
|
||
item.name?.toLowerCase().includes(query.toLowerCase()),
|
||
);
|
||
|
||
if (isLoading) {
|
||
return (
|
||
<div className="flex justify-center pt-12">
|
||
<CircularProgress />
|
||
</div>
|
||
);
|
||
}
|
||
|
||
if (!form) {
|
||
return (
|
||
<Box
|
||
sx={{
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'center',
|
||
paddingTop: '2.5rem'
|
||
}}
|
||
>
|
||
Эта форма не существует
|
||
</Box>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<>
|
||
{form && <FormInfo form={form} />}
|
||
<Box
|
||
sx={{
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'center',
|
||
paddingTop: '2.5rem'
|
||
}}
|
||
>
|
||
<Box sx={{ display: 'flex', gap: '1rem', flexDirection: 'column', width: '50rem' }}>
|
||
<Box sx={{ display: 'flex', gap: '1rem' }}>
|
||
<span>Таблицы</span>
|
||
<Chip>
|
||
<IconWithContent icon={<TableIcon />}>
|
||
<span style={{ fontSize: '0.8rem' }}>{tables.length} шт.</span>
|
||
</IconWithContent>
|
||
</Chip>
|
||
</Box>
|
||
|
||
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||
<Box sx={{ display: 'flex', gap: '1rem' }}>
|
||
<SearchComponent
|
||
placeholder="Поиск по названию таблицы"
|
||
value={query}
|
||
onChange={setQuery}
|
||
/>
|
||
<Button variant="contained" onClick={handleClickCreate}>
|
||
Создать
|
||
</Button>
|
||
</Box>
|
||
<Box>
|
||
<Button
|
||
variant="outlined"
|
||
onClick={() => setModalStageOpen(true)}
|
||
>
|
||
<span>Этапы</span>
|
||
</Button>
|
||
<SettingStageModal
|
||
isOpen={modalStageOpen}
|
||
onClose={() => {
|
||
setModalStageOpen(false);
|
||
}}
|
||
formId={formId}
|
||
/>
|
||
</Box>
|
||
</Box>
|
||
<TableList
|
||
filteredTables={filteredTables}
|
||
basePath="/table-temp"
|
||
query={query}
|
||
projects={projects}
|
||
/>
|
||
</Box>
|
||
</Box>
|
||
<Modal
|
||
open={modalOpen}
|
||
onClose={handleCancel}
|
||
title="Создать таблицу"
|
||
actions={
|
||
<>
|
||
<Button
|
||
variant="grey"
|
||
onClick={handleCancel}
|
||
sx={{ height: '2.5rem' }}
|
||
>
|
||
Отменить
|
||
</Button>
|
||
<Button
|
||
variant="contained"
|
||
onClick={handleCreateTable}
|
||
sx={{ height: '2.5rem' }}
|
||
>
|
||
Создать
|
||
</Button>
|
||
</>
|
||
}
|
||
>
|
||
<FormControl fullWidth>
|
||
<FormLabel required>Название таблицы</FormLabel>
|
||
<TextField
|
||
size="small"
|
||
placeholder="Укажите название таблицы"
|
||
value={newTableName}
|
||
onChange={(e) => setNewTableName(e.target.value)}
|
||
/>
|
||
<Box
|
||
sx={{
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: '0.75rem',
|
||
marginTop: '1rem'
|
||
}}
|
||
>
|
||
<CustomCheckbox
|
||
checked={newTableIsProject}
|
||
onChange={(e) => {
|
||
setNewTableIsProject(e.target.checked ? true : false);
|
||
}}
|
||
/>
|
||
<Box sx={{ fontSize: '1rem', color: '#1F1F1F' }}>
|
||
Проектная таблица
|
||
</Box>
|
||
</Box>
|
||
</FormControl>
|
||
</Modal>
|
||
</>
|
||
);
|
||
}
|