expand sub tasks

This commit is contained in:
chamiakJ
2025-07-03 01:31:05 +05:30
parent 3bef18901a
commit ecd4d29a38
435 changed files with 13150 additions and 11087 deletions

View File

@@ -5,7 +5,10 @@ import { useAppDispatch } from '@/hooks/useAppDispatch';
import { useSocket } from '@/socket/socketContext';
import { SocketEvents } from '@/shared/socket-events';
import { Task } from '@/types/task-management.types';
import { updateTask, selectCurrentGroupingV3 } from '@/features/task-management/task-management.slice';
import {
updateTask,
selectCurrentGroupingV3,
} from '@/features/task-management/task-management.slice';
interface TaskStatusDropdownProps {
task: Task;
@@ -13,10 +16,10 @@ interface TaskStatusDropdownProps {
isDarkMode?: boolean;
}
const TaskStatusDropdown: React.FC<TaskStatusDropdownProps> = ({
task,
projectId,
isDarkMode = false
const TaskStatusDropdown: React.FC<TaskStatusDropdownProps> = ({
task,
projectId,
isDarkMode = false,
}) => {
const dispatch = useAppDispatch();
const { socket, connected } = useSocket();
@@ -24,36 +27,39 @@ const TaskStatusDropdown: React.FC<TaskStatusDropdownProps> = ({
const [dropdownPosition, setDropdownPosition] = useState({ top: 0, left: 0 });
const buttonRef = useRef<HTMLButtonElement>(null);
const dropdownRef = useRef<HTMLDivElement>(null);
const statusList = useAppSelector(state => state.taskStatusReducer.status);
const currentGroupingV3 = useAppSelector(selectCurrentGroupingV3);
// Find current status details
const currentStatus = useMemo(() => {
return statusList.find(status =>
status.name?.toLowerCase() === task.status?.toLowerCase() ||
status.id === task.status
return statusList.find(
status =>
status.name?.toLowerCase() === task.status?.toLowerCase() || status.id === task.status
);
}, [statusList, task.status]);
// Handle status change
const handleStatusChange = useCallback((statusId: string, statusName: string) => {
if (!task.id || !statusId || !connected) return;
const handleStatusChange = useCallback(
(statusId: string, statusName: string) => {
if (!task.id || !statusId || !connected) return;
console.log('🎯 Status change initiated:', { taskId: task.id, statusId, statusName });
console.log('🎯 Status change initiated:', { taskId: task.id, statusId, statusName });
socket?.emit(
SocketEvents.TASK_STATUS_CHANGE.toString(),
JSON.stringify({
task_id: task.id,
status_id: statusId,
parent_task: null, // Assuming top-level tasks for now
team_id: projectId, // Using projectId as teamId
})
);
socket?.emit(SocketEvents.GET_TASK_PROGRESS.toString(), task.id);
setIsOpen(false);
}, [task.id, connected, socket, projectId]);
socket?.emit(
SocketEvents.TASK_STATUS_CHANGE.toString(),
JSON.stringify({
task_id: task.id,
status_id: statusId,
parent_task: null, // Assuming top-level tasks for now
team_id: projectId, // Using projectId as teamId
})
);
socket?.emit(SocketEvents.GET_TASK_PROGRESS.toString(), task.id);
setIsOpen(false);
},
[task.id, connected, socket, projectId]
);
// Calculate dropdown position and handle outside clicks
useEffect(() => {
@@ -73,7 +79,7 @@ const TaskStatusDropdown: React.FC<TaskStatusDropdownProps> = ({
top: rect.bottom + window.scrollY + 4,
left: rect.left + window.scrollX,
});
document.addEventListener('mousedown', handleClickOutside);
}
@@ -83,14 +89,15 @@ const TaskStatusDropdown: React.FC<TaskStatusDropdownProps> = ({
}, [isOpen]);
// Get status color - enhanced dark mode support
const getStatusColor = useCallback((status: any) => {
if (isDarkMode) {
return status?.color_code_dark || status?.color_code || '#4b5563';
}
return status?.color_code || '#6b7280';
}, [isDarkMode]);
const getStatusColor = useCallback(
(status: any) => {
if (isDarkMode) {
return status?.color_code_dark || status?.color_code || '#4b5563';
}
return status?.color_code || '#6b7280';
},
[isDarkMode]
);
// Status display name - format status names by replacing underscores with spaces
const getStatusDisplayName = useCallback((status: string) => {
@@ -112,7 +119,7 @@ const TaskStatusDropdown: React.FC<TaskStatusDropdownProps> = ({
{/* Status Button - Rounded Pill Design */}
<button
ref={buttonRef}
onClick={(e) => {
onClick={e => {
e.preventDefault();
e.stopPropagation();
setIsOpen(!isOpen);
@@ -123,12 +130,20 @@ const TaskStatusDropdown: React.FC<TaskStatusDropdownProps> = ({
whitespace-nowrap
`}
style={{
backgroundColor: currentStatus ? getStatusColor(currentStatus) : (isDarkMode ? '#4b5563' : '#9ca3af'),
backgroundColor: currentStatus
? getStatusColor(currentStatus)
: isDarkMode
? '#4b5563'
: '#9ca3af',
color: 'white',
}}
>
<span className="truncate">{currentStatus ? formatStatusName(currentStatus.name || '') : getStatusDisplayName(task.status)}</span>
<svg
<span className="truncate">
{currentStatus
? formatStatusName(currentStatus.name || '')
: getStatusDisplayName(task.status)}
</span>
<svg
className={`w-3 h-3 transition-transform duration-200 ${isOpen ? 'rotate-180' : ''}`}
fill="none"
stroke="currentColor"
@@ -139,84 +154,95 @@ const TaskStatusDropdown: React.FC<TaskStatusDropdownProps> = ({
</button>
{/* Dropdown Menu - Redesigned */}
{isOpen && createPortal(
<div
ref={dropdownRef}
className={`
{isOpen &&
createPortal(
<div
ref={dropdownRef}
className={`
fixed min-w-[160px] max-w-[220px]
rounded border backdrop-blur-xs z-9999
${isDarkMode
? 'bg-gray-900/95 border-gray-600 shadow-2xl shadow-black/50'
: 'bg-white/95 border-gray-200 shadow-2xl shadow-gray-500/20'
${
isDarkMode
? 'bg-gray-900/95 border-gray-600 shadow-2xl shadow-black/50'
: 'bg-white/95 border-gray-200 shadow-2xl shadow-gray-500/20'
}
`}
style={{
top: dropdownPosition.top,
left: dropdownPosition.left,
zIndex: 9999,
animation: 'fadeInScale 0.15s ease-out',
}}
>
{/* Status Options */}
<div className="py-1 max-h-64 overflow-y-auto">
{statusList.map((status, index) => {
const isSelected = status.name?.toLowerCase() === task.status?.toLowerCase() || status.id === task.status;
return (
<button
key={status.id}
onClick={() => handleStatusChange(status.id!, status.name!)}
className={`
style={{
top: dropdownPosition.top,
left: dropdownPosition.left,
zIndex: 9999,
animation: 'fadeInScale 0.15s ease-out',
}}
>
{/* Status Options */}
<div className="py-1 max-h-64 overflow-y-auto">
{statusList.map((status, index) => {
const isSelected =
status.name?.toLowerCase() === task.status?.toLowerCase() ||
status.id === task.status;
return (
<button
key={status.id}
onClick={() => handleStatusChange(status.id!, status.name!)}
className={`
w-full px-3 py-2.5 text-left text-xs font-medium flex items-center gap-3
transition-all duration-150 hover:scale-[1.02] active:scale-[0.98]
${isDarkMode
? 'hover:bg-gray-700/80 text-gray-100'
: 'hover:bg-gray-50/70 text-gray-900'
${
isDarkMode
? 'hover:bg-gray-700/80 text-gray-100'
: 'hover:bg-gray-50/70 text-gray-900'
}
${isSelected
? (isDarkMode ? 'bg-gray-700/60 ring-1 ring-blue-400/40' : 'bg-blue-50/50 ring-1 ring-blue-200')
: ''
${
isSelected
? isDarkMode
? 'bg-gray-700/60 ring-1 ring-blue-400/40'
: 'bg-blue-50/50 ring-1 ring-blue-200'
: ''
}
`}
style={{
animationDelay: `${index * 30}ms`,
animation: 'slideInFromLeft 0.2s ease-out forwards',
}}
>
{/* Status Color Indicator */}
<div
className={`w-3 h-3 rounded-full shadow-sm border-2 ${
isDarkMode ? 'border-gray-800/30' : 'border-white/20'
}`}
style={{ backgroundColor: getStatusColor(status) }}
/>
{/* Status Name */}
<span className="flex-1 truncate">
{formatStatusName(status.name || '')}
</span>
{/* Current Status Badge */}
{isSelected && (
<div className="flex items-center gap-1">
<div className={`w-1.5 h-1.5 rounded-full ${isDarkMode ? 'bg-blue-400' : 'bg-blue-500'}`} />
<span className={`text-xs font-medium ${isDarkMode ? 'text-blue-300' : 'text-blue-600'}`}>
Current
</span>
</div>
)}
</button>
);
})}
</div>
</div>,
document.body
)}
style={{
animationDelay: `${index * 30}ms`,
animation: 'slideInFromLeft 0.2s ease-out forwards',
}}
>
{/* Status Color Indicator */}
<div
className={`w-3 h-3 rounded-full shadow-sm border-2 ${
isDarkMode ? 'border-gray-800/30' : 'border-white/20'
}`}
style={{ backgroundColor: getStatusColor(status) }}
/>
{/* Status Name */}
<span className="flex-1 truncate">{formatStatusName(status.name || '')}</span>
{/* Current Status Badge */}
{isSelected && (
<div className="flex items-center gap-1">
<div
className={`w-1.5 h-1.5 rounded-full ${isDarkMode ? 'bg-blue-400' : 'bg-blue-500'}`}
/>
<span
className={`text-xs font-medium ${isDarkMode ? 'text-blue-300' : 'text-blue-600'}`}
>
Current
</span>
</div>
)}
</button>
);
})}
</div>
</div>,
document.body
)}
{/* CSS Animations - Injected as style tag */}
{isOpen && createPortal(
<style>
{`
{isOpen &&
createPortal(
<style>
{`
@keyframes fadeInScale {
from {
opacity: 0;
@@ -239,11 +265,11 @@ const TaskStatusDropdown: React.FC<TaskStatusDropdownProps> = ({
}
}
`}
</style>,
document.head
)}
</style>,
document.head
)}
</>
);
};
export default TaskStatusDropdown;
export default TaskStatusDropdown;