Skip to content

Commit

Permalink
Merge pull request #2078 from ever-co/feat/activity
Browse files Browse the repository at this point in the history
feat: get daily apps visited
  • Loading branch information
evereq authored Jan 11, 2024
2 parents fe491ca + 56e9d28 commit 50296c1
Show file tree
Hide file tree
Showing 24 changed files with 349 additions and 15 deletions.
29 changes: 29 additions & 0 deletions apps/web/app/api/timer/daily/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { authenticatedGuard } from '@app/services/server/guards/authenticated-guard-app';
import { getEmployeeDailyRequest } from '@app/services/server/requests/timer/daily';
import { NextResponse } from 'next/server';

export async function GET(req: Request) {
const res = new NextResponse();
const { $res, user, tenantId, organizationId, access_token } = await authenticatedGuard(req, res);
if (!user) return $res('Unauthorized');

const { searchParams } = new URL(req.url);

const { endDate, startDate, type } = searchParams as unknown as {
startDate: Date;
endDate: Date;
type: string;
};

const { data } = await getEmployeeDailyRequest({
tenantId,
organizationId,
employeeId: user.employee.id,
todayEnd: endDate,
todayStart: startDate,
type,
bearer_token: access_token
});

return $res(data);
}
25 changes: 24 additions & 1 deletion apps/web/app/helpers/array-data.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ITimerSlot } from '@app/interfaces/timer/ITimerSlot';
import { pad } from './number';
import { ITimerApps } from '@app/interfaces/timer/ITimerApp';
export function groupDataByHour(data: ITimerSlot[]) {
const groupedData: { startedAt: string; stoppedAt: string; items: ITimerSlot[] }[] = [];

Expand All @@ -24,9 +25,31 @@ export function groupDataByHour(data: ITimerSlot[]) {
return groupedData.sort((a, b) => (new Date(a.stoppedAt) < new Date(b.stoppedAt) ? 1 : -1));
}

export function groupAppsByHour(apps: ITimerApps[]) {
const groupedData: { hour: string; totalMilliseconds: number; apps: ITimerApps[] }[] = [];

apps.forEach((app) => {
const time = app.time.slice(0, 5);

const hourDataIndex = groupedData.findIndex((el) => el.hour == time);

if (hourDataIndex !== -1) {
groupedData[hourDataIndex].apps.push(app);
groupedData[hourDataIndex].totalMilliseconds += +app.duration;
} else
groupedData.push({
hour: app.time.slice(0, 5),
totalMilliseconds: +app.duration,
apps: [app]
});
});

return groupedData.sort((a, b) => (new Date(a.hour) > new Date(b.hour) ? -1 : 1));
}

const formatTime = (d: Date | string, addHour: boolean) => {
d = d instanceof Date ? d : new Date(d);
if (addHour)
return `${new Date(d).getHours() < 10 ? pad(new Date(d).getHours()) + 1 : new Date(d).getHours() + 1}:00`;
return `${new Date(d).getHours() < 10 ? pad(new Date(d).getHours() + 1) : new Date(d).getHours() + 1}:00`;
else return `${new Date(d).getHours() < 10 ? pad(new Date(d).getHours()) : new Date(d).getHours()}:00`;
};
47 changes: 47 additions & 0 deletions apps/web/app/hooks/features/useTimeDailyActivity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
'use client';

import { useCallback, useEffect } from 'react';
import { useQuery } from '../useQuery';
import { useRecoilState } from 'recoil';
import { timeAppsState } from '@app/stores/time-slot';
import moment from 'moment';
import { useAuthenticateUser } from './useAuthenticateUser';
import { getTimerDailyRequestAPI } from '@app/services/client/api';

export function useTimeDailyActivity(type: string) {
const { user } = useAuthenticateUser();
const [visitedApps, setVisitedApps] = useRecoilState(timeAppsState);

const { loading, queryCall } = useQuery(getTimerDailyRequestAPI);

const getVisitedApps = useCallback(() => {
const todayStart = moment().startOf('day').toDate();
const todayEnd = moment().endOf('day').toDate();

queryCall({
tenantId: user?.tenantId ?? '',
organizationId: user?.employee.organizationId ?? '',
employeeId: user?.employee.id ?? '',
todayEnd,
type,
todayStart
})
.then((response) => {
if (response.data) {
console.log(response.data);
setVisitedApps(response.data?.data);
}
})
.catch((err) => console.log(err));
}, [queryCall, setVisitedApps, user, type]);

useEffect(() => {
getVisitedApps();
}, [user, getVisitedApps]);

return {
visitedApps,
getVisitedApps,
loading
};
}
8 changes: 8 additions & 0 deletions apps/web/app/interfaces/timer/ITimerApp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export interface ITimerApps {
sessions: string;
duration: string;
employeeId: string;
date: Date | string;
time: string;
title: string;
}
35 changes: 35 additions & 0 deletions apps/web/app/services/client/api/activity/activity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { get } from '@app/services/client/axios';
import { GAUZY_API_BASE_SERVER_URL } from '@app/constants';

export async function getTimerDailyRequestAPI({
tenantId,
organizationId,
employeeId,
todayEnd,
todayStart,
type
}: {
tenantId: string;
organizationId: string;
employeeId: string;
todayEnd: Date;
todayStart: Date;
type: string;
}) {
const params = {
tenantId: tenantId,
organizationId: organizationId,
'employeeIds[0]': employeeId,
startDate: todayStart.toISOString(),
endDate: todayEnd.toISOString(),
'types[0]': type
};
const query = new URLSearchParams(params);
const endpoint = GAUZY_API_BASE_SERVER_URL.value
? `/timesheet/activity/daily?${query.toString()}`
: `/timer/daily?${query.toString()}`;

const data = await get(endpoint, true);

return data;
}
1 change: 1 addition & 0 deletions apps/web/app/services/client/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ export * from './integrations';
export * from './organization-projects';

export * from './activity/time-slots';
export * from './activity/activity';
36 changes: 36 additions & 0 deletions apps/web/app/services/server/requests/timer/daily.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { serverFetch } from '../../fetch';
import { ITimerSlotDataRequest } from '@app/interfaces/timer/ITimerSlot';

export function getEmployeeDailyRequest({
bearer_token,
tenantId,
organizationId,
todayEnd,
todayStart,
employeeId,
type
}: {
bearer_token: string;
tenantId: string;
organizationId: string;
todayEnd: Date;
todayStart: Date;
employeeId: string;
type: string;
}) {
const params = {
tenantId: tenantId,
organizationId: organizationId,
'employeeIds[0]': employeeId,
startDate: todayStart.toISOString(),
endDate: todayEnd.toISOString(),
'types[0]': type
};
const query = new URLSearchParams(params);
return serverFetch<ITimerSlotDataRequest>({
path: `/timesheet/activity/daily?${query.toString()}`,
method: 'GET',
bearer_token,
tenantId
});
}
6 changes: 6 additions & 0 deletions apps/web/app/stores/time-slot.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { ITimerApps } from '@app/interfaces/timer/ITimerApp';
import { ITimerSlot } from '@app/interfaces/timer/ITimerSlot';
import { atom } from 'recoil';

export const timeSlotsState = atom<ITimerSlot[]>({
key: 'timeSlotsState',
default: []
});

export const timeAppsState = atom<ITimerApps[]>({
key: 'timeAppsState',
default: []
});
49 changes: 48 additions & 1 deletion apps/web/lib/features/activity/apps.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,50 @@
import { useTimeDailyActivity } from '@app/hooks/features/useTimeDailyActivity';
import { AppVisitedSkeleton } from './components/app-visited-skeleton';
import { groupAppsByHour } from '@app/helpers/array-data';
import { useTranslations } from 'next-intl';
import AppVisitedItem from './components/app-visited-Item';

export function AppsTab() {
return <div>Apps Tab</div>;
const { visitedApps, loading } = useTimeDailyActivity('APP');
const t = useTranslations();
const apps = groupAppsByHour(visitedApps);
return (
<div>
<div className="flex justify-end w-full">{/* TODO: Filters components */}</div>
<header className="bg-gray-200 dark:bg-[#26272C] rounded-md p-4 flex items-center justify-between">
<h3 className="text-lg font-semibold w-1/4">{t('timer.APPS')}</h3>
<h3 className="text-lg text-center font-semibold w-1/4">{t('timer.VISITED_DATES')}</h3>
<h3 className="text-lg text-center font-semibold w-1/4">{t('timer.PERCENT_USED')}</h3>
<h3 className="text-lg font-semibold w-1/4 text-end">{t('timer.TIME_SPENT_IN_HOURS')}</h3>
</header>
<section>
{apps.map((app, i) => (
<div
key={i}
className="border shadow-lg shadow-gray-200 rounded-md my-4 p-4 dark:border-[#FFFFFF0D] bg-white dark:bg-[#1B1D22]"
>
<h3>{app.hour}</h3>
<div>
{app.apps?.map((item, i) => (
<div key={i} className="w-full">
<AppVisitedItem app={item} totalMilliseconds={app.totalMilliseconds} />
</div>
))}
</div>
</div>
))}
</section>
{visitedApps.length < 1 && !loading && (
<div className="hover:dark:bg-[#26272C] border dark:border-[#26272C] dark:bg-[#191a20] p-4 py-16 rounded-md flex justify-center items-center my-2">
<p className="text-lg text-center">{t('timer.THERE_IS_NO_APPS_VISITED')}</p>
</div>
)}
{loading && visitedApps.length < 1 && (
<>
<AppVisitedSkeleton />
<AppVisitedSkeleton />
</>
)}
</div>
);
}
24 changes: 24 additions & 0 deletions apps/web/lib/features/activity/components/app-visited-Item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { formatDateString, secondsToTime } from '@app/helpers';
import { ITimerApps } from '@app/interfaces/timer/ITimerApp';
import { ProgressBar } from 'lib/components';
import React from 'react';

const AppVisitedItem = ({ app, totalMilliseconds }: { app: ITimerApps; totalMilliseconds: number }) => {
const { h, m, s } = secondsToTime(+app.duration);
const percent = ((+app.duration * 100) / totalMilliseconds).toFixed(2);
return (
<div className="hover:dark:bg-[#26272C] border dark:border-[#26272C] bg-gray-200 dark:bg-[#191a20] p-4 rounded-md flex justify-between apps-center my-2">
<p className="text-lg w-1/4">{app.title}</p>
<p className="text-lg text-center w-1/4">
{formatDateString(new Date(app.date).toISOString())} - {app.time}
</p>
<div className="text-lg w-1/4 flex justify-center gap-2 px-4">
<p className="w-1/4">{percent}%</p>
<ProgressBar progress={percent + '%'} width={`75%`} />
</div>
<p className="text-lg w-1/4 text-end">{`${h}:${m}:${s}`}</p>
</div>
);
};

export default AppVisitedItem;
18 changes: 18 additions & 0 deletions apps/web/lib/features/activity/components/app-visited-skeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export function AppVisitedSkeleton() {
return (
<div className=" dark:bg-[#26272C] p-4 py-6 animate-pulse rounded-md flex justify-between items-center my-2">
<div className="w-1/4 p-2">
<p className="animate-pulse w-28 py-2 rounded bg-gray-200 dark:bg-[#191a20]"></p>
</div>
<div className="w-1/4 p-2">
<p className="animate-pulse w-28 py-2 rounded bg-gray-200 dark:bg-[#191a20]"></p>
</div>
<div className="w-1/4 p-2">
<p className="animate-pulse w-28 py-2 rounded bg-gray-200 dark:bg-[#191a20]"></p>
</div>
<div className="w-1/4 p-2">
<p className="animate-pulse w-28 py-2 rounded bg-gray-200 dark:bg-[#191a20]"></p>
</div>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const ScreenshootPerHour = ({
endTime={el.stoppedAt}
startTime={el.startedAt}
percent={el.percentage}
imageUrl={el.screenshots[0].thumbUrl}
imageUrl={el.screenshots[0]?.thumbUrl}
/>
</div>
))}
Expand Down
7 changes: 6 additions & 1 deletion apps/web/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,12 @@
"TIME_ACTIVITY": "Activity",
"TOTAL_HOURS": "Total Hours",
"NO_SCREENSHOOT": "No Screenshoots",
"PERCENT_OF_MINUTES": " % of 10 Minutes"
"PERCENT_OF_MINUTES": " % of 10 Minutes",
"APPS": "Apps",
"VISITED_DATES": "Visited Dates",
"PERCENT_USED": "Percent Used",
"TIME_SPENT_IN_HOURS": "Time spent (Hours)",
"THERE_IS_NO_APPS_VISITED": "There is no Apps Visited."
},

"task": {
Expand Down
7 changes: 6 additions & 1 deletion apps/web/messages/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,12 @@
"TIME_ACTIVITY": "Activity",
"TOTAL_HOURS": "Total Hours",
"NO_SCREENSHOOT": "No Screenshoots",
"PERCENT_OF_MINUTES": " % of 10 Minutes"
"PERCENT_OF_MINUTES": " % of 10 Minutes",
"APPS": "Apps",
"VISITED_DATES": "Visited Dates",
"PERCENT_USED": "Percent Used",
"TIME_SPENT_IN_HOURS": "Time spent (Hours)",
"THERE_IS_NO_APPS_VISITED": "There is no Apps Visited."
},
"task": {
"TITLE": "Tâche",
Expand Down
7 changes: 6 additions & 1 deletion apps/web/messages/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,12 @@
"TIME_ACTIVITY": "Activity",
"TOTAL_HOURS": "Total Hours",
"NO_SCREENSHOOT": "No Screenshoots",
"PERCENT_OF_MINUTES": " % of 10 Minutes"
"PERCENT_OF_MINUTES": " % of 10 Minutes",
"APPS": "Apps",
"VISITED_DATES": "Visited Dates",
"PERCENT_USED": "Percent Used",
"TIME_SPENT_IN_HOURS": "Time spent (Hours)",
"THERE_IS_NO_APPS_VISITED": "There is no Apps Visited."
},

"task": {
Expand Down
7 changes: 6 additions & 1 deletion apps/web/messages/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,12 @@
"TIME_ACTIVITY": "Activity",
"TOTAL_HOURS": "Total Hours",
"NO_SCREENSHOOT": "No Screenshoots",
"PERCENT_OF_MINUTES": " % of 10 Minutes"
"PERCENT_OF_MINUTES": " % of 10 Minutes",
"APPS": "Apps",
"VISITED_DATES": "Visited Dates",
"PERCENT_USED": "Percent Used",
"TIME_SPENT_IN_HOURS": "Time spent (Hours)",
"THERE_IS_NO_APPS_VISITED": "There is no Apps Visited."
},

"task": {
Expand Down
7 changes: 6 additions & 1 deletion apps/web/messages/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,12 @@
"TIME_ACTIVITY": "Activity",
"TOTAL_HOURS": "Total Hours",
"NO_SCREENSHOOT": "No Screenshoots",
"PERCENT_OF_MINUTES": " % of 10 Minutes"
"PERCENT_OF_MINUTES": " % of 10 Minutes",
"APPS": "Apps",
"VISITED_DATES": "Visited Dates",
"PERCENT_USED": "Percent Used",
"TIME_SPENT_IN_HOURS": "Time spent (Hours)",
"THERE_IS_NO_APPS_VISITED": "There is no Apps Visited."
},

"task": {
Expand Down
Loading

0 comments on commit 50296c1

Please sign in to comment.