feat(assignee-selector): enhance member invitation functionality and integrate project manager checks
- Added hooks for project manager status and authentication to manage member invitation visibility. - Refactored dropdown toggle logic for improved readability and performance. - Updated UI to conditionally render the invite member button based on user roles (admin or project manager). - Cleaned up code formatting for better consistency and maintainability.
This commit is contained in:
@@ -13,6 +13,8 @@ import { sortTeamMembers } from '@/utils/sort-team-members';
|
|||||||
import { useAppDispatch } from '@/hooks/useAppDispatch';
|
import { useAppDispatch } from '@/hooks/useAppDispatch';
|
||||||
import { setIsFromAssigner, toggleProjectMemberDrawer } from '@/features/projects/singleProject/members/projectMembersSlice';
|
import { setIsFromAssigner, toggleProjectMemberDrawer } from '@/features/projects/singleProject/members/projectMembersSlice';
|
||||||
import { updateEnhancedKanbanTaskAssignees } from '@/features/enhanced-kanban/enhanced-kanban.slice';
|
import { updateEnhancedKanbanTaskAssignees } from '@/features/enhanced-kanban/enhanced-kanban.slice';
|
||||||
|
import useIsProjectManager from '@/hooks/useIsProjectManager';
|
||||||
|
import { useAuthStatus } from '@/hooks/useAuthStatus';
|
||||||
|
|
||||||
interface AssigneeSelectorProps {
|
interface AssigneeSelectorProps {
|
||||||
task: IProjectTask;
|
task: IProjectTask;
|
||||||
@@ -42,6 +44,8 @@ const AssigneeSelector: React.FC<AssigneeSelectorProps> = ({
|
|||||||
const currentSession = useAuthService().getCurrentSession();
|
const currentSession = useAuthService().getCurrentSession();
|
||||||
const { socket } = useSocket();
|
const { socket } = useSocket();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
const { isAdmin } = useAuthStatus();
|
||||||
|
const isProjectManager = useIsProjectManager();
|
||||||
|
|
||||||
const filteredMembers = useMemo(() => {
|
const filteredMembers = useMemo(() => {
|
||||||
return teamMembers?.data?.filter(member =>
|
return teamMembers?.data?.filter(member =>
|
||||||
@@ -64,7 +68,7 @@ const AssigneeSelector: React.FC<AssigneeSelectorProps> = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleClickOutside = (event: MouseEvent) => {
|
const handleClickOutside = (event: MouseEvent) => {
|
||||||
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node) &&
|
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node) &&
|
||||||
buttonRef.current && !buttonRef.current.contains(event.target as Node)) {
|
buttonRef.current && !buttonRef.current.contains(event.target as Node)) {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -75,8 +79,8 @@ const AssigneeSelector: React.FC<AssigneeSelectorProps> = ({
|
|||||||
if (buttonRef.current) {
|
if (buttonRef.current) {
|
||||||
const rect = buttonRef.current.getBoundingClientRect();
|
const rect = buttonRef.current.getBoundingClientRect();
|
||||||
const isVisible = rect.top >= 0 && rect.left >= 0 &&
|
const isVisible = rect.top >= 0 && rect.left >= 0 &&
|
||||||
rect.bottom <= window.innerHeight &&
|
rect.bottom <= window.innerHeight &&
|
||||||
rect.right <= window.innerWidth;
|
rect.right <= window.innerWidth;
|
||||||
|
|
||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
updateDropdownPosition();
|
updateDropdownPosition();
|
||||||
@@ -302,12 +306,10 @@ const AssigneeSelector: React.FC<AssigneeSelectorProps> = ({
|
|||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
{pendingChanges.has(member.id || '') && (
|
{pendingChanges.has(member.id || '') && (
|
||||||
<div className={`absolute inset-0 flex items-center justify-center ${
|
<div className={`absolute inset-0 flex items-center justify-center ${isDarkMode ? 'bg-gray-800/50' : 'bg-white/50'
|
||||||
isDarkMode ? 'bg-gray-800/50' : 'bg-white/50'
|
}`}>
|
||||||
}`}>
|
<div className={`w-3 h-3 border border-t-transparent rounded-full animate-spin ${isDarkMode ? 'border-blue-400' : 'border-blue-600'
|
||||||
<div className={`w-3 h-3 border border-t-transparent rounded-full animate-spin ${
|
}`} />
|
||||||
isDarkMode ? 'border-blue-400' : 'border-blue-600'
|
|
||||||
}`} />
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -340,22 +342,26 @@ const AssigneeSelector: React.FC<AssigneeSelectorProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<div className={`p-2 border-t ${isDarkMode ? 'border-gray-600' : 'border-gray-200'}`}>
|
|
||||||
<button
|
{(isAdmin || isProjectManager) && (
|
||||||
className={`
|
<div className={`p-2 border-t ${isDarkMode ? 'border-gray-600' : 'border-gray-200'}`}>
|
||||||
w-full flex items-center justify-center gap-1 px-2 py-1 text-xs rounded
|
<button
|
||||||
transition-colors
|
className={`
|
||||||
${isDarkMode
|
w-full flex items-center justify-center gap-1 px-2 py-1 text-xs rounded
|
||||||
? 'text-blue-400 hover:bg-gray-700'
|
transition-colors
|
||||||
: 'text-blue-600 hover:bg-blue-50'
|
${isDarkMode
|
||||||
}
|
? 'text-blue-400 hover:bg-gray-700'
|
||||||
`}
|
: 'text-blue-600 hover:bg-blue-50'
|
||||||
onClick={handleInviteProjectMemberDrawer}
|
}
|
||||||
>
|
`}
|
||||||
<UserAddOutlined />
|
onClick={handleInviteProjectMemberDrawer}
|
||||||
Invite member
|
>
|
||||||
</button>
|
<UserAddOutlined />
|
||||||
</div>
|
Invite member
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
</div>,
|
</div>,
|
||||||
document.body
|
document.body
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user