diff --git a/worklenz-frontend/src/components/CustomAvatar.tsx b/worklenz-frontend/src/components/CustomAvatar.tsx index 309e2cb7..ef63448e 100644 --- a/worklenz-frontend/src/components/CustomAvatar.tsx +++ b/worklenz-frontend/src/components/CustomAvatar.tsx @@ -1,25 +1,37 @@ +import React from 'react'; import Tooltip from 'antd/es/tooltip'; import Avatar from 'antd/es/avatar'; import { AvatarNamesMap } from '../shared/constants'; -const CustomAvatar = ({ avatarName, size = 32 }: { avatarName: string; size?: number }) => { - const avatarCharacter = avatarName[0].toUpperCase(); +interface CustomAvatarProps { + avatarName: string; + size?: number; +} - return ( - - - {avatarCharacter} - - - ); -}; +const CustomAvatar = React.forwardRef( + ({ avatarName, size = 32 }, ref) => { + const avatarCharacter = avatarName[0].toUpperCase(); + + return ( + +
+ + {avatarCharacter} + +
+
+ ); + } +); + +CustomAvatar.displayName = 'CustomAvatar'; export default CustomAvatar; diff --git a/worklenz-frontend/src/components/CustomColordLabel.tsx b/worklenz-frontend/src/components/CustomColordLabel.tsx index aa7afba5..068907f0 100644 --- a/worklenz-frontend/src/components/CustomColordLabel.tsx +++ b/worklenz-frontend/src/components/CustomColordLabel.tsx @@ -8,46 +8,51 @@ interface CustomColordLabelProps { isDarkMode?: boolean; } -const CustomColordLabel: React.FC = ({ label, isDarkMode = false }) => { - const truncatedName = - label.name && label.name.length > 10 ? `${label.name.substring(0, 10)}...` : label.name; +const CustomColordLabel = React.forwardRef( + ({ label, isDarkMode = false }, ref) => { + const truncatedName = + label.name && label.name.length > 10 ? `${label.name.substring(0, 10)}...` : label.name; - // Ensure we have a valid color, fallback to a default if not - const backgroundColor = label.color || '#6b7280'; // Default to gray-500 if no color - - // Function to determine if we should use white or black text based on background color - const getTextColor = (bgColor: string): string => { - // Remove # if present - const color = bgColor.replace('#', ''); + // Handle different color property names for different types + const backgroundColor = (label as Label).color || (label as ITaskLabel).color_code || '#6b7280'; // Default to gray-500 if no color - // Convert to RGB - const r = parseInt(color.substr(0, 2), 16); - const g = parseInt(color.substr(2, 2), 16); - const b = parseInt(color.substr(4, 2), 16); - - // Calculate luminance - const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255; - - // Return white for dark backgrounds, black for light backgrounds - return luminance > 0.5 ? '#000000' : '#ffffff'; - }; + // Function to determine if we should use white or black text based on background color + const getTextColor = (bgColor: string): string => { + // Remove # if present + const color = bgColor.replace('#', ''); + + // Convert to RGB + const r = parseInt(color.substr(0, 2), 16); + const g = parseInt(color.substr(2, 2), 16); + const b = parseInt(color.substr(4, 2), 16); + + // Calculate luminance + const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255; + + // Return white for dark backgrounds, black for light backgrounds + return luminance > 0.5 ? '#000000' : '#ffffff'; + }; - const textColor = getTextColor(backgroundColor); + const textColor = getTextColor(backgroundColor); - return ( - - - {truncatedName} - - - ); -}; + return ( + + + {truncatedName} + + + ); + } +); + +CustomColordLabel.displayName = 'CustomColordLabel'; export default CustomColordLabel; diff --git a/worklenz-frontend/src/components/CustomNumberLabel.tsx b/worklenz-frontend/src/components/CustomNumberLabel.tsx index 16521087..89c2d740 100644 --- a/worklenz-frontend/src/components/CustomNumberLabel.tsx +++ b/worklenz-frontend/src/components/CustomNumberLabel.tsx @@ -9,28 +9,28 @@ interface CustomNumberLabelProps { color?: string; // Add color prop for label color } -const CustomNumberLabel: React.FC = ({ - labelList, - namesString, - isDarkMode = false, - color, -}) => { - // Use provided color, or fall back to NumbersColorMap based on first digit - const backgroundColor = color || (() => { - const firstDigit = namesString.match(/\d/)?.[0] || '0'; - return NumbersColorMap[firstDigit] || NumbersColorMap['0']; - })(); - - return ( - - - {namesString} - - - ); -}; +const CustomNumberLabel = React.forwardRef( + ({ labelList, namesString, isDarkMode = false, color }, ref) => { + // Use provided color, or fall back to NumbersColorMap based on first digit + const backgroundColor = color || (() => { + const firstDigit = namesString.match(/\d/)?.[0] || '0'; + return NumbersColorMap[firstDigit] || NumbersColorMap['0']; + })(); + + return ( + + + {namesString} + + + ); + } +); + +CustomNumberLabel.displayName = 'CustomNumberLabel'; export default CustomNumberLabel; diff --git a/worklenz-frontend/src/components/common/project-status-icon/project-status-icon.tsx b/worklenz-frontend/src/components/common/project-status-icon/project-status-icon.tsx index 0be59f72..09974db2 100644 --- a/worklenz-frontend/src/components/common/project-status-icon/project-status-icon.tsx +++ b/worklenz-frontend/src/components/common/project-status-icon/project-status-icon.tsx @@ -1,3 +1,4 @@ +import React from 'react'; import Icon, { CheckCircleOutlined, ClockCircleOutlined, @@ -12,10 +13,23 @@ const iconMap = { 'check-circle': CheckCircleOutlined, }; -const ProjectStatusIcon = ({ iconName, color }: { iconName: string; color: string }) => { - const IconComponent = iconMap[iconName as keyof typeof iconMap]; - if (!IconComponent) return null; - return ; -}; +interface ProjectStatusIconProps { + iconName: string; + color: string; +} + +const ProjectStatusIcon = React.forwardRef( + ({ iconName, color }, ref) => { + const IconComponent = iconMap[iconName as keyof typeof iconMap]; + if (!IconComponent) return null; + return ( + + + + ); + } +); + +ProjectStatusIcon.displayName = 'ProjectStatusIcon'; export default ProjectStatusIcon; diff --git a/worklenz-frontend/src/components/common/tooltip-wrapper/tooltip-wrapper.tsx b/worklenz-frontend/src/components/common/tooltip-wrapper/tooltip-wrapper.tsx new file mode 100644 index 00000000..ab9d1504 --- /dev/null +++ b/worklenz-frontend/src/components/common/tooltip-wrapper/tooltip-wrapper.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { Tooltip, TooltipProps } from 'antd'; + +interface TooltipWrapperProps extends Omit { + children: React.ReactElement; +} + +/** + * TooltipWrapper - A wrapper component that helps avoid findDOMNode warnings in React StrictMode + * + * This component ensures that the child element can properly receive refs from Ant Design's Tooltip + * by wrapping it in a div with a ref when necessary. + */ +const TooltipWrapper = React.forwardRef( + ({ children, ...tooltipProps }, ref) => { + return ( + +
+ {children} +
+
+ ); + } +); + +TooltipWrapper.displayName = 'TooltipWrapper'; + +export default TooltipWrapper; \ No newline at end of file diff --git a/worklenz-frontend/src/components/task-list-v2/TaskRow.tsx b/worklenz-frontend/src/components/task-list-v2/TaskRow.tsx index a816dfe0..61673d2b 100644 --- a/worklenz-frontend/src/components/task-list-v2/TaskRow.tsx +++ b/worklenz-frontend/src/components/task-list-v2/TaskRow.tsx @@ -54,7 +54,7 @@ const TaskLabelsCell: React.FC = memo(({ labels, isDarkMode } return ( - <> +
{labels.map((label, index) => { const extendedLabel = label as any; return extendedLabel.end && extendedLabel.names && extendedLabel.name ? ( @@ -73,7 +73,7 @@ const TaskLabelsCell: React.FC = memo(({ labels, isDarkMode /> ); })} - +
); }); @@ -322,9 +322,19 @@ const TaskRow: React.FC = memo(({ taskId, projectId, visibleColumn {isSubtask &&
}
- - {taskDisplayName} - + + + {taskDisplayName} + + {/* Subtask count indicator - only show if count > 1 */} {!isSubtask && task.sub_tasks_count != null && task.sub_tasks_count !== 0 && ( @@ -552,7 +562,7 @@ const TaskRow: React.FC = memo(({ taskId, projectId, visibleColumn case 'labels': return ( -
+