This commit is contained in:
chamikaJ
2025-04-17 18:28:54 +05:30
parent f583291d8a
commit 8825b0410a
2837 changed files with 241385 additions and 127578 deletions

View File

@@ -0,0 +1,19 @@
.status-dropdown .ant-dropdown-menu {
padding: 0 !important;
margin-top: 8px !important;
overflow: hidden;
}
.status-dropdown .ant-dropdown-menu-item {
padding: 0 !important;
}
.status-dropdown-card .ant-card-body {
padding: 0 !important;
}
.status-menu .ant-menu-item {
display: flex;
align-items: center;
height: 41px !important;
}

View File

@@ -0,0 +1,117 @@
import { Badge, Flex, Select } from 'antd';
import './home-tasks-status-dropdown.css';
import { useAppSelector } from '@/hooks/useAppSelector';
import { useTranslation } from 'react-i18next';
import { ITaskStatus } from '@/types/status.types';
import { useState, useEffect, useMemo } from 'react';
import { ALPHA_CHANNEL } from '@/shared/constants';
import { useSocket } from '@/socket/socketContext';
import { SocketEvents } from '@/shared/socket-events';
import { ITaskListStatusChangeResponse } from '@/types/tasks/task-list-status.types';
import { IProjectTask } from '@/types/project/projectTasksViewModel.types';
import { useGetMyTasksQuery } from '@/api/home-page/home-page.api.service';
type HomeTasksStatusDropdownProps = {
task: IProjectTask;
teamId: string;
};
const HomeTasksStatusDropdown = ({ task, teamId }: HomeTasksStatusDropdownProps) => {
const { t } = useTranslation('task-list-table');
const { socket, connected } = useSocket();
const { homeTasksConfig } = useAppSelector(state => state.homePageReducer);
const {
refetch
} = useGetMyTasksQuery(homeTasksConfig, {
skip: true // Skip automatic queries entirely
});
const [selectedStatus, setSelectedStatus] = useState<ITaskStatus | undefined>(undefined);
const handleStatusChange = (statusId: string) => {
if (!task.id || !statusId) return;
socket?.emit(
SocketEvents.TASK_STATUS_CHANGE.toString(),
JSON.stringify({
task_id: task.id,
status_id: statusId,
parent_task: task.parent_task_id || null,
team_id: teamId,
})
);
getTaskProgress(task.id);
};
const handleTaskStatusChange = (response: ITaskListStatusChangeResponse) => {
if (response && response.id === task.id) {
const updatedTask = {
...task,
status_color: response.color_code,
complete_ratio: +response.complete_ratio || 0,
status_id: response.status_id,
status_category: response.statusCategory,
};
setSelectedStatus(updatedTask);
// Only refetch when there's an actual status change
if (response.status_id !== task.status_id) {
refetch();
}
}
};
const getTaskProgress = (taskId: string) => {
socket?.emit(SocketEvents.GET_TASK_PROGRESS.toString(), taskId);
};
useEffect(() => {
const foundStatus = task.project_statuses?.find(status => status.id === task.status_id);
setSelectedStatus(foundStatus);
}, [task.status_id, task.project_statuses]);
useEffect(() => {
socket?.on(SocketEvents.TASK_STATUS_CHANGE.toString(), handleTaskStatusChange);
return () => {
socket?.removeListener(SocketEvents.TASK_STATUS_CHANGE.toString(), handleTaskStatusChange);
};
}, [connected]);
const options = useMemo(
() =>
task.project_statuses?.map(status => ({
value: status.id,
label: (
<Flex gap={8} align="center">
<Badge color={status.color_code} text={status.name} />
</Flex>
),
})),
[task.project_statuses]
);
return (
<>
{
<Select
variant="borderless"
value={task.status_id}
onChange={handleStatusChange}
dropdownStyle={{ borderRadius: 8, minWidth: 150, maxWidth: 200 }}
style={{
backgroundColor: selectedStatus?.color_code + ALPHA_CHANNEL,
borderRadius: 16,
height: 22,
}}
labelRender={value => {
const status = task.project_statuses?.find(status => status.id === value.value);
return status ? <span style={{ fontSize: 13 }}>{status.name}</span> : '';
}}
options={options}
/>
}
</>
);
};
export default HomeTasksStatusDropdown;

