Skip to content

Commit

Permalink
[Feat]: Add special config for Managers to enable/disable time tracki…
Browse files Browse the repository at this point in the history
…ng for Members (#2925)

* feat: Add config for Managers to enable/disable time tracking for Members

* feat: Add config for Managers to enable/disable time tracking for Member on profile

* fix: cspell

* fix: error

* fix: active team managers
  • Loading branch information
Innocent-Akim authored Aug 19, 2024
1 parent 83daaad commit 6b582e8
Show file tree
Hide file tree
Showing 11 changed files with 174 additions and 50 deletions.
24 changes: 18 additions & 6 deletions apps/web/app/[locale]/profile/[memberId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +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 { TableActionPopover } from 'lib/settings/table-action-popover';
// import { ActivityCalendar } from 'lib/features/activity/calendar';

export type FilterTab = 'Tasks' | 'Screenshots' | 'Apps' | 'Visited Sites';
Expand Down Expand Up @@ -197,17 +198,19 @@ const Profile = React.memo(function ProfilePage({ params }: { params: { memberId

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
Expand All @@ -220,6 +223,7 @@ function UserProfileDetail({ member }: { member?: OT_Member }) {
backgroundColor: `${stc(userName)}80`
}}
>

{imageUrl && isValidUrl(imageUrl) ? (
<Avatar
size={size}
Expand All @@ -228,15 +232,19 @@ function UserProfileDetail({ member }: { member?: OT_Member }) {
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]"
Expand All @@ -245,11 +253,15 @@ function UserProfileDetail({ member }: { member?: OT_Member }) {
</>
)}
</div>

<div className="flex flex-col gap-3.5">
<Text.Heading as="h3" className="text-2xl md:text-4xl">
{user?.firstName} {user?.lastName}
</Text.Heading>
<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>
Expand Down
24 changes: 24 additions & 0 deletions apps/web/app/api/employee/[id]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { IUpdateEmployee } from "@app/interfaces";
import { authenticatedGuard } from "@app/services/server/guards/authenticated-guard-app";
import { updateEmployees } from "@app/services/server/requests";
import { NextResponse } from "next/server";

export async function PUT(req: Request, { params }: { params: { id: string } }) {
const res = new NextResponse();
const { id } = params;
if (!id) {
return
}
const { $res, user, access_token } = await authenticatedGuard(req, res);
if (!user) return $res('Unauthorized');
const body = (await req.json()) as unknown as IUpdateEmployee;

const response = await updateEmployees(
{
bearer_token: access_token,
body,
id
}
);
return $res(response.data)
}
16 changes: 0 additions & 16 deletions apps/web/app/api/employee/working/route.ts

This file was deleted.

19 changes: 18 additions & 1 deletion apps/web/app/hooks/features/useEmployee.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { getWorkingEmployeesAPI } from '@app/services/client/api';
import { getWorkingEmployeesAPI, updateEmployeeAPI } from '@app/services/client/api';
import { workingEmployeesEmailState, workingEmployeesState } from '@app/stores/employee';
import { useCallback, useEffect } from 'react';
import { useRecoilState } from 'recoil';

import { useQuery } from '../useQuery';
import { useAuthenticateUser } from './useAuthenticateUser';
import { IUpdateEmployee } from '@app/interfaces';

export const useEmployee = () => {
const { user } = useAuthenticateUser();
Expand Down Expand Up @@ -39,3 +40,19 @@ export const useEmployee = () => {
workingEmployeesEmail
};
};


export const useEmployeeUpdate = () => {
const { queryCall: employeeUpdateQuery, loading: isLoading } = useQuery(updateEmployeeAPI);

const updateEmployee = useCallback(({ id, data
}: { id: string, data: IUpdateEmployee }) => {
employeeUpdateQuery({ id, data })
.then((res) => res.data)
.catch((error) => {
console.log(error);
});
}, []);

return { updateEmployee, isLoading }
}
1 change: 1 addition & 0 deletions apps/web/app/interfaces/IEmployee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export interface IEmployee {
}

export type ICreateEmployee = Pick<IEmployee, 'tenantId' | 'userId' | 'organizationId' | 'startedWorkOn'>;
export type IUpdateEmployee = Pick<IEmployee, 'id' | 'isTrackingEnabled' | 'organizationId' | 'tenantId' | 'isActive'>

export interface IRole {
id?: string;
Expand Down
9 changes: 7 additions & 2 deletions apps/web/app/services/client/api/employee.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { GAUZY_API_BASE_SERVER_URL } from '@app/constants';
import { IWorkingEmployee, PaginationResponse } from '@app/interfaces';
import { get } from '../axios';
import { IUpdateEmployee, IWorkingEmployee, PaginationResponse } from '@app/interfaces';
import { get, put } from '../axios';
import qs from 'qs';

export async function getWorkingEmployeesAPI(tenantId: string, organizationId: string) {
Expand All @@ -15,3 +15,8 @@ export async function getWorkingEmployeesAPI(tenantId: string, organizationId: s

return get<PaginationResponse<IWorkingEmployee>>(endpoint, { tenantId });
}


export function updateEmployeeAPI({ id, data }: { id: string, data: IUpdateEmployee }) {
return put<IUpdateEmployee>(`/employee/${id}`, data);
}
12 changes: 11 additions & 1 deletion apps/web/app/services/server/requests/employee.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PaginationResponse } from '@app/interfaces';
import { ICreateEmployee, IEmployee, IWorkingEmployee } from '@app/interfaces/IEmployee';
import { ICreateEmployee, IEmployee, IUpdateEmployee, IWorkingEmployee } from '@app/interfaces/IEmployee';
import { serverFetch } from '../fetch';
import qs from 'qs';

Expand Down Expand Up @@ -29,3 +29,13 @@ export function getOrganizationEmployees(bearer_token: string, tenantId: string,
tenantId
});
}

export function updateEmployees({ bearer_token, id, body }: { bearer_token: string, id: string, body: IUpdateEmployee }) {
return serverFetch<IEmployee>({
path: `/employee/${id}`,
method: 'PUT',
bearer_token,
body

})
}
8 changes: 7 additions & 1 deletion apps/web/app/stores/employee.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IWorkingEmployee } from '@app/interfaces';
import { IUpdateEmployee, IWorkingEmployee } from '@app/interfaces';
import { atom } from 'recoil';

export const workingEmployeesState = atom<IWorkingEmployee[]>({
Expand All @@ -10,3 +10,9 @@ export const workingEmployeesEmailState = atom<string[]>({
key: 'workingEmployeesEmailState',
default: []
});


export const employeeUpdateState = atom<IUpdateEmployee>({
key: 'employeeUpdateState',
default: null!,
})
4 changes: 2 additions & 2 deletions apps/web/lib/components/avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ export function Avatar({
height: size,
...(backgroundColor
? {
backgroundColor
}
backgroundColor
}
: {})
}}
>
Expand Down
3 changes: 1 addition & 2 deletions apps/web/lib/settings/member-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ export const MemberTable = ({ members }: { members: OT_Member[] }) => {
const handleEdit = (member: OT_Member) => {
setEditMember(member);
};

const handelNameChange = useCallback(
(event: ChangeEvent<HTMLInputElement>) => {
const name = event.target.value || '';
Expand Down Expand Up @@ -210,7 +209,7 @@ export const MemberTable = ({ members }: { members: OT_Member[] }) => {
<MemberTableStatus status={member.employee.isActive ? 'Member' : 'Suspended'} />
</td>
<td className="flex items-center justify-center py-4">
<TableActionPopover member={member} handleEdit={handleEdit} />
<TableActionPopover member={member} handleEdit={handleEdit} status='settings' />
</td>
</tr>
))}
Expand Down
Loading

0 comments on commit 6b582e8

Please sign in to comment.