271 lines
7.9 KiB
JavaScript
271 lines
7.9 KiB
JavaScript
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||
import { FormsApi } from '../api/forms';
|
||
import { CardsSkeleton } from '../components/common/Skeleton';
|
||
import { SwitchFormTask } from '../components/common/SwitchFormTask/SwitchFormTask';
|
||
import Modal from '../components/common/Modal/Modal';
|
||
import { PageContainer, TasksContainer } from './StyledForPage';
|
||
import { TaskCardComponent } from '../components/common/TaskCardComponent';
|
||
import { WhitePlus } from '../components/common/icons/icons';
|
||
import {
|
||
Autocomplete,
|
||
Box,
|
||
Button,
|
||
Divider,
|
||
FormControl,
|
||
FormLabel,
|
||
TextField,
|
||
} from '@mui/material';
|
||
import SearchComponent from '../components/common/SearchComponent';
|
||
import { CustomCheckbox } from '../components/common/StyledCheckbox';
|
||
import { HeaderSwitch } from '../components/HeaderMenu/HeaderSwitch';
|
||
import { toast } from 'react-toastify';
|
||
import { PrimaryButton } from '../components/common/Buttons/Buttons';
|
||
|
||
export default function FormsPage() {
|
||
const [items, setItems] = useState([]);
|
||
const [loading, setLoading] = useState(true);
|
||
const [loadingUpdate, setLoadingUpdate] = useState(false);
|
||
const [openModalCreate, setOpenModalCreate] = useState(false);
|
||
const [query, setQuery] = useState('');
|
||
const baseFormState = useMemo(
|
||
() => ({
|
||
nameForm: '',
|
||
basis: false,
|
||
basisForSelect: [],
|
||
needBasis: false,
|
||
}),
|
||
[],
|
||
);
|
||
|
||
const [newFormState, setNewFormState] = useState(baseFormState);
|
||
|
||
const resetFormState = useCallback(() => {
|
||
setNewFormState((prev) => ({
|
||
...baseFormState,
|
||
basisForSelect: prev.basisForSelect,
|
||
}));
|
||
}, [baseFormState]);
|
||
|
||
const getFormsList = async () => {
|
||
try {
|
||
const data = await FormsApi.formsList();
|
||
setItems(data);
|
||
} catch (error) {
|
||
toast.error('Ошибка загрузки форм');
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
useEffect(() => {
|
||
getFormsList();
|
||
}, []);
|
||
|
||
useEffect(() => {
|
||
const basisForSelect = items.map((item) => ({
|
||
id: item.id,
|
||
name: item.title || '',
|
||
}));
|
||
|
||
setNewFormState((prev) => ({
|
||
...prev,
|
||
basisForSelect,
|
||
}));
|
||
}, [items]);
|
||
|
||
const handleOpenCreateModal = () => {
|
||
setOpenModalCreate(true);
|
||
};
|
||
|
||
const handleCloseCreateModal = () => {
|
||
setOpenModalCreate(false);
|
||
resetFormState();
|
||
};
|
||
|
||
const createForm = async () => {
|
||
try {
|
||
let data;
|
||
setLoadingUpdate(true);
|
||
|
||
if (newFormState.needBasis) {
|
||
data = await FormsApi.createFormFromOther({
|
||
title: newFormState.nameForm,
|
||
form_id: newFormState.basis,
|
||
});
|
||
} else {
|
||
data = await FormsApi.createForm({
|
||
title: newFormState.nameForm,
|
||
});
|
||
}
|
||
|
||
setItems((prev) => [...prev, data.result]);
|
||
setOpenModalCreate(false);
|
||
resetFormState();
|
||
} catch (error) {
|
||
toast.error(error.response?.data?.detail || 'Не удалось создать форму');
|
||
} finally {
|
||
setLoadingUpdate(false);
|
||
}
|
||
};
|
||
|
||
// Фильтруем формы по query
|
||
const filteredItems = items.filter((item) =>
|
||
item.title?.toLowerCase().includes(query.toLowerCase()),
|
||
);
|
||
|
||
return (
|
||
<PageContainer>
|
||
<HeaderSwitch></HeaderSwitch>
|
||
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||
<SwitchFormTask />
|
||
<PrimaryButton
|
||
onClick={handleOpenCreateModal}
|
||
startIcon={<WhitePlus />}
|
||
>
|
||
Создать форму
|
||
</PrimaryButton>
|
||
</Box>
|
||
|
||
<SearchComponent
|
||
placeholder="Поиск по названию формы"
|
||
value={query}
|
||
onChange={setQuery}
|
||
/>
|
||
|
||
<TasksContainer>
|
||
{loading ? (
|
||
<CardsSkeleton count={8} />
|
||
) : (
|
||
<>
|
||
{filteredItems.length === 0 && (
|
||
<div className="text-sm text-gray-500">
|
||
{query
|
||
? 'Ничего не найдено'
|
||
: 'Нет форм. Создайте новую, чтобы начать.'}
|
||
</div>
|
||
)}
|
||
{filteredItems.map((t) => (
|
||
<TaskCardComponent
|
||
key={t.id + '-form-id'}
|
||
taskForm={t}
|
||
isForm={true}
|
||
/>
|
||
))}
|
||
</>
|
||
)}
|
||
</TasksContainer>
|
||
|
||
<Modal
|
||
open={openModalCreate}
|
||
onClose={handleCloseCreateModal}
|
||
title="Создать форму"
|
||
actions={
|
||
<>
|
||
<Button
|
||
variant="grey"
|
||
onClick={handleCloseCreateModal}
|
||
style={{ height: '2.5rem' }}
|
||
disabled={loadingUpdate}
|
||
>
|
||
Отменить
|
||
</Button>
|
||
<Button
|
||
variant="contained"
|
||
style={{ height: '2.5rem' }}
|
||
onClick={createForm}
|
||
loading={loadingUpdate}
|
||
>
|
||
Создать
|
||
</Button>
|
||
</>
|
||
}
|
||
>
|
||
<div className="flex flex-col gap-4 min-h-[16.5rem]">
|
||
<FormControl fullWidth>
|
||
<FormLabel required>Название формы</FormLabel>
|
||
<TextField
|
||
size="small"
|
||
placeholder="Укажите название формы"
|
||
onChange={(e) =>
|
||
setNewFormState((prev) => ({
|
||
...prev,
|
||
nameForm: e.target.value,
|
||
}))
|
||
}
|
||
/>
|
||
</FormControl>
|
||
|
||
<Divider style={{ marginTop: '0.5rem', marginBottom: '0.5rem' }} />
|
||
|
||
<div className="flex items-center gap-3">
|
||
<CustomCheckbox
|
||
checked={newFormState.needBasis}
|
||
onChange={(e) => {
|
||
setNewFormState((prev) => ({
|
||
...prev,
|
||
needBasis: e.target.checked ? true : false,
|
||
}));
|
||
}}
|
||
/>
|
||
<div className="leading-tight">
|
||
<div
|
||
className="font-medium"
|
||
style={{ fontSize: '1rem', color: '#1F1F1F' }}
|
||
>
|
||
Создать на основе существующей формы
|
||
</div>
|
||
<div
|
||
className="text-gray-500 mt-1"
|
||
style={{ fontSize: '0.9rem' }}
|
||
>
|
||
Вы можете дублировать и редактировать существующую форму
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{newFormState.needBasis && (
|
||
<FormControl fullWidth>
|
||
<FormLabel required>Форма</FormLabel>
|
||
<Autocomplete
|
||
size="small"
|
||
options={newFormState.basisForSelect}
|
||
noOptionsText="Не найдено"
|
||
getOptionLabel={(option) => option.name || ''}
|
||
value={
|
||
newFormState.basisForSelect.find(
|
||
(item) => item.id === newFormState.basis,
|
||
) || null
|
||
}
|
||
onChange={(event, newValue) => {
|
||
setNewFormState((prev) => ({
|
||
...prev,
|
||
basis: newValue ? newValue.id : false,
|
||
}));
|
||
}}
|
||
slotProps={{
|
||
popper: {
|
||
style: { zIndex: 60 },
|
||
},
|
||
}}
|
||
renderInput={(params) => (
|
||
<TextField
|
||
{...params}
|
||
placeholder="Не выбрана"
|
||
sx={{
|
||
'& .MuiOutlinedInput-root': {
|
||
'& fieldset': {
|
||
borderColor: 'rgba(0,0,0,0.1)',
|
||
},
|
||
},
|
||
}}
|
||
/>
|
||
)}
|
||
/>
|
||
</FormControl>
|
||
)}
|
||
</div>
|
||
</Modal>
|
||
</PageContainer>
|
||
);
|
||
}
|