Files
worklenz/worklenz-frontend/src/components/schedule/grant-chart/project-timeline-bar.tsx
chamikaJ 8825b0410a init
2025-04-17 18:28:54 +05:30

167 lines
5.5 KiB
TypeScript

import React, { useState } from 'react';
import { Flex, Popover, Typography } from 'antd';
import { useAppDispatch } from '@/hooks/useAppDispatch';
import { useTranslation } from 'react-i18next';
import { useAppSelector } from '@/hooks/useAppSelector';
import { getWorking, toggleScheduleDrawer } from '../../../features/schedule/scheduleSlice';
import ProjectTimelineModal from '../../../features/schedule/ProjectTimelineModal';
import { Resizable } from 're-resizable';
import { themeWiseColor } from '../../../utils/themeWiseColor';
import { MoreOutlined } from '@ant-design/icons';
import { CELL_WIDTH } from '../../../shared/constants';
import { ScheduleData } from '@/types/schedule/schedule-v2.types';
type ProjectTimelineBarProps = {
project: any;
indicatorOffset: number;
indicatorWidth: number;
defaultData?: ScheduleData;
};
const ProjectTimelineBar = ({
project,
indicatorOffset,
indicatorWidth,
defaultData,
}: ProjectTimelineBarProps) => {
const [width, setWidth] = useState(indicatorWidth);
const [currentDuration, setCurrentDuration] = useState(indicatorWidth);
const [totalHours, setTotalHours] = useState(project?.total_hours);
const [leftOffset, setLeftOffset] = useState(indicatorOffset);
const [isModalOpen, setIsModalOpen] = useState(false);
const { t } = useTranslation('schedule');
const themeMode = useAppSelector(state => state.themeReducer.mode);
const dispatch = useAppDispatch();
const handleResize = (
event: MouseEvent | TouchEvent,
direction: string,
ref: HTMLElement,
delta: { width: number; height: number }
) => {
let newWidth = width;
let newLeftOffset = leftOffset;
if (direction === 'right') {
newWidth = Math.max(CELL_WIDTH, width + delta.width);
if (newWidth <= CELL_WIDTH * 30) {
setWidth(newWidth);
const newDuration = Math.round(newWidth / CELL_WIDTH);
setCurrentDuration(newDuration);
setTotalHours(newDuration * project?.hours_per_day);
}
} else if (direction === 'left') {
const deltaWidth = Math.min(leftOffset, delta.width);
newLeftOffset = leftOffset - deltaWidth;
newWidth = width + deltaWidth;
if (newLeftOffset >= 0 && newWidth >= CELL_WIDTH && newWidth <= CELL_WIDTH * 30) {
setLeftOffset(newLeftOffset);
setWidth(newWidth);
const newDuration = Math.round(newWidth / CELL_WIDTH);
setCurrentDuration(newDuration);
setTotalHours(newDuration * project?.hours_per_day);
}
}
};
return (
<Popover
content={<ProjectTimelineModal defaultData={defaultData} projectId={project?.id} setIsModalOpen={setIsModalOpen} />}
trigger={'click'}
open={isModalOpen}
>
<Resizable
size={{ width, height: 56 }}
onResizeStop={(e, direction, ref, delta) =>
handleResize(e, direction as 'left' | 'right', ref, delta)
}
minWidth={CELL_WIDTH}
maxWidth={CELL_WIDTH * 30}
grid={[CELL_WIDTH, 1]}
enable={{
top: false,
right: true,
bottom: false,
left: true,
topRight: false,
bottomRight: false,
bottomLeft: false,
topLeft: false,
}}
handleComponent={{
right: <MoreOutlined style={{ fontSize: 24, color: 'white' }} />,
left: <MoreOutlined style={{ fontSize: 24, color: 'white' }} />,
}}
handleClasses={{
right:
'hidden group-hover:flex -translate-x-[5px] bg-[#1890ff] px-1 justify-center rounded-tr rounded-br',
left: 'hidden group-hover:flex translate-x-[5px] bg-[#1890ff] px-1 justify-center rounded-tl rounded-bl',
}}
className="group hover:shadow-md"
style={{
marginInlineStart: leftOffset,
backgroundColor: themeWiseColor(
'rgba(240, 248, 255, 1)',
'rgba(0, 142, 204, 0.5)',
themeMode
),
borderRadius: 6,
border: `1px solid ${themeWiseColor(
'rgba(149, 197, 248, 1)',
'rgba(24, 144, 255, 1)',
themeMode
)}`,
display: 'flex',
alignItems: 'center',
flexDirection: 'column',
justifyContent: 'center',
padding: '4px 10px',
zIndex: 99,
cursor: 'pointer',
}}
>
<Flex
vertical
align="center"
justify="center"
style={{ width: '100%' }}
onClick={() => {setIsModalOpen(true);dispatch(getWorking());}}
>
<Typography.Text
style={{
fontSize: '12px',
fontWeight: 'bold',
}}
ellipsis={{ expanded: false }}
>
{t('total')} {totalHours}h
</Typography.Text>
{currentDuration > 1 && (
<Typography.Text style={{ fontSize: '10px' }} ellipsis={{ expanded: false }}>
{t('perDay')} {project?.hours_per_day}h
</Typography.Text>
)}
<Typography.Text
style={{
fontSize: '10px',
textDecoration: 'underline',
width: 'fit-content',
}}
ellipsis={{ expanded: false }}
onClick={e => {
e.stopPropagation();
dispatch(toggleScheduleDrawer());
}}
>
20 {t('tasks')}
</Typography.Text>
</Flex>
</Resizable>
</Popover>
);
};
export default React.memo(ProjectTimelineBar);