2026-08-06 17:44:49 +03:00

307 lines
8.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import styled from '@emotion/styled';
import { Box, Chip, Stack } from '@mui/material';
// src/components/common/ProjectCardComponent/ProjectCardComponent.jsx
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { toast } from 'react-toastify';
import { ProjectsApi } from '../../api/projects';
import { IconWithContent } from '../../components/common/IconWithContent';
import { CheckBoxCheckSvg, CheckBoxUncheckSvg, DateIcon, UserIcon } from '../../components/common/icons/icons';
import { ModalCreateProject } from './ModalCreateProject';
const ProjectCard = styled.div`
width: 26.8rem;
height: 15rem;
display: flex;
padding: 1.25rem;
flex-direction: column;
justify-content: space-between;
align-items: flex-start;
box-sizing: border-box;
border: 0.06rem solid var(--Stroke, rgba(0, 0, 0, 0.1));
border-radius: 1rem;
box-shadow:
0rem 2rem 4rem -2rem rgba(0, 0, 0, 0.02),
0rem 4rem 6rem -1rem rgba(0, 0, 0, 0.02);
background: var(--White, rgba(255, 255, 255, 1));
overflow: hidden;
cursor: pointer;
position: relative;
&:hover {
transform: translateY(-5px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.12);
}
`;
const CardHeader = styled.div`
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
`;
const ProjectId = styled.div`
font-size: 0.75rem;
font-weight: 400;
line-height: 1rem;
letter-spacing: 0rem;
text-align: left;
color: rgba(0, 0, 0, 0.6);
`;
const CardBody = styled.div`
width: 100%;
flex: 1;
display: flex;
flex-direction: column;
gap: 0.8125rem;
justify-content: flex-end;
height: 100%;
`;
const ProjectTitle = styled.div`
font-size: 1rem;
font-weight: 500;
color: var(--Text-Primary, #333);
line-height: 1.63;
margin: 0;
padding: 0;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
`;
const StatusChip = styled(Chip)`
font-size: 0.7rem;
height: 1.5rem;
background-color: ${({ status }) => {
switch (status) {
case 'active':
return '#4caf50';
case 'completed':
return '#2196f3';
case 'paused':
return '#ff9800';
case 'archived':
return '#9e9e9e';
default:
return '#9e9e9e';
}
}};
color: white;
`;
const LevelChip = styled(Chip)`
font-size: 0.6rem;
height: 1.25rem;
background-color: rgba(0, 0, 0, 0.08);
color: rgba(0, 0, 0, 0.7);
`;
const ProjectInfoGrid = styled.div`
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-top: 0.25rem;
`;
const InfoChip = styled(Chip)`
font-size: 0.6rem;
height: 1.25rem;
background-color: rgba(0, 0, 0, 0.05);
color: rgba(0, 0, 0, 0.7);
`;
const getStatusLabel = (status) => {
const statusMap = {
active: 'Активный',
completed: 'Завершен',
paused: 'Приостановлен',
archived: 'Архивирован',
};
return statusMap[status] || status || 'Не указан';
};
export const ProjectCardComponent = ({
project,
projectDataInit,
exportMode = false,
isLoadingFile = false,
onAddForExport,
onExportClick,
}) => {
const navigate = useNavigate();
const {
id,
name,
org_unit_name,
years,
status,
level,
project_type,
vsp_format,
placement_type,
object_address,
staff_count,
total_area,
report_count,
parent_id,
} = project;
const [isEditModalOpen, setEditModalOpen] = useState(false);
const [currentName, setCurrentName] = useState(name || '');
const [isChecked, setIsChecked] = useState(false);
const [editFormData, setEditFormData] = useState({});
const [projectData, setProjectData] = useState(projectDataInit);
useEffect(() => {
setIsChecked(false);
}, [exportMode]);
// Функция для подготовки данных проекта к редактированию
const prepareProjectDataForEdit = () => {
return {
'ССП/РФ': project.org_unit_id || null,
Год: years || null,
'Тип проекта развития': project_type || null,
'Название проекта': currentName,
'Формат ВСП': vsp_format || null,
'Адрес объекта': object_address || '',
'Целевая численность, чел.': staff_count || 0,
'Тип размещения': placement_type || null,
'Площадь размещения, м2': total_area || 0,
};
};
const handleOpenEdit = (event) => {
event.stopPropagation();
setEditFormData(prepareProjectDataForEdit());
setEditModalOpen(true);
};
const handleCloseEdit = () => {
setEditModalOpen(false);
setEditFormData({});
};
const handleCheckboxClick = (event) => {
event.stopPropagation();
if (!isLoadingFile) {
setIsChecked(!isChecked);
onAddForExport(!isChecked);
} else {
toast.warning('Файл формируется!');
}
};
const handleExportProject = (event) => {
event.stopPropagation();
onExportClick?.(id, name);
};
const handleNavigate = () => {
if (!exportMode) {
navigate(`/project/${id}`);
}
};
// Обновленная функция обновления проекта
const updateProject = async (formData) => {
try {
const projectData = {
name: formData['Название проекта'] || currentName,
project_type: formData['Тип проекта развития'] || null,
vsp_format: formData['Формат ВСП'] || null,
object_address: formData['Адрес объекта'] || '',
staff_count: Number(formData['Целевая численность, чел.']) || 0,
placement_type: formData['Тип размещения'] || null,
total_area: Number(formData['Площадь размещения, м2']) || 0,
year: formData['Год'] ? Number(formData['Год']) : null,
branch_id: formData['ССП/РФ'] || null,
};
await ProjectsApi.update(id, projectData);
// Обновляем локальные данные
setCurrentName(projectData.name);
setEditModalOpen(false);
toast.success('Данные проекта успешно обновлены');
// Можно добавить рефреш данных или перезагрузку страницы
window.location.reload(); // или используйте callback для обновления списка
} catch (error) {
toast.error(error?.response?.data?.detail || 'Не удалось обновить данные проекта');
}
};
const handleSaveProject = async (formData) => {
await updateProject(formData);
};
const getInitials = (name) => {
if (!name) return '?';
const words = name.split(' ');
if (words.length >= 2) {
return `${words[0][0]}${words[1][0]}`.toUpperCase();
}
return name.substring(0, 2).toUpperCase();
};
return (
<>
<ProjectCard onClick={handleNavigate} key={id}>
<Box sx={{ width: '100%' }}>
<CardHeader>
<ProjectId>ID: {id}</ProjectId>
<Stack direction={'row'} spacing={'.5rem'} sx={{}}>
{!exportMode && (
<>
{status && <StatusChip status={status} label={getStatusLabel(status)} size='small' />}
{/* {Пока нет редактирование + экспорт} */}
{/* <ExportButton onClick={handleExportProject} />
<Edit onClick={handleOpenEdit} /> */}
</>
)}
</Stack>
</CardHeader>
<ProjectTitle>{currentName}</ProjectTitle>
<ProjectInfoGrid>
{project_type && <InfoChip label={`Тип: ${project_type}`} size='small' />}
{report_count !== undefined && report_count !== null && <InfoChip label={`Отчетов: ${report_count}`} size='small' />}
</ProjectInfoGrid>
</Box>
<Stack
sx={{
direction: 'row',
justifyContent: 'space-between',
width: '100%',
alignItems: 'end',
}}>
<CardBody>
<IconWithContent icon={<DateIcon />}>{years.join(', ') || 'Год не указан'}</IconWithContent>
<IconWithContent icon={<UserIcon />}>{org_unit_name || 'Организация не указана'}</IconWithContent>
</CardBody>
{exportMode && <div>{isChecked ? <CheckBoxCheckSvg size='1.625rem' /> : <CheckBoxUncheckSvg size='1.625rem' />}</div>}
</Stack>
</ProjectCard>
<ModalCreateProject
open={isEditModalOpen}
onClose={handleCloseEdit}
onCreate={handleSaveProject}
config={projectData}
initialData={editFormData}
isEdit={true}
projectId={id}
/>
</>
);
};