Skip to content

Commit

Permalink
Merge pull request #2562 from ever-co/stage
Browse files Browse the repository at this point in the history
Release
  • Loading branch information
evereq authored May 30, 2024
2 parents aebe7f9 + 0d4af6b commit 92ea6f6
Show file tree
Hide file tree
Showing 23 changed files with 295 additions and 138 deletions.
142 changes: 82 additions & 60 deletions apps/web/app/[locale]/profile/[memberId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ITimerStatusEnum, OT_Member } from '@app/interfaces';
import { clsxm, isValidUrl } from '@app/utils';
import clsx from 'clsx';
import { withAuthentication } from 'lib/app/authenticator';
import { Avatar, Breadcrumb, Container, Text, VerticalSeparator } from 'lib/components';
import { Avatar, Breadcrumb, Button, Container, Text, VerticalSeparator } from 'lib/components';
import { ArrowLeftIcon } from 'assets/svg';
import { TaskFilter, Timer, TimerStatus, UserProfileTask, getTimerStatusValue, useTaskFilter } from 'lib/features';
import { MainHeader, MainLayout } from 'lib/layout';
Expand All @@ -29,6 +29,7 @@ const Profile = React.memo(function ProfilePage({ params }: { params: { memberId
const profile = useUserProfilePage();
const { user } = useAuthenticateUser();
const { isTrackingEnabled, activeTeam, activeTeamManagers } = useOrganizationTeams();
const members = activeTeam?.members;
const { getEmployeeDayPlans } = useDailyPlan();
const fullWidth = useRecoilValue(fullWidthState);
const [activityFilter, setActivityFilter] = useState<FilterTab>('Tasks');
Expand Down Expand Up @@ -74,72 +75,93 @@ const Profile = React.memo(function ProfilePage({ params }: { params: { memberId
getEmployeeDayPlans(profile.member?.employeeId ?? '');
}, [getEmployeeDayPlans, profile.member?.employeeId]);

// Example usage

return (
<>
<MainLayout showTimer={profileIsAuthUser && isTrackingEnabled}>
<MainHeader
fullWidth={fullWidth}
className={clsxm(hookFilterType && ['pb-0'], 'pb-2', 'pt-20 sticky top-20 z-50')}
>
{/* Breadcrumb */}
<div className="flex items-center gap-8">
<Link href="/">
<ArrowLeftIcon className="w-6 h-6" />
</Link>

<Breadcrumb paths={breadcrumb} className="text-sm" />
{Array.isArray(members) && members.length && !profile.member ? (
<MainLayout>
<div className=" absolute top-[50%] left-[50%] transform -translate-x-1/2 -translate-y-1/2">
<div className="flex flex-col justify-center items-center gap-5">
<Text className="text-[40px] font-bold text-center text-[#282048] dark:text-light--theme">
{t('common.MEMBER')} {t('common.NOT_FOUND')}!
</Text>

<Text className=" font-light text-center text-gray-400">
{t('pages.profile.MEMBER_NOT_FOUND_MSG_1')}
</Text>
<Text className=" font-light text-center text-gray-400">
{t('pages.profile.MEMBER_NOT_FOUND_MSG_1')}
</Text>

<Button className="m-auto font-normal rounded-lg ">
<Link href="/">{t('pages.profile.GO_TO_HOME')}</Link>
</Button>
</div>
</div>
</MainLayout>
) : (
<MainLayout showTimer={profileIsAuthUser && isTrackingEnabled}>
<MainHeader
fullWidth={fullWidth}
className={clsxm(hookFilterType && ['pb-0'], 'pb-2', 'pt-20 sticky top-20 z-50')}
>
{/* Breadcrumb */}
<div className="flex items-center gap-8">
<Link href="/">
<ArrowLeftIcon className="w-6 h-6" />
</Link>

{/* User Profile Detail */}
<div className="flex flex-col items-center justify-between py-5 md:py-10 md:flex-row">
<UserProfileDetail member={profile.member} />

{profileIsAuthUser && isTrackingEnabled && (
<Timer
className={clsxm(
'p-5 rounded-2xl shadow-xlcard',
'dark:border-[0.125rem] dark:border-[#28292F]',
'dark:bg-[#1B1D22]'
)}
/>
)}
</div>
{/* TaskFilter */}
<TaskFilter profile={profile} hook={hook} />
</MainHeader>
{/* Divider */}
<div className="h-0.5 bg-[#FFFFFF14]"></div>
{hook.tab == 'worked' && canSeeActivity && (
<Container fullWidth={fullWidth} className="py-8">
<div className={clsxm('flex justify-start items-center gap-4 mt-3')}>
{Object.keys(activityScreens).map((filter, i) => (
<div key={i} className="flex cursor-pointer justify-start items-center gap-4">
{i !== 0 && <VerticalSeparator />}
<div
className={clsxm(
'text-gray-500',
activityFilter == filter && 'text-black dark:text-white'
)}
onClick={() => changeActivityFilter(filter as FilterTab)}
>
{filter}
</div>
</div>
))}
<Breadcrumb paths={breadcrumb} className="text-sm" />
</div>
</Container>
)}

<Container fullWidth={fullWidth} className="mb-10">
{hook.tab !== 'worked' || activityFilter == 'Tasks' ? (
<UserProfileTask profile={profile} tabFiltered={hook} />
) : (
activityScreens[activityFilter] ?? null
{/* User Profile Detail */}
<div className="flex flex-col items-center justify-between py-5 md:py-10 md:flex-row">
<UserProfileDetail member={profile.member} />

{profileIsAuthUser && isTrackingEnabled && (
<Timer
className={clsxm(
'p-5 rounded-2xl shadow-xlcard',
'dark:border-[0.125rem] dark:border-[#28292F]',
'dark:bg-[#1B1D22]'
)}
/>
)}
</div>
{/* TaskFilter */}
<TaskFilter profile={profile} hook={hook} />
</MainHeader>
{/* Divider */}
<div className="h-0.5 bg-[#FFFFFF14]"></div>
{hook.tab == 'worked' && canSeeActivity && (
<Container fullWidth={fullWidth} className="py-8">
<div className={clsxm('flex justify-start items-center gap-4 mt-3')}>
{Object.keys(activityScreens).map((filter, i) => (
<div key={i} className="flex cursor-pointer justify-start items-center gap-4">
{i !== 0 && <VerticalSeparator />}
<div
className={clsxm(
'text-gray-500',
activityFilter == filter && 'text-black dark:text-white'
)}
onClick={() => changeActivityFilter(filter as FilterTab)}
>
{filter}
</div>
</div>
))}
</div>
</Container>
)}
</Container>
</MainLayout>

<Container fullWidth={fullWidth} className="mb-10">
{hook.tab !== 'worked' || activityFilter == 'Tasks' ? (
<UserProfileTask profile={profile} tabFiltered={hook} />
) : (
activityScreens[activityFilter] ?? null
)}
</Container>
</MainLayout>
)}
</>
);
});
Expand Down
12 changes: 4 additions & 8 deletions apps/web/app/hooks/features/useLanguageSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,10 @@ export function useLanguageSettings() {

const loadLanguagesData = useCallback(() => {
setActiveLanguageId(getActiveLanguageIdCookie());
if (user) {
return queryCall(user.role.isSystem).then((res) => {
setLanguages(
res?.data?.items.filter((item: any) => APPLICATION_LANGUAGES_CODE.includes(item.code)) || []
);
return res;
});
}
return queryCall(user?.role?.isSystem ?? false).then((res) => {
setLanguages(res?.data?.items.filter((item: any) => APPLICATION_LANGUAGES_CODE.includes(item.code)) || []);
return res;
});
}, [queryCall, setActiveLanguageId, setLanguages, user]);

const setActiveLanguage = useCallback(
Expand Down
84 changes: 69 additions & 15 deletions apps/web/lib/components/image-overlapper.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useState } from 'react';
import { Popover, PopoverContent, PopoverTrigger } from '@components/ui/popover';
import Image from 'next/image';
import Link from 'next/link';
Expand All @@ -12,8 +13,10 @@ 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 { TaskAvatars } from 'lib/features';
import { FaCheck } from "react-icons/fa6";
import TeamMember from 'lib/components/team-member';
import { IEmployee } from '@app/interfaces';

export interface ImageOverlapperProps {
id: string;
Expand All @@ -22,14 +25,13 @@ export interface ImageOverlapperProps {
}

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


export default function ImageOverlapper({
images,
radius = 20,
Expand All @@ -38,7 +40,8 @@ export default function ImageOverlapper({
diameter = 40,
iconType = false,
arrowData = null,
hasActiveMembers = false
hasActiveMembers = false,
assignTaskButtonCall = false,
}: {
images: ImageOverlapperProps[];
radius?: number;
Expand All @@ -48,6 +51,7 @@ export default function ImageOverlapper({
iconType?: boolean;
arrowData?: ArrowDataProps | null;
hasActiveMembers?: boolean;
assignTaskButtonCall?: boolean;
}) {
// Split the array into two arrays based on the display number
const firstArray = images.slice(0, displayImageCount);
Expand All @@ -58,15 +62,39 @@ export default function ImageOverlapper({
const { isOpen, openModal, closeModal } = useModal();
const { activeTeam } = useOrganizationTeams();
const allMembers = activeTeam?.members || [];
const [assignedMembers, setAssignedMembers] = useState<IEmployee[]>([...(item?.members || [])]);
const [unassignedMembers, setUnassignedMembers] = useState<IEmployee[]>([]);
const [validate, setValidate] = useState<boolean>(false);

const t = useTranslations();

const hasMembers = item?.members.length > 0;
const onCheckMember = (member: any) => {
const checkUser = assignedMembers.some((el: IEmployee) => el.id === member.id);
if (checkUser) {
const updatedMembers = assignedMembers.filter((el: IEmployee) => el.id != member.id);
setAssignedMembers(updatedMembers);
setUnassignedMembers([...unassignedMembers, member]);
} else {
setAssignedMembers([...assignedMembers, member]);
const updatedUnassign = unassignedMembers.filter((el: IEmployee) => el.id != member.id);
setUnassignedMembers(updatedUnassign);
}

}

const onCLickValidate = () => {
setValidate(!validate);
closeModal();
}

const hasMembers = item?.members?.length > 0;
const membersList = { assignedMembers, unassignedMembers };

if (imageLength == undefined) {
return <Skeleton height={40} width={40} borderRadius={100} className="rounded-full dark:bg-[#353741]" />;
}
if ((!hasMembers && item) || hasActiveMembers) {

if ((!hasMembers && item) || hasActiveMembers || assignTaskButtonCall) {
return (
<div>
{
Expand All @@ -88,22 +116,48 @@ export default function ImageOverlapper({
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-[45vh] justify-start"
className="bg-light--theme-light dark:bg-dark--theme-light py-5 rounded-xl w-full md:min-w-[20vw] md:max-w-fit h-[45vh] justify-start"
titleClass="font-normal"
>
<Divider className="mt-4" />
<ul className="py-6 overflow-auto">
<ul className="py-6 overflow-auto p-5">
{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}
onCheckMember={onCheckMember}
membersList={membersList}
validate={validate}
/>
</li>
);
})}
</ul>

<div className="sticky top-[100%] h-[60px] w-[100%]">
<Divider className="mt-4" />
<div className='flex -space-x-3.5 overflow-hidden flex-center mt-[5px] items-center sm:justify-between'>
<TaskAvatars task={{ members: assignedMembers }} limit={3} />
<div className="flex px-4 h-fit">
<button
className="flex-row justify-center py-2 px-4 bg-primary
dark:bg-primary-light text-white text-sm disabled:bg-primary-light
disabled:opacity-40 rounded-xl flex min-w-0 w-28
h-12 gap-1 items-center"
onClick={() => {
onCLickValidate();
}}>
<FaCheck size={17} fill="#ffffff" />
{" Validate"}
</button>
</div>
</div>
</div>
</Modal>
</div>
</div>
Expand Down
36 changes: 27 additions & 9 deletions apps/web/lib/components/team-member.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,40 @@
import { UserInfo } from 'lib/features/team/user-team-card/user-info';
import { useTeamMemberCard } from '@app/hooks';
import { useModal } from '@app/hooks';
import { FaCheck } from "react-icons/fa6";
import { useEffect } from 'react';
import { IEmployee } from '@app/interfaces';

export default function TeamMember({ member, item }: { member: any; item: any }) {
export default function TeamMember(
{ member, item, onCheckMember, membersList, validate }
:
{ member: any; item: any; onCheckMember: any; membersList: any; validate: any }
) {
const memberInfo = useTeamMemberCard(member);
const { assignTask } = useTeamMemberCard(member);
const { closeModal } = useModal();
const checkAssign = membersList.assignedMembers.some((el:IEmployee) => el.id === member.employeeId);
const checkUnassign = membersList.unassignedMembers.some((el:IEmployee) => el.id === member.employeeId);

return (
<div
onClick={() => {
useEffect(() => {
if (validate) {
if (checkAssign) {
assignTask(item);
closeModal();
}}
} else if (checkUnassign) {
memberInfo.unassignTask(item);
}
}
}, [validate, checkAssign, checkUnassign, item, assignTask, memberInfo]);

const assignMember = () => {
onCheckMember(member.employee);
}

className="w-100 cursor-pointer"
return (
<div
onClick={() => { assignMember() }}
className="w-100 cursor-pointer flex items-center"
>
<UserInfo memberInfo={memberInfo} className="2xl:w-[20.625rem] w-100 pointer-events-none" />
{checkAssign ? (<FaCheck size={17} fill="#3826a6" />) : (<></>)}
</div>
);
}
Loading

0 comments on commit 92ea6f6

Please sign in to comment.