View File

@@ -0,0 +1,110 @@
import { useSocket } from "@/socket/socketContext";
import { IProjectTask } from "@/types/project/projectTasksViewModel.types";
import { DatePicker } from "antd";
import dayjs from 'dayjs';
import calendar from 'dayjs/plugin/calendar';
import { SocketEvents } from '@/shared/socket-events';
import type { Dayjs } from 'dayjs';
import { useTranslation } from "react-i18next";
import { useEffect, useState, useMemo } from "react";
import { useAppSelector } from "@/hooks/useAppSelector";
import { useGetMyTasksQuery } from "@/api/home-page/home-page.api.service";
import { getUserSession } from "@/utils/session-helper";
// Extend dayjs with the calendar plugin
dayjs.extend(calendar);
type HomeTasksDatePickerProps = {
record: IProjectTask;
};
const HomeTasksDatePicker = ({ record }: HomeTasksDatePickerProps) => {
const { socket, connected } = useSocket();
const { t } = useTranslation('home');
const { homeTasksConfig } = useAppSelector(state => state.homePageReducer);
const { refetch } = useGetMyTasksQuery(homeTasksConfig, {
skip: true // Skip automatic queries entirely
});
// Use useMemo to avoid re-renders when record.end_date is the same
const initialDate = useMemo(() =>
record.end_date ? dayjs(record.end_date) : null
, [record.end_date]);
const [selectedDate, setSelectedDate] = useState<Dayjs | null>(initialDate);
// Update selected date when record changes
useEffect(() => {
setSelectedDate(initialDate);
}, [initialDate]);
const handleChangeReceived = (value: any) => {
refetch();
};
useEffect(() => {
socket?.on(SocketEvents.TASK_END_DATE_CHANGE.toString(), handleChangeReceived);
socket?.on(SocketEvents.TASK_STATUS_CHANGE.toString(), handleChangeReceived);
return () => {
socket?.removeListener(SocketEvents.TASK_END_DATE_CHANGE.toString(), handleChangeReceived);
socket?.removeListener(SocketEvents.TASK_STATUS_CHANGE.toString(), handleChangeReceived);
};
}, [connected]);
const handleEndDateChanged = (value: Dayjs | null, task: IProjectTask) => {
setSelectedDate(value);
if (!task.id) return;
const body = {
task_id: task.id,
end_date: value?.format('YYYY-MM-DD'),
parent_task: task.parent_task_id,
time_zone: getUserSession()?.timezone_name
? getUserSession()?.timezone_name
: Intl.DateTimeFormat().resolvedOptions().timeZone,
};
socket?.emit(SocketEvents.TASK_END_DATE_CHANGE.toString(), JSON.stringify(body));
};
// Function to dynamically format the date based on the calendar rules
const getFormattedDate = (date: Dayjs | null) => {
if (!date) return '';
return date.calendar(null, {
sameDay: '[Today]',
nextDay: '[Tomorrow]',
nextWeek: 'MMM DD',
lastDay: '[Yesterday]',
lastWeek: 'MMM DD',
sameElse: date.year() === dayjs().year() ? 'MMM DD' : 'MMM DD, YYYY',
});
};
return (
<DatePicker
allowClear
disabledDate={
record.start_date ? current => current.isBefore(dayjs(record.start_date)) : undefined
}
placeholder={t('tasks.dueDatePlaceholder')}
value={selectedDate}
onChange={value => handleEndDateChanged(value || null, record || null)}
format={(value) => getFormattedDate(value)} // Dynamically format the displayed value
style={{
color: selectedDate
? selectedDate.isSame(dayjs(), 'day') || selectedDate.isSame(dayjs().add(1, 'day'), 'day')
? '#52c41a'
: selectedDate.isAfter(dayjs().add(1, 'day'), 'day')
? undefined
: '#ff4d4f'
: undefined,
width: '125px', // Ensure the input takes full width
}}
inputReadOnly // Prevent manual input to avoid overflow issues
variant={'borderless'} // Make the DatePicker borderless
suffixIcon={null}
/>
);
};
export default HomeTasksDatePicker;