Skip to content

Commit

Permalink
[Improve] Add the functionality to select multiple assignees (#2533)
Browse files Browse the repository at this point in the history
* [Improve] Add the functionality to select multiple assignees

* Add the functionality to show the members when clicking the arrow

* Remove the arrow in the task card component and update the assign task button

* Remove unused variables

* Remove unused imports

* Fix type error, add functionality to add more assignees to a task and make popup display 5 members
  • Loading branch information
GedeonTS committed May 19, 2024
1 parent c6902ae commit 356ff51
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 24 deletions.
51 changes: 41 additions & 10 deletions apps/web/lib/components/image-overlapper.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Popover, PopoverContent, PopoverTrigger } from '@components/ui/popover';
import Image from 'next/image';
import Link from 'next/link';
import { ITeamTask, ITimerStatus } from '@app/interfaces';
import Skeleton from 'react-loading-skeleton';
import { Tooltip } from './tooltip';
import { ScrollArea } from '@components/ui/scroll-bar';
Expand All @@ -9,6 +10,8 @@ import { useModal } from '@app/hooks';
import { Modal, Divider } from 'lib/components';
import { useOrganizationTeams } from '@app/hooks';
import { useTranslations } from 'next-intl';
import { TaskAssignButton } from '../../lib/features/task/task-assign-button';
import { clsxm } from '@app/utils';

import TeamMember from 'lib/components/team-member';

Expand All @@ -18,18 +21,33 @@ export interface ImageOverlapperProps {
alt: string;
}

interface ArrowDataProps {
activeTaskStatus: ITimerStatus | null | undefined;
disabled: boolean;
task: ITeamTask;
className: string | undefined;
iconClassName: string | undefined;
}


export default function ImageOverlapper({
images,
radius = 20,
displayImageCount = 4,
item = null,
diameter = 40
diameter = 40,
iconType = false,
arrowData = null,
hasActiveMembers = false
}: {
images: ImageOverlapperProps[];
radius?: number;
displayImageCount?: number;
item?: any;
diameter?: number;
iconType?: boolean;
arrowData?: ArrowDataProps | null;
hasActiveMembers?: boolean;
}) {
// Split the array into two arrays based on the display number
const firstArray = images.slice(0, displayImageCount);
Expand All @@ -48,28 +66,41 @@ export default function ImageOverlapper({
if (imageLength == undefined) {
return <Skeleton height={40} width={40} borderRadius={100} className="rounded-full dark:bg-[#353741]" />;
}
if (!hasMembers && item) {
if ((!hasMembers && item) || hasActiveMembers) {
return (
<div>
<CircleIcon className="w-6 h-6 cursor-pointer stroke-[#d3d3d3]" onClick={openModal} style={{ width: diameter, height: diameter }} />
{
iconType ? (
<TaskAssignButton
onClick={openModal}
disabled={arrowData?.activeTaskStatus ? arrowData?.disabled : arrowData?.task.status === 'closed'}
className={clsxm('h-9 w-9', arrowData?.className)}
iconClassName={arrowData?.iconClassName}
/>

) : (
<CircleIcon className="w-6 h-6 cursor-pointer stroke-[#d3d3d3]" onClick={openModal} style={{ width: diameter, height: diameter }} />
)
}

<div>
<Modal
isOpen={isOpen}
closeModal={closeModal}
title={t('common.SELECT_TEAM_MEMBER')}
className="bg-light--theme-light dark:bg-dark--theme-light p-5 rounded-xl w-full md:w-[20vw] h-[70vh] justify-start"
className="bg-light--theme-light dark:bg-dark--theme-light p-5 rounded-xl w-full md:w-[20vw] h-[45vh] justify-start"
titleClass="font-normal"
>
<Divider className="mt-4" />
<ul className="py-6 max-h-56 overflow-auto">
<ul className="py-6 overflow-auto">
{allMembers?.map((member: any) => {
return (
<li
key={member.employee}
className="w-100 border border-transparent hover:border-blue-500 hover:border-opacity-50 rounded-lg cursor-pointer"
>
<TeamMember member={member} item={item} />
</li>
key={member.employee}
className="w-100 border border-transparent hover:border-blue-500 hover:border-opacity-50 rounded-lg cursor-pointer"
>
<TeamMember member={member} item={item} />
</li>
);
})}
</ul>
Expand Down
35 changes: 21 additions & 14 deletions apps/web/lib/features/task/task-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
useTeamTasks,
useTimerView
} from '@app/hooks';
import ImageComponent, { ImageOverlapperProps } from 'lib/components/image-overlapper';
import {
IClassName,
IDailyPlan,
Expand Down Expand Up @@ -43,7 +44,6 @@ import { SetterOrUpdater, useRecoilValue } from 'recoil';
import { TaskEstimateInfo } from '../team/user-team-card/task-estimate';
import { TimerButton } from '../timer/timer-button';
import { TaskAllStatusTypes } from './task-all-status-type';
import { TaskAssignButton } from './task-assign-button';
import { TaskNameInfoDisplay } from './task-displays';
import { TaskAvatars } from './task-item';
import { ActiveTaskStatusDropdown } from './task-status';
Expand Down Expand Up @@ -123,6 +123,15 @@ export function TaskCard(props: Props) {

const memberInfo = useTeamMemberCard(currentMember || undefined);
const taskEdition = useTMCardTaskEdit(task);
const activeMembers = task != null && task.members.length > 0;
const taskAssignee: ImageOverlapperProps[] = task?.members.map((member: any) => {
return {
id: member.user.id,
url: member.user.imageUrl,
alt: member.user.firstName
};
}) || [];


return (
<>
Expand Down Expand Up @@ -159,8 +168,9 @@ export function TaskCard(props: Props) {
)}

{viewType === 'unassign' && (
<div className="w-[20%] flex justify-around">
<div className="w-[20%] flex justify-around items-center">
<UsersTaskAssigned task={task} />
<ImageComponent radius={40} images={taskAssignee} item={task} hasActiveMembers={activeMembers} />
</div>
)}
<VerticalSeparator />
Expand All @@ -186,9 +196,9 @@ export function TaskCard(props: Props) {
{!isAuthUser && task && viewType === 'unassign' && (
<AssignTaskButtonCall
task={task}
assignTask={memberInfo.assignTask}
className="w-11 h-11 border border-[#0000001A] dark:border-[0.125rem] dark:border-[#28292F]"
iconClassName='text-primary dark:text-white'
taskAssignee={taskAssignee}
/>
)}
</div>
Expand Down Expand Up @@ -376,14 +386,14 @@ function TimerButtonCall({

function AssignTaskButtonCall({
task,
assignTask,
className,
iconClassName
iconClassName,
taskAssignee,
}: {
task: ITeamTask;
assignTask: (task: ITeamTask) => Promise<void>;
className?: string;
iconClassName?: string;
taskAssignee: ImageOverlapperProps[];
}) {
const {
disabled,
Expand All @@ -394,15 +404,12 @@ function AssignTaskButtonCall({

const activeTaskStatus = activeTeamTask?.id === task.id ? timerStatus : undefined;

const arrowData = {
activeTaskStatus, disabled, task, className, iconClassName
}

return (
<TaskAssignButton
onClick={() => {
assignTask(task);
}}
disabled={activeTaskStatus ? disabled : task.status === 'closed'}
className={clsxm('h-9 w-9', className)}
iconClassName={iconClassName}
/>
<ImageComponent radius={30} images={taskAssignee} item={task} iconType={true} arrowData={arrowData} />
);
}

Expand Down

0 comments on commit 356ff51

Please sign in to comment.