Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Tasks Rendering #2956

Merged
merged 3 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { useTimer } from '@app/hooks';
import { ITimerStatusEnum, OT_Member } from '@app/interfaces';
import { isValidUrl } from '@app/utils';
import { getTimerStatusValue, TimerStatus } from 'lib/features';
import { cn } from 'lib/utils';
import { useMemo } from 'react';
import stc from 'string-to-color';
import { Avatar, Text } from 'lib/components';
import { imgTitle } from '@app/helpers';
import { TableActionPopover } from 'lib/settings/table-action-popover';

export function UserProfileDetail({ member }: { member?: OT_Member }) {
const user = useMemo(() => member?.employee.user, [member?.employee.user]);

const userName = `${user?.firstName || ''} ${user?.lastName || ''}`;
const imgUrl = user?.image?.thumbUrl || user?.image?.fullUrl || user?.imageUrl;
const imageUrl = useMemo(() => imgUrl, [imgUrl]);
const size = 100;
const { timerStatus } = useTimer();
// const isManager = activeTeamManagers.find((member) => member.employee.user?.id === member?.employee.user?.id);
const timerStatusValue: ITimerStatusEnum = useMemo(() => {
return getTimerStatusValue(timerStatus, member, false);
}, [timerStatus, member]);
return (
<div className="flex items-center mb-4 space-x-4 md:mb-0">
<div
className={cn(
`w-[100px] h-[100px]`, // removed the size variable from width and height, as passing variables is not supported by tailwind
'flex justify-center items-center relative',
'rounded-full text-white',
'shadow-md text-7xl dark:text-6xl font-thin font-PlusJakartaSans ',
!imageUrl && 'dark:border-[0.375rem] dark:border-[#26272C]'
)}
style={{
backgroundColor: `${stc(userName)}80`
}}
>
{imageUrl && isValidUrl(imageUrl) ? (
<Avatar
size={size}
className="relative dark:border-[0.375rem] dark:border-[#26272C]"
imageUrl={imageUrl}
alt={userName}
imageTitle={userName.charAt(0)}
>
<TimerStatus
status={timerStatusValue}
className="absolute z-20 bottom-3 right-[10%] -mb-5 border-[0.2956rem] border-white dark:border-[#26272C]"
tooltipClassName="mt-24 dark:mt-20 mr-3"
/>
</Avatar>
) : (
<>
{imgTitle(userName).charAt(0)}

<TimerStatus
status={timerStatusValue}
className="absolute z-20 border-[0.2956rem] border-white dark:border-[#26272C]"
tooltipClassName="absolute -bottom-[0.625rem] dark:-bottom-[0.75rem] right-[10%] w-[1.875rem] h-[1.875rem] rounded-full"
/>
</>
)}
</div>
<div className="flex flex-col gap-3.5 w-full">
<div className="flex items-center gap-x-4">
<Text.Heading as="h3" className="text-2xl md:text-4xl">
{user?.firstName} {user?.lastName}
</Text.Heading>
<div className="h-8 w-8">
<TableActionPopover member={member!} status="profile" />
</div>
</div>
<Text className="text-lg text-gray-500">{user?.email}</Text>
</div>
</div>
);
}
268 changes: 95 additions & 173 deletions apps/web/app/[locale]/profile/[memberId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
'use client';

/* eslint-disable no-mixed-spaces-and-tabs */
import { imgTitle } from '@app/helpers';
import { useAuthenticateUser, useDailyPlan, useOrganizationTeams, useTimer, useUserProfilePage } from '@app/hooks';
import { ITimerStatusEnum, OT_Member } from '@app/interfaces';
import { clsxm, isValidUrl } from '@app/utils';
import clsx from 'clsx';
import { useAuthenticateUser, useDailyPlan, useOrganizationTeams, useUserProfilePage } from '@app/hooks';
import { withAuthentication } from 'lib/app/authenticator';
import { Avatar, Breadcrumb, Button, Container, Text, VerticalSeparator } from 'lib/components';
import { Breadcrumb, Button, Container, Text, VerticalSeparator } from 'lib/components';
import { ArrowLeftIcon } from 'assets/svg';
import { TaskFilter, Timer, TimerStatus, UserProfileTask, getTimerStatusValue, useTaskFilter } from 'lib/features';
import { TaskFilter, Timer, UserProfileTask, useTaskFilter } from 'lib/features';
import { MainHeader, MainLayout } from 'lib/layout';
import Link from 'next/link';
import React, { useCallback, useMemo, useState } from 'react';
import { useTranslations } from 'next-intl';
import stc from 'string-to-color';

import { useRecoilValue, useSetRecoilState } from 'recoil';
import { fullWidthState } from '@app/stores/fullWidth';
Expand All @@ -23,7 +18,8 @@ import { AppsTab } from 'lib/features/activity/apps';
import { VisitedSitesTab } from 'lib/features/activity/visited-sites';
import { activityTypeState } from '@app/stores/activity-type';
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@components/ui/resizable';
import { TableActionPopover } from 'lib/settings/table-action-popover';
import { UserProfileDetail } from './components/UserProfileDetail';
import { cn } from 'lib/utils';
// import { ActivityCalendar } from 'lib/features/activity/calendar';

export type FilterTab = 'Tasks' | 'Screenshots' | 'Apps' | 'Visited Sites';
Expand Down Expand Up @@ -90,179 +86,105 @@ const Profile = React.memo(function ProfilePage({ params }: { params: { memberId
getEmployeeDayPlans(profile.member?.employeeId ?? '');
}, [getEmployeeDayPlans, profile.member?.employeeId]);

return (
<>
{Array.isArray(members) && members.length && !profile.member ? (
<MainLayout>
<div
ref={profile.loadTaskStatsIObserverRef}
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>
if (Array.isArray(members) && members.length && !profile.member) {
return (
<MainLayout>
<div
ref={profile.loadTaskStatsIObserverRef}
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>

<Button className="m-auto font-normal rounded-lg ">
<Link href="/">{t('pages.profile.GO_TO_HOME')}</Link>
</Button>
</div>
</div>
</MainLayout>
);
}

<Button className="m-auto font-normal rounded-lg ">
<Link href="/">{t('pages.profile.GO_TO_HOME')}</Link>
</Button>
return (
<MainLayout showTimer={(headerSize <= 11.8 && isTrackingEnabled) || !profileIsAuthUser}>
<ResizablePanelGroup direction="vertical">
<ResizablePanel
defaultSize={30}
maxSize={48}
className={cn(headerSize < 20 ? '!overflow-hidden' : '!overflow-visible')}
onResize={(size) => setHeaderSize(size)}
>
<MainHeader fullWidth={fullWidth} className={cn(hookFilterType && ['pb-0'], 'pb-2')}>
{/* Breadcrumb */}
<div className="flex items-center gap-8">
<Link href="/">
<ArrowLeftIcon className="w-6 h-6" />
</Link>

<Breadcrumb paths={breadcrumb} className="text-sm" />
</div>
</div>
</MainLayout>
) : (
<MainLayout showTimer={(headerSize <= 11.8 && isTrackingEnabled) || !profileIsAuthUser}>
<ResizablePanelGroup direction="vertical">
<ResizablePanel
defaultSize={30}
maxSize={48}
className={clsxm(headerSize < 20 ? '!overflow-hidden' : '!overflow-visible')}
onResize={(size) => setHeaderSize(size)}
>
<MainHeader fullWidth={fullWidth} className={clsxm(hookFilterType && ['pb-0'], 'pb-2')}>
{/* Breadcrumb */}
<div className="flex items-center gap-8">
<Link href="/">
<ArrowLeftIcon className="w-6 h-6" />
</Link>

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

{/* 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]'
)}
/>
{profileIsAuthUser && isTrackingEnabled && (
<Timer
className={cn(
'p-5 rounded-2xl shadow-xlcard',
'dark:border-[0.125rem] dark:border-[#28292F]',
'dark:bg-[#1B1D22]'
)}
</div>
{/* TaskFilter */}
<TaskFilter profile={profile} hook={hook} />
</MainHeader>
{/* <div className="p-1">
/>
)}
</div>
{/* TaskFilter */}
<TaskFilter profile={profile} hook={hook} />
</MainHeader>
{/* <div className="p-1">
<ActivityCalendar />
</div> */}
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize={65} maxSize={95} className="!overflow-y-scroll custom-scrollbar">
{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>
))}
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize={65} maxSize={95} className="!overflow-y-scroll custom-scrollbar">
{hook.tab == 'worked' && canSeeActivity && (
<Container fullWidth={fullWidth} className="py-8">
<div className={cn('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={cn(
'text-gray-500',
activityFilter == filter && 'text-black dark:text-white'
)}
onClick={() => changeActivityFilter(filter as FilterTab)}
>
{filter}
</div>
</div>
</Container>
)}

<Container fullWidth={fullWidth} className="mb-10">
{hook.tab !== 'worked' || activityFilter == 'Tasks' ? (
<UserProfileTask profile={profile} tabFiltered={hook} />
) : (
activityScreens[activityFilter] ?? null
)}
</Container>
</ResizablePanel>
</ResizablePanelGroup>
</MainLayout>
)}
</>
))}
</div>
</Container>
)}

