Skip to content

Commit

Permalink
feat: add 'Stats' tab with global metrics and enhanced legend component
Browse files Browse the repository at this point in the history
  • Loading branch information
Innocent-Akim committed Aug 8, 2024
1 parent 379ab35 commit cc605d6
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 94 deletions.
6 changes: 3 additions & 3 deletions apps/web/app/[locale]/profile/[memberId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ 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 { ActivityCalendar } from 'lib/features/activity/calendar';
// import { ActivityCalendar } from 'lib/features/activity/calendar';

export type FilterTab = 'Tasks' | 'Screenshots' | 'Apps' | 'Visited Sites';

Expand Down Expand Up @@ -149,9 +149,9 @@ const Profile = React.memo(function ProfilePage({ params }: { params: { memberId
{/* TaskFilter */}
<TaskFilter profile={profile} hook={hook} />
</MainHeader>
<div className="p-1">
{/* <div className="p-1">
<ActivityCalendar />
</div>
</div> */}
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize={65} maxSize={95} className="!overflow-y-scroll custom-scrollbar">
Expand Down
73 changes: 0 additions & 73 deletions apps/web/lib/features/activity/calendar.tsx

This file was deleted.

7 changes: 7 additions & 0 deletions apps/web/lib/features/activity/screen-calendar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"use client"
import React from 'react'
import { ActivityCalendar } from '../integrations/activity-calendar'

export function ScreenCalendar() {
return (<ActivityCalendar />)
}
107 changes: 107 additions & 0 deletions apps/web/lib/features/integrations/activity-calendar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
'use client';

import { useTimeLogs } from '@app/hooks/features/useTimeLogs';
import { useEffect, useState } from 'react';
import { CalendarDatum, ResponsiveCalendar } from '@nivo/calendar';
import Skeleton from 'react-loading-skeleton';
import moment from 'moment';

export function ActivityCalendar() {
const { timerLogsDailyReport, timerLogsDailyReportLoading } = useTimeLogs();
const [calendarData, setCalendarData] = useState<CalendarDatum[]>([]);
useEffect(() => {
setCalendarData(
timerLogsDailyReport.map((el) => ({ value: Number((el.sum / 3600).toPrecision(2)), day: el.date }))
);
}, [timerLogsDailyReport]);

return (
<div className=" h-[650px] w-full flex items-center justify-center overflow-y-hidden overflow-x-auto">
{timerLogsDailyReportLoading ? (
<ActivityCalendarSkeleton />
) : (
<div className='flex flex-col w-full h-full relative'>
<ActivityLegend />
<ResponsiveCalendar
data={calendarData}
from={moment().startOf('year').toDate()}
to={moment().startOf('year').toDate()}
emptyColor="#ffffff"
colors={['#ADD8E6', '#9370DB', '#6A5ACD', '#0000FF']}
yearSpacing={40}
monthBorderWidth={0}
dayBorderWidth={0}
daySpacing={2}
monthLegendPosition="before"
margin={{ top: 10, right: 5, bottom: 10, left: 5 }}
legends={[
{
anchor: 'bottom-right',
direction: 'row',
translateY: 36,
itemCount: 3,
itemWidth: 42,
itemHeight: 36,
itemsSpacing: 14,
itemDirection: 'right-to-left',
data: [
{ color: '#0000FF', label: '8 hours or more', id: 'legend-blue' },
{ color: '#6A5ACD', label: '6 hours', id: 'legend-slateblue' },
{ color: '#9370DB', label: '4 hours', id: 'legend-purple' },
{ color: '#ADD8E6', label: '2 hours', id: 'legend-light-blue' }
]
}
]}
monthSpacing={20}
monthLegend={(_, __, d) => d.toLocaleString('en-US', { month: 'short' })}
/>


</div>
)}
</div>
);
}

// Skeletons
function ActivityCalendarSkeleton() {
const { innerWidth: deviceWith } = window;

const skeletons = Array.from(Array(12));

return (
<div className="w-full overflow-hidden flex h-32 items-center justify-around">
{skeletons.map((_, index) => (
<Skeleton
key={index}
width={(deviceWith - (deviceWith * 10) / 100) / 12}
className=" dark:bg-transparent h-32"
/>
))}
</div>
);
}

function ActivityLegend() {
return (
<div className="flex flex-col w-full items-start p-4 bg-white dark:bg-dark--theme-light rounded-lg shadow-sm">
<h3 className="text-lg font-bold mb-2">Legend</h3>
<div className="flex items-center mb-2" id="legend-blue">
<span className="w-4 h-4 inline-block mr-2" style={{ backgroundColor: '#0000FF' }}></span>
<span>8 Hours or more</span>
</div>
<div className="flex items-center mb-2" id="legend-slateblue">
<span className="w-4 h-4 inline-block mr-2" style={{ backgroundColor: '#6A5ACD' }}></span>
<span>6 Hours</span>
</div>
<div className="flex items-center mb-2" id="legend-purple">
<span className="w-4 h-4 inline-block mr-2" style={{ backgroundColor: '#9370DB' }}></span>
<span>4 Hours</span>
</div>
<div className="flex items-center mb-2" id="legend-light-blue">
<span className="w-4 h-4 inline-block mr-2" style={{ backgroundColor: '#ADD8E6' }}></span>
<span>2 Hours</span>
</div>
</div>
)
}
24 changes: 17 additions & 7 deletions apps/web/lib/features/task/task-filters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@ import { useDateRange } from '@app/hooks/useDateRange';
import { TaskDatePickerWithRange } from './task-date-range';
import '../../../styles/style.css';
import { AddManualTimeModal } from '../manual-time/add-manual-time-modal';
import { useTimeLogs } from '@app/hooks/features/useTimeLogs';

export type ITab = 'worked' | 'assigned' | 'unassigned' | 'dailyplan';
export type ITab = 'worked' | 'assigned' | 'unassigned' | 'dailyplan' | 'stats';

type ITabs = {
tab: ITab;
name: string;
count: number;
count?: number;
description: string;
};

Expand All @@ -55,7 +56,7 @@ export function useTaskFilter(profile: I_UserProfilePage) {
const { activeTeamManagers, activeTeam } = useOrganizationTeams();
const { user } = useAuthenticateUser();
const { profileDailyPlans } = useDailyPlan();

const { timerLogsDailyReport } = useTimeLogs();
const isManagerConnectedUser = useMemo(
() => activeTeamManagers.findIndex((member) => member.employee?.user?.id == user?.id),
[activeTeamManagers, user?.id]
Expand All @@ -79,6 +80,7 @@ export function useTaskFilter(profile: I_UserProfilePage) {
unassigned: profile.tasksGrouped.unassignedTasks,
assigned: profile.tasksGrouped.assignedTasks,
worked: profile.tasksGrouped.workedTasks,
stats: [],
dailyplan: [] // Change this soon
}),
[profile.tasksGrouped.assignedTasks, profile.tasksGrouped.unassignedTasks, profile.tasksGrouped.workedTasks]
Expand Down Expand Up @@ -109,17 +111,25 @@ export function useTaskFilter(profile: I_UserProfilePage) {
name: t('common.UNASSIGNED'),
description: t('task.tabFilter.UNASSIGNED_DESCRIPTION'),
count: profile.tasksGrouped.unassignedTasks.length
}
},

];

// For tabs on profile page, display "Worked" and "Daily Plan" only for the logged in user or managers
if (activeTeam?.shareProfileView || canSeeActivity) {

tabs.push({
tab: 'dailyplan',
name: 'Daily Plan',
description: 'This tab shows all yours tasks planned',
count: profile.tasksGrouped.planned
});
tabs.push({
tab: 'stats',
name: 'Stats',
description: 'This tab shows all stats',
count: timerLogsDailyReport.length,
});
tabs.unshift({
tab: 'worked',
name: t('common.WORKED'),
Expand Down Expand Up @@ -192,9 +202,9 @@ export function useTaskFilter(profile: I_UserProfilePage) {
.every((k) => {
return k === 'label'
? intersection(
statusFilters[k],
task['tags'].map((item) => item.name)
).length === statusFilters[k].length
statusFilters[k],
task['tags'].map((item) => item.name)
).length === statusFilters[k].length
: statusFilters[k].includes(task[k]);
});
});
Expand Down
21 changes: 10 additions & 11 deletions apps/web/lib/features/user-profile-tasks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { TaskCard } from './task/task-card';
import { I_TaskFilter } from './task/task-filters';
import { useTranslations } from 'next-intl';
import { useMemo } from 'react';
import { ScreenCalendar } from './activity/screen-calendar';
type Props = {
tabFiltered: I_TaskFilter;
profile: I_UserProfilePage;
Expand Down Expand Up @@ -65,15 +66,14 @@ export function UserProfileTask({ profile, tabFiltered }: Props) {
isAuthUser={profile.isAuthUser}
activeAuthTask={true}
profile={profile}
taskBadgeClassName={` ${
profile.activeUserTeamTask?.issueType === 'Bug'
? '!px-[0.3312rem] py-[0.2875rem]'
: '!px-[0.375rem] py-[0.375rem]'
} rounded-sm`}
taskBadgeClassName={` ${profile.activeUserTeamTask?.issueType === 'Bug'
? '!px-[0.3312rem] py-[0.2875rem]'
: '!px-[0.375rem] py-[0.375rem]'
} rounded-sm`}
taskTitleClassName="mt-[0.0625rem]"
/>
)}

{tabFiltered.tab === 'stats' && <ScreenCalendar />}
{tabFiltered.tab === 'dailyplan' && <UserProfilePlans />}

{tabFiltered.tab === 'worked' && otherTasks.length > 0 && (
Expand All @@ -96,11 +96,10 @@ export function UserProfileTask({ profile, tabFiltered }: Props) {
activeAuthTask={false}
viewType={tabFiltered.tab === 'unassigned' ? 'unassign' : 'default'}
profile={profile}
taskBadgeClassName={`${
task.issueType === 'Bug'
? '!px-[0.3312rem] py-[0.2875rem]'
: '!px-[0.375rem] py-[0.375rem]'
} rounded-sm`}
taskBadgeClassName={`${task.issueType === 'Bug'
? '!px-[0.3312rem] py-[0.2875rem]'
: '!px-[0.375rem] py-[0.375rem]'
} rounded-sm`}
taskTitleClassName="mt-[0.0625rem]"
/>
</li>
Expand Down

0 comments on commit cc605d6

Please sign in to comment.