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

Release #2079

Merged
merged 7 commits into from
Jan 11, 2024
Merged

Release #2079

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
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
Loading