<Container fullWidth={fullWidth} className="mb-10">
{hook.tab !== 'worked' || activityFilter == 'Tasks' ? (
<UserProfileTask profile={profile} tabFiltered={hook} paginateTasks={true} />
) : (
activityScreens[activityFilter] ?? null
)}
</Container>
</ResizablePanel>
</ResizablePanelGroup>
</MainLayout>
);
});

function UserProfileDetail({ member }: { member?: OT_Member }) {
const user = useMemo(() => member?.employee.user, [member?.employee.user]);

const userName = `${user?.firstName || ''} ${user?.lastName || ''}`;
const imgUrl = user?.image?.thumbUrl || user?.image?.fullUrl || user?.imageUrl;
const imageUrl = useMemo(() => imgUrl, [imgUrl]);
const size = 100;
const { timerStatus } = useTimer();
// const isManager = activeTeamManagers.find((member) => member.employee.user?.id === member?.employee.user?.id);
const timerStatusValue: ITimerStatusEnum = useMemo(() => {
return getTimerStatusValue(timerStatus, member, false);
}, [timerStatus, member]);
return (
<div className="flex items-center mb-4 space-x-4 md:mb-0">

<div
className={clsx(
`w-[100px] h-[100px]`, // removed the size variable from width and height, as passing variables is not supported by tailwind
'flex justify-center items-center relative',
'rounded-full text-white',
'shadow-md text-7xl dark:text-6xl font-thin font-PlusJakartaSans ',
!imageUrl && 'dark:border-[0.375rem] dark:border-[#26272C]'
)}
style={{
backgroundColor: `${stc(userName)}80`
}}
>

{imageUrl && isValidUrl(imageUrl) ? (
<Avatar
size={size}
className="relative dark:border-[0.375rem] dark:border-[#26272C]"
imageUrl={imageUrl}
alt={userName}
imageTitle={userName.charAt(0)}
>

<TimerStatus
status={timerStatusValue}
className="absolute z-20 bottom-3 right-[10%] -mb-5 border-[0.2956rem] border-white dark:border-[#26272C]"
tooltipClassName="mt-24 dark:mt-20 mr-3"
/>

</Avatar>
) : (
<>

{imgTitle(userName).charAt(0)}

<TimerStatus
status={timerStatusValue}
className="absolute z-20 border-[0.2956rem] border-white dark:border-[#26272C]"
tooltipClassName="absolute -bottom-[0.625rem] dark:-bottom-[0.75rem] right-[10%] w-[1.875rem] h-[1.875rem] rounded-full"
/>
</>
)}
</div>
<div className="flex flex-col gap-3.5 w-full">
<div className='flex items-center gap-x-4'>
<Text.Heading as="h3" className="text-2xl md:text-4xl">
{user?.firstName} {user?.lastName}
</Text.Heading>
<div className='h-8 w-8'>
<TableActionPopover member={member!} status='profile' />
</div>
</div>
<Text className="text-lg text-gray-500">{user?.email}</Text>
</div>
</div>
);
}

export default withAuthentication(Profile, { displayName: 'ProfilePage' });
Loading
Loading