diff --git a/apps/web/app/[locale]/profile/[memberId]/page.tsx b/apps/web/app/[locale]/profile/[memberId]/page.tsx index 11928f482..a0e99201b 100644 --- a/apps/web/app/[locale]/profile/[memberId]/page.tsx +++ b/apps/web/app/[locale]/profile/[memberId]/page.tsx @@ -12,23 +12,24 @@ import { ArrowLeft } from 'lib/components/svgs'; import { TaskFilter, Timer, TimerStatus, UserProfileTask, getTimerStatusValue, useTaskFilter } from 'lib/features'; import { MainHeader, MainLayout } from 'lib/layout'; import Link from 'next/link'; -import { useMemo, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { useTranslations } from 'next-intl'; import stc from 'string-to-color'; import { useRecoilValue } from 'recoil'; import { fullWidthState } from '@app/stores/fullWidth'; -import { ActivityFilters } from '@app/constants'; import { ScreenshootTab } from 'lib/features/activity/screenshoots'; import { AppsTab } from 'lib/features/activity/apps'; import { VisitedSitesTab } from 'lib/features/activity/visited-sites'; -const Profile = ({ params }: { params: { memberId: string } }) => { +type FilterTab = 'Tasks' | 'Screenshots' | 'Apps' | 'Visited Sites'; + +const Profile = React.memo(function ProfilePage({ params }: { params: { memberId: string } }) { const profile = useUserProfilePage(); const { user } = useAuthenticateUser(); const { isTrackingEnabled, activeTeam } = useOrganizationTeams(); const fullWidth = useRecoilValue(fullWidthState); - const [activityFilter, setActivityFilter] = useState(ActivityFilters.TASKS); + const [activityFilter, setActivityFilter] = useState('Tasks'); const hook = useTaskFilter(profile); const canSeeActivity = profile.userProfile?.id === user?.id || user?.role?.name?.toUpperCase() == 'MANAGER'; @@ -39,11 +40,23 @@ const Profile = ({ params }: { params: { memberId: string } }) => { { title: JSON.parse(t('pages.profile.BREADCRUMB')) || '', href: `/profile/${params.memberId}` } ]; - console.log({ activityFilter }); + const activityScreens = { + Tasks: , + Screenshots: , + Apps: , + 'Visited Sites': + }; const profileIsAuthUser = useMemo(() => profile.isAuthUser, [profile.isAuthUser]); const hookFilterType = useMemo(() => hook.filterType, [hook.filterType]); + const changeActivityFilter = useCallback( + (filter: FilterTab) => { + setActivityFilter(filter); + }, + [setActivityFilter] + ); + return ( <> @@ -80,7 +93,7 @@ const Profile = ({ params }: { params: { memberId: string } }) => { {hook.tab == 'worked' && canSeeActivity && (
- {Object.values(ActivityFilters).map((filter: ActivityFilters, i) => ( + {Object.keys(activityScreens).map((filter, i) => (
{i !== 0 && }
{ 'text-gray-500', activityFilter == filter && 'text-black dark:text-white' )} - onClick={() => setActivityFilter(filter)} + onClick={() => changeActivityFilter(filter as FilterTab)} > {filter}
@@ -99,22 +112,12 @@ const Profile = ({ params }: { params: { memberId: string } }) => { )} - {hook.tab == 'worked' && activityFilter == ActivityFilters.TASKS ? ( - - ) : hook.tab == 'worked' && canSeeActivity && activityFilter == ActivityFilters.SCREENSHOOTS ? ( - - ) : hook.tab == 'worked' && canSeeActivity && activityFilter == ActivityFilters.APPS ? ( - - ) : hook.tab == 'worked' && canSeeActivity && activityFilter == ActivityFilters.VISITED_SITES ? ( - - ) : ( - - )} + {activityScreens[activityFilter] ?? null} ); -}; +}); function UserProfileDetail({ member }: { member?: OT_Member }) { const user = useMemo(() => member?.employee.user, [member?.employee.user]); diff --git a/apps/web/app/[locale]/task/[id]/page.tsx b/apps/web/app/[locale]/task/[id]/page.tsx index b285b4610..7541a3788 100644 --- a/apps/web/app/[locale]/task/[id]/page.tsx +++ b/apps/web/app/[locale]/task/[id]/page.tsx @@ -84,7 +84,7 @@ const TaskDetails = () => { {/* */} {/* */} - {/* */} + {/* */}
diff --git a/apps/web/app/api/organization-projects/[id]/route.ts b/apps/web/app/api/organization-projects/[id]/route.ts index 88a982d9c..0b2242eb3 100644 --- a/apps/web/app/api/organization-projects/[id]/route.ts +++ b/apps/web/app/api/organization-projects/[id]/route.ts @@ -9,14 +9,15 @@ export async function PUT(req: Request, { params }: { params: { id: string } }) if (!user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); const { id } = params; + const body = await req.json(); - return $res( - await editOrganizationProjectsRequest({ - bearer_token: access_token, - id, - datas: body, - tenantId - }) - ); + const response = await editOrganizationProjectsRequest({ + bearer_token: access_token, + id, + datas: body, + tenantId + }); + + return $res(response.data); } diff --git a/apps/web/app/api/organization-projects/setting/[id]/route.ts b/apps/web/app/api/organization-projects/setting/[id]/route.ts index 93c6816d2..830715142 100644 --- a/apps/web/app/api/organization-projects/setting/[id]/route.ts +++ b/apps/web/app/api/organization-projects/setting/[id]/route.ts @@ -10,15 +10,13 @@ export async function PUT(req: Request, { params }: { params: { id: string } }) const { id } = params; const body = await req.json(); - switch (req.method) { - case 'PUT': - return $res( - await editOrganizationProjectsSettingsRequest({ - bearer_token: access_token, - id, - datas: body, - tenantId - }) - ); - } + + const response = await editOrganizationProjectsSettingsRequest({ + bearer_token: access_token, + id, + datas: body, + tenantId + }); + + $res(response.data); } diff --git a/apps/web/app/api/organization-team-employee/[id]/active-task/route.ts b/apps/web/app/api/organization-team-employee/[id]/active-task/route.ts index fddf8fbd6..5857a8804 100644 --- a/apps/web/app/api/organization-team-employee/[id]/active-task/route.ts +++ b/apps/web/app/api/organization-team-employee/[id]/active-task/route.ts @@ -12,13 +12,13 @@ export async function PUT(req: Request, { params }: { params: { id: string } }) const body = (await req.json()) as IOrganizationTeamEmployeeUpdate; if (id) { - return $res( - await updateOrganizationTeamEmployeeActiveTaskRequest({ - id: id as string, - bearer_token: access_token, - tenantId, - body - }) - ); + const response = await updateOrganizationTeamEmployeeActiveTaskRequest({ + id: id as string, + bearer_token: access_token, + tenantId, + body + }); + + return $res(response.data); } } diff --git a/apps/web/app/api/organization-team-employee/[id]/route.ts b/apps/web/app/api/organization-team-employee/[id]/route.ts index 654aebb94..1d5b2e580 100644 --- a/apps/web/app/api/organization-team-employee/[id]/route.ts +++ b/apps/web/app/api/organization-team-employee/[id]/route.ts @@ -15,15 +15,15 @@ export async function PUT(req: Request, { params }: { params: { id: string } }) const { id } = params; + const response = await updateOrganizationTeamEmployeeRequest({ + id: id as string, + bearer_token: access_token, + tenantId, + body: body + }); + if (id) { - return $res( - await updateOrganizationTeamEmployeeRequest({ - id: id as string, - bearer_token: access_token, - tenantId, - body: body - }) - ); + return $res(response.data); } } @@ -37,16 +37,16 @@ export async function DELETE(req: Request, { params }: { params: { id: string } const { employeeId } = searchParams as unknown as { employeeId: string }; const { id } = params; + const response = await deleteOrganizationTeamEmployeeRequest({ + id: id as string, + bearer_token: access_token, + tenantId, + organizationId, + employeeId: employeeId as string, + organizationTeamId: teamId + }); + if (id) { - return $res( - await deleteOrganizationTeamEmployeeRequest({ - id: id as string, - bearer_token: access_token, - tenantId, - organizationId, - employeeId: employeeId as string, - organizationTeamId: teamId - }) - ); + return $res(response.data); } } diff --git a/apps/web/app/api/role-permissions/[id]/route.ts b/apps/web/app/api/role-permissions/[id]/route.ts index 4dea0195c..6ceb4941c 100644 --- a/apps/web/app/api/role-permissions/[id]/route.ts +++ b/apps/web/app/api/role-permissions/[id]/route.ts @@ -2,7 +2,7 @@ import { authenticatedGuard } from '@app/services/server/guards/authenticated-gu import { getRolePermissionsRequest, updateRolePermissionRequest } from '@app/services/server/requests'; import { NextResponse } from 'next/server'; -export async function GET(req: Request, { params }: { params: { id: string } }) { +export async function GET(req: Request, { params }: { params: { id: string } }) { const res = new NextResponse(); const { $res, user, access_token, tenantId } = await authenticatedGuard(req, res); @@ -10,15 +10,13 @@ export async function GET(req: Request, { params }: { params: { id: string } }) const { id } = params; - return $res( - ( - await getRolePermissionsRequest({ - bearer_token: access_token, - tenantId, - roleId: id as string - }) - ).data - ); + const response = await getRolePermissionsRequest({ + bearer_token: access_token, + tenantId, + roleId: id as string + }); + + return $res(response.data); } export async function PUT(req: Request) { @@ -29,13 +27,11 @@ export async function PUT(req: Request) { const body = await req.json(); - return $res( - ( - await updateRolePermissionRequest({ - bearer_token: access_token, - tenantId, - data: body - }) - ).data - ); + const response = await updateRolePermissionRequest({ + bearer_token: access_token, + tenantId, + data: body + }); + + return $res(response.data); } diff --git a/apps/web/app/api/roles/route.ts b/apps/web/app/api/roles/route.ts index cc4a1fe87..39d7a8761 100644 --- a/apps/web/app/api/roles/route.ts +++ b/apps/web/app/api/roles/route.ts @@ -9,32 +9,30 @@ export async function GET(req: Request) { const { $res, user, access_token, tenantId } = await authenticatedGuard(req, res); if (!user) return $res('unauthorized'); - return $res( - ( - await getRolesRequest({ - bearer_token: access_token, - tenantId - }) - ).data - ); + const response = await getRolesRequest({ + bearer_token: access_token, + tenantId + }); + + return $res(response.data); } export async function POST(req: Request) { const res = new NextResponse(); const { $res, user, access_token, tenantId } = await authenticatedGuard(req, res); + const body = (await req.json()) as IRole; + if (!user) return $res('unauthorized'); - return $res( - ( - await createRoleRequest({ - bearer_token: access_token, - tenantId, - data: { - ...body, - tenantId - } - }) - ).data - ); + const response = await createRoleRequest({ + bearer_token: access_token, + tenantId, + data: { + ...body, + tenantId + } + }); + + return $res(response.data); } diff --git a/apps/web/app/hooks/features/useOrganizationTeams.ts b/apps/web/app/hooks/features/useOrganizationTeams.ts index 79d11a0da..cab8fa04a 100644 --- a/apps/web/app/hooks/features/useOrganizationTeams.ts +++ b/apps/web/app/hooks/features/useOrganizationTeams.ts @@ -213,14 +213,14 @@ export function useOrganizationTeams() { const setActiveTeam = useCallback( (team: (typeof teams)[0]) => { - setActiveTeamIdCookie(team.id); - setOrganizationIdCookie(team.organizationId); + setActiveTeamIdCookie(team?.id); + setOrganizationIdCookie(team?.organizationId); // This must be called at the end (Update store) - setActiveTeamId(team.id); + setActiveTeamId(team?.id); // Set Project Id to cookie // TODO: Make it dynamic when we add Dropdown in Navbar - if (team && team.projects && team.projects.length) { + if (team && team?.projects && team.projects.length) { setActiveProjectIdCookie(team.projects[0].id); } }, diff --git a/apps/web/app/hooks/features/useTimeDailyActivity.ts b/apps/web/app/hooks/features/useTimeDailyActivity.ts index dff66b892..ee2509123 100644 --- a/apps/web/app/hooks/features/useTimeDailyActivity.ts +++ b/apps/web/app/hooks/features/useTimeDailyActivity.ts @@ -2,32 +2,34 @@ import { useCallback, useEffect } from 'react'; import { useQuery } from '../useQuery'; -import { useRecoilState } from 'recoil'; -import { timeAppVisitedDetail, timeAppsState, timeVisitedSitesState } from '@app/stores/time-slot'; +import { useRecoilState, useRecoilValue } from 'recoil'; +import { timeAppsState, timeVisitedSitesState } from '@app/stores/time-slot'; import moment from 'moment'; import { useAuthenticateUser } from './useAuthenticateUser'; import { getTimerDailyRequestAPI } from '@app/services/client/api'; -import { useUserProfilePage } from './useUserProfilePage'; +import { activityTypeState } from '@app/stores/activity-type'; -export function useTimeDailyActivity(type: string, id?: string) { - const profile = useUserProfilePage(); +export function useTimeDailyActivity(type?: string) { const { user } = useAuthenticateUser(); const [visitedApps, setVisitedApps] = useRecoilState(timeAppsState); - const [visitedAppDetail, setVisitedAppDetail] = useRecoilState(timeAppVisitedDetail); + const activityFilter = useRecoilValue(activityTypeState); const [visitedSites, setVisitedSites] = useRecoilState(timeVisitedSitesState); const { loading, queryCall } = useQuery(getTimerDailyRequestAPI); const getVisitedApps = useCallback( - ({ title }: { title?: string }) => { + (title?: string) => { const todayStart = moment().startOf('day').toDate(); const todayEnd = moment().endOf('day').toDate(); - const employeeId = id ?? profile.member?.employeeId ?? ''; - if (profile.userProfile?.id === user?.id || user?.role?.name?.toUpperCase() == 'MANAGER') { + const employeeId = activityFilter.member ? activityFilter.member?.employeeId : user?.employee?.id; + if ( + activityFilter.member?.employeeId === user?.employee.id || + user?.role?.name?.toUpperCase() == 'MANAGER' + ) { queryCall({ tenantId: user?.tenantId ?? '', organizationId: user?.employee.organizationId ?? '', - employeeId: employeeId, + employeeId: employeeId ?? '', todayEnd, type, todayStart, @@ -36,37 +38,26 @@ export function useTimeDailyActivity(type: string, id?: string) { .then((response) => { if (response.data) { // @ts-ignore - if (title) setVisitedAppDetail(response.data[0]); - else if (type == 'APP') setVisitedApps(response.data); + // if (title) setVisitedAppDetail(response.data[0]); + if (type == 'APP') setVisitedApps(response.data); else setVisitedSites(response.data); } }) .catch((err) => console.log(err)); } }, - [ - profile.member?.employeeId, - profile.userProfile?.id, - user?.id, - user?.role?.name, - user?.tenantId, - user?.employee.organizationId, - queryCall, - type, - setVisitedAppDetail, - setVisitedApps, - setVisitedSites - ] + // eslint-disable-next-line react-hooks/exhaustive-deps + [queryCall, type] ); useEffect(() => { - getVisitedApps({}); - }, [user, getVisitedApps]); + getVisitedApps(); + }, [getVisitedApps]); return { visitedApps, visitedSites, - visitedAppDetail, + // visitedAppDetail, getVisitedApps, loading }; diff --git a/apps/web/app/hooks/features/useTimeSlot.ts b/apps/web/app/hooks/features/useTimeSlot.ts index 949c95aed..54deda3f3 100644 --- a/apps/web/app/hooks/features/useTimeSlot.ts +++ b/apps/web/app/hooks/features/useTimeSlot.ts @@ -2,16 +2,18 @@ import { useCallback, useEffect } from 'react'; import { useQuery } from '../useQuery'; -import { useRecoilState } from 'recoil'; +import { useRecoilState, useRecoilValue } from 'recoil'; import { timeSlotsState } from '@app/stores/time-slot'; import moment from 'moment'; import { useAuthenticateUser } from './useAuthenticateUser'; import { deleteTimerLogsRequestAPI, getTimerLogsRequestAPI } from '@app/services/client/api'; import { useUserProfilePage } from './useUserProfilePage'; +import { activityTypeState } from '@app/stores/activity-type'; -export function useTimeSlots(id?: string) { +export function useTimeSlots(hasFilter?: boolean) { const { user } = useAuthenticateUser(); const [timeSlots, setTimeSlots] = useRecoilState(timeSlotsState); + const activityFilter = useRecoilValue(activityTypeState); const profile = useUserProfilePage(); const { loading, queryCall } = useQuery(getTimerLogsRequestAPI); @@ -20,8 +22,8 @@ export function useTimeSlots(id?: string) { const getTimeSlots = useCallback(() => { const todayStart = moment().startOf('day').toDate(); const todayEnd = moment().endOf('day').toDate(); - const employeeId = id ? id : profile.member?.employeeId ; - if ( profile.userProfile?.id === user?.id || user?.role?.name?.toUpperCase() == 'MANAGER') { + const employeeId = activityFilter.member ? activityFilter.member?.employeeId : user?.employee?.id; + if (activityFilter.member?.employeeId === user?.employee.id || user?.role?.name?.toUpperCase() == 'MANAGER') { queryCall({ tenantId: user?.tenantId ?? '', organizationId: user?.employee.organizationId ?? '', @@ -35,17 +37,8 @@ export function useTimeSlots(id?: string) { } }); } - }, [ - id, - profile.member?.employeeId, - profile.userProfile?.id, - user?.id, - user?.role?.name, - user?.tenantId, - user?.employee.organizationId, - queryCall, - setTimeSlots - ]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [hasFilter, activityFilter.member?.employeeId, profile.member?.employeeId, user?.id, queryCall, setTimeSlots]); const deleteTimeSlots = useCallback( (ids: string[]) => { @@ -63,7 +56,7 @@ export function useTimeSlots(id?: string) { useEffect(() => { getTimeSlots(); - }, [user, getTimeSlots]); + }, [getTimeSlots]); return { timeSlots, diff --git a/apps/web/app/hooks/features/useUserDetails.ts b/apps/web/app/hooks/features/useUserDetails.ts new file mode 100644 index 000000000..e624a71da --- /dev/null +++ b/apps/web/app/hooks/features/useUserDetails.ts @@ -0,0 +1,66 @@ +'use client'; + +import { ITeamTask } from '@app/interfaces'; +import { useCallback, useEffect } from 'react'; +import { useAuthenticateUser } from './useAuthenticateUser'; +import { useAuthTeamTasks } from './useAuthTeamTasks'; +import { useOrganizationTeams } from './useOrganizationTeams'; +import { useTaskStatistics } from './useTaskStatistics'; +import { useTeamTasks } from './useTeamTasks'; + +export function useUserDetails(memberId: string) { + const { activeTeam } = useOrganizationTeams(); + const { activeTeamTask, updateTask } = useTeamTasks(); + + const { user: auth } = useAuthenticateUser(); + const { getTasksStatsData } = useTaskStatistics(); + + const members = activeTeam?.members || []; + + const matchUser = members.find((m) => { + return m.employee.userId === memberId; + }); + + const isAuthUser = auth?.employee?.userId === memberId; + + const activeUserTeamTask = isAuthUser ? activeTeamTask : matchUser?.lastWorkedTask; + + const userProfile = isAuthUser ? auth : matchUser?.employee.user; + + const employeeId = isAuthUser ? auth?.employee?.id : matchUser?.employeeId; + + /* Filtering the tasks */ + const tasksGrouped = useAuthTeamTasks(userProfile); + + useEffect(() => { + if (employeeId) { + getTasksStatsData(employeeId); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [employeeId]); + + const assignTask = useCallback( + (task: ITeamTask) => { + if (!matchUser?.employeeId) { + return Promise.resolve(); + } + + return updateTask({ + ...task, + members: [...task.members, (matchUser?.employeeId ? { id: matchUser?.employeeId } : {}) as any] + }); + }, + [updateTask, matchUser] + ); + + return { + isAuthUser, + activeUserTeamTask, + userProfile, + tasksGrouped, + member: matchUser, + assignTask + }; +} + +export type I_UserProfilePage = ReturnType; diff --git a/apps/web/app/hooks/features/useUserProfilePage.ts b/apps/web/app/hooks/features/useUserProfilePage.ts index 790a739b0..4a03c87b5 100644 --- a/apps/web/app/hooks/features/useUserProfilePage.ts +++ b/apps/web/app/hooks/features/useUserProfilePage.ts @@ -42,7 +42,8 @@ export function useUserProfilePage() { if (employeeId) { getTasksStatsData(employeeId); } - }, [getTasksStatsData, employeeId]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [employeeId]); const assignTask = useCallback( (task: ITeamTask) => { diff --git a/apps/web/app/interfaces/IActivityFilter.ts b/apps/web/app/interfaces/IActivityFilter.ts new file mode 100644 index 000000000..df7d343b9 --- /dev/null +++ b/apps/web/app/interfaces/IActivityFilter.ts @@ -0,0 +1,9 @@ +import { OT_Member } from './IOrganizationTeam'; + +export interface IActivityFilter { + type: 'DATE' | 'TICKET'; + member: OT_Member | null; + taskId?: string; + dateStart?: Date; + dateStop?: Date; +} diff --git a/apps/web/app/services/client/api/activity/activity.ts b/apps/web/app/services/client/api/activity/activity.ts index f64309313..755b37019 100644 --- a/apps/web/app/services/client/api/activity/activity.ts +++ b/apps/web/app/services/client/api/activity/activity.ts @@ -16,7 +16,7 @@ export async function getTimerDailyRequestAPI({ employeeId: string; todayEnd: Date; todayStart: Date; - type: string; + type?: string | undefined; title?: string; }) { const params: { @@ -25,18 +25,19 @@ export async function getTimerDailyRequestAPI({ 'employeeIds[0]': string; startDate: string; endDate: string; - 'types[0]': string; + 'types[0]'?: string; 'title[0]'?: string; } = { tenantId: tenantId, organizationId: organizationId, 'employeeIds[0]': employeeId, startDate: todayStart.toISOString(), - endDate: todayEnd.toISOString(), - 'types[0]': type + endDate: todayEnd.toISOString() }; + if (type) params['types[0]'] = type; if (title) params['title[0]'] = title; const query = new URLSearchParams(params); + console.log('QUERY', query); const endpoint = GAUZY_API_BASE_SERVER_URL.value ? `/timesheet/activity/daily?${query.toString()}` : `/timer/daily?${query.toString()}`; diff --git a/apps/web/app/services/client/api/organization-projects.ts b/apps/web/app/services/client/api/organization-projects.ts index 84f4c5d64..1f2d7243f 100644 --- a/apps/web/app/services/client/api/organization-projects.ts +++ b/apps/web/app/services/client/api/organization-projects.ts @@ -1,18 +1,14 @@ import { IProject } from '@app/interfaces'; -import api from '../axios'; +import { put } from '../axios'; export function editOrganizationProjectSettingAPI(id: string, data: any, tenantId?: string) { - return api.put(`/organization-projects/setting/${id}`, data, { - headers: { - 'Tenant-Id': tenantId - } + return put(`/organization-projects/setting/${id}`, data, { + tenantId }); } export function editOrganizationProjectAPI(id: string, data: any, tenantId?: string) { - return api.put(`/organization-projects/${id}`, data, { - headers: { - 'Tenant-Id': tenantId - } + return put(`/organization-projects/${id}`, data, { + tenantId }); } diff --git a/apps/web/app/services/client/api/organization-team-employee.ts b/apps/web/app/services/client/api/organization-team-employee.ts index e14328d1a..e4210ae25 100644 --- a/apps/web/app/services/client/api/organization-team-employee.ts +++ b/apps/web/app/services/client/api/organization-team-employee.ts @@ -1,7 +1,7 @@ import { IOrganizationTeamEmployeeUpdate } from '@app/interfaces'; -import { CreateResponse } from '@app/interfaces/IDataResponse'; import { IOrganizationTeam } from '@app/interfaces/IOrganizationTeam'; -import api from '../axios'; +import { deleteApi, put } from '../axios'; +import { getActiveTeamIdCookie } from '@app/helpers'; export function deleteOrganizationEmployeeTeamAPI({ id, @@ -14,21 +14,20 @@ export function deleteOrganizationEmployeeTeamAPI({ organizationId: string; tenantId: string; }) { - return api.delete>( - `/organization-team-employee/${id}?tenantId=${tenantId}&employeeId=${employeeId}&organizationId=${organizationId}` + const teamId = getActiveTeamIdCookie(); + + return deleteApi( + `/organization-team-employee/${id}?tenantId=${tenantId}&employeeId=${employeeId}&organizationId=${organizationId}&organizationTeamId=${teamId}` ); } export function updateOrganizationEmployeeTeamAPI(id: string, data: Partial) { - return api.put>(`/organization-team-employee/${id}`, data); + return put(`/organization-team-employee/${id}`, data); } export function updateOrganizationTeamEmployeeActiveTaskAPI( id: string, data: Partial ) { - return api.put>( - `/organization-team-employee/${id}/active-task`, - data - ); + return put(`/organization-team-employee/${id}/active-task`, data); } diff --git a/apps/web/app/services/client/api/role-permissions.ts b/apps/web/app/services/client/api/role-permissions.ts index c55ee84be..127d8f0ad 100644 --- a/apps/web/app/services/client/api/role-permissions.ts +++ b/apps/web/app/services/client/api/role-permissions.ts @@ -1,10 +1,23 @@ import { IRolePermissions, PaginationResponse } from '@app/interfaces/'; -import api from '../axios'; +import { get, put } from '../axios'; +import { getTenantIdCookie } from '@app/helpers'; export function getRolePermissionAPI(id: string) { - return api.get>(`/role-permissions/${id}`); + const tenantId = getTenantIdCookie(); + + const params = { + data: JSON.stringify({ + findInput: { + roleId: id, + tenantId + } + }) + }; + const query = new URLSearchParams(params); + + return get>(`/role-permissions/${id}?${query.toString()}`); } export function updateRolePermissionAPI(data: IRolePermissions) { - return api.put(`/role-permissions/${data.id}`, data); + return put(`/role-permissions/${data.id}`, data); } diff --git a/apps/web/app/services/client/api/roles.ts b/apps/web/app/services/client/api/roles.ts index 5e3999383..c1e88d5ab 100644 --- a/apps/web/app/services/client/api/roles.ts +++ b/apps/web/app/services/client/api/roles.ts @@ -1,12 +1,12 @@ import { IRole, PaginationResponse } from '@app/interfaces'; -import api from '../axios'; +import api, { get, post } from '../axios'; export function getRolesAPI() { - return api.get>('/roles'); + return get>('/roles'); } export function createRoleAPI(data: IRole) { - return api.post('/roles', data); + return post('/roles', data); } export function deleteRoleAPI(id: string) { diff --git a/apps/web/app/services/client/axios.ts b/apps/web/app/services/client/axios.ts index f355fc9a8..a940e724f 100644 --- a/apps/web/app/services/client/axios.ts +++ b/apps/web/app/services/client/axios.ts @@ -120,11 +120,11 @@ function post(url: string, data?: Record | FormData, config?: AP const { directAPI = true } = config || {}; if (baseURL && directAPI && data && !(data instanceof FormData)) { - if (!data.tenantId) { + if (!data.tenantId && data.tenantId !== null) { data.tenantId = tenantId; } - if (!data.organizationId) { + if (!data.organizationId && data.organizationId !== null) { data.organizationId = organizationId; } } diff --git a/apps/web/app/stores/activity-type.ts b/apps/web/app/stores/activity-type.ts new file mode 100644 index 000000000..2f7d01ea5 --- /dev/null +++ b/apps/web/app/stores/activity-type.ts @@ -0,0 +1,10 @@ +import { IActivityFilter } from '@app/interfaces/IActivityFilter'; +import { atom } from 'recoil'; + +export const activityTypeState = atom({ + key: 'activityTypeState', + default: { + type: 'DATE', + member: null + } +}); diff --git a/apps/web/components/ui/svgs/expand.tsx b/apps/web/components/ui/svgs/expand.tsx new file mode 100644 index 000000000..98819f67a --- /dev/null +++ b/apps/web/components/ui/svgs/expand.tsx @@ -0,0 +1,29 @@ +export function ExpandIcon({ width, height, fill = '#8C7AE4' }: { width: number; height: number; fill?: string }) { + return ( + + + + ); +} + +export function CollapseUpIcon({ width, height, fill = '#8C7AE4' }: { width: number; height: number; fill?: string }) { + return ( + + + + + + + + + + + + + ); +} diff --git a/apps/web/lib/features/activity/apps.tsx b/apps/web/lib/features/activity/apps.tsx index c83eca996..f274d5126 100644 --- a/apps/web/lib/features/activity/apps.tsx +++ b/apps/web/lib/features/activity/apps.tsx @@ -5,11 +5,10 @@ import { useTranslations } from 'next-intl'; import AppVisitedItem from './components/app-visited-Item'; // import { AppVisitedModal } from './components/app-visited-details'; -export function AppsTab({ id}: {id?: string}) { - const { visitedApps, loading } = useTimeDailyActivity('APP', id); +export function AppsTab() { + const { visitedApps, loading } = useTimeDailyActivity(); const t = useTranslations(); const apps = groupAppsByHour(visitedApps); - console.log("INTO APP TAB"); return (
{/* TODO: Filters components */}
@@ -30,11 +29,7 @@ export function AppsTab({ id}: {id?: string}) { {app.apps?.map((item, i) => (
{/* */} - + {/* */}
))} diff --git a/apps/web/lib/features/activity/screenshoots.tsx b/apps/web/lib/features/activity/screenshoots.tsx index 0c45e14b2..6a1984030 100644 --- a/apps/web/lib/features/activity/screenshoots.tsx +++ b/apps/web/lib/features/activity/screenshoots.tsx @@ -48,9 +48,8 @@ export function ScreenshootTab() { ); } - -export function ScreenshootTeamTab({ id}: {id:string}) { - const { timeSlots, loading } = useTimeSlots(id); +export function ScreenshootTeamTab() { + const { timeSlots, loading } = useTimeSlots(true); const t = useTranslations(); return ( @@ -61,7 +60,6 @@ export function ScreenshootTeamTab({ id}: {id:string}) { timeSlots={hourData.items} startedAt={hourData.startedAt} stoppedAt={hourData.stoppedAt} - /> ))} {timeSlots.length < 1 && !loading && ( diff --git a/apps/web/lib/features/activity/task-activity.tsx b/apps/web/lib/features/activity/task-activity.tsx new file mode 100644 index 000000000..32efad3c9 --- /dev/null +++ b/apps/web/lib/features/activity/task-activity.tsx @@ -0,0 +1,5 @@ +export function TaskActivity(){ + return ( +
+ ) +} \ No newline at end of file diff --git a/apps/web/lib/features/activity/visited-sites.tsx b/apps/web/lib/features/activity/visited-sites.tsx index b38a32506..c2e960ec1 100644 --- a/apps/web/lib/features/activity/visited-sites.tsx +++ b/apps/web/lib/features/activity/visited-sites.tsx @@ -3,9 +3,10 @@ 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'; +import React from 'react'; -export function VisitedSitesTab({ id }: { id?: string}) { - const { visitedSites, loading } = useTimeDailyActivity('URL', id); +export const VisitedSitesTab = React.memo(function VisitedSitesT() { + const { visitedSites, loading } = useTimeDailyActivity('URL'); const t = useTranslations(); const sites = groupAppsByHour(visitedSites); return ( @@ -47,4 +48,4 @@ export function VisitedSitesTab({ id }: { id?: string}) { )}
); -} +}); diff --git a/apps/web/lib/features/team/user-team-card/index.tsx b/apps/web/lib/features/team/user-team-card/index.tsx index dc1f88829..c4ed39c43 100644 --- a/apps/web/lib/features/team/user-team-card/index.tsx +++ b/apps/web/lib/features/team/user-team-card/index.tsx @@ -2,20 +2,22 @@ import { secondsToTime } from '@app/helpers'; import { useCollaborative, useTMCardTaskEdit, useTaskStatistics, useTeamMemberCard } from '@app/hooks'; -import { IClassName, IOrganizationTeamList } from '@app/interfaces'; +import { IClassName, IOrganizationTeamList, OT_Member } from '@app/interfaces'; import { timerSecondsState } from '@app/stores'; import { clsxm } from '@app/utils'; import { Card, HorizontalSeparator, InputField, Text, VerticalSeparator } from 'lib/components'; import { DraggerIcon } from 'lib/components/svgs'; import { TaskTimes, TodayWorkedTime } from 'lib/features'; import { useTranslations } from 'next-intl'; -import { useRecoilValue } from 'recoil'; +import { useRecoilValue, useSetRecoilState } from 'recoil'; import { TaskEstimateInfo } from './task-estimate'; import { TaskInfo } from './task-info'; import { UserInfo } from './user-info'; import { UserTeamCardMenu } from './user-team-card-menu'; import React from 'react'; import UserTeamActivity from './user-team-card-activity'; +import { CollapseUpIcon, ExpandIcon } from '@components/ui/svgs/expand'; +import { activityTypeState } from '@app/stores/activity-type'; type IUserTeamCard = { active?: boolean; @@ -49,9 +51,19 @@ export function UserTeamCard({ const { collaborativeSelect, user_selected, onUserSelect } = useCollaborative(memberInfo.memberUser); const seconds = useRecoilValue(timerSecondsState); + const setActivityFilter = useSetRecoilState(activityTypeState); const { activeTaskTotalStat, addSeconds } = useTaskStatistics(seconds); const [showActivity, setShowActivity] = React.useState(false); + const showActivityFilter = (type: 'DATE' | 'TICKET', member: OT_Member | null) => { + setShowActivity((prev) => !prev); + setActivityFilter((prev) => ({ + ...prev, + type, + member + })); + }; + let totalWork = <>; if (memberInfo.isAuthUser) { const { h, m } = secondsToTime( @@ -111,8 +123,8 @@ export function UserTeamCard({ )} >
-
- +
+
{/* Show user name, email and image */} @@ -120,12 +132,24 @@ export function UserTeamCard({ {/* Task information */} - +
+ +

showActivityFilter('TICKET', memberInfo.member ?? null)} + > + {!showActivity ? ( + + ) : ( + + )} +

+
{/* TaskTimes */} @@ -148,17 +172,23 @@ export function UserTeamCard({ {/* TodayWorkedTime */} -
setShowActivity((prev) => !prev)}> - +
+ +

showActivityFilter('DATE', memberInfo.member ?? null)} + className="flex items-center w-8 h-8 border dark:border-gray-800 rounded justify-center cursor-pointer text-center" + > + {!showActivity ? ( + + ) : ( + + )} +

{/* Card menu */}
{menu}
- + { - const id = member?.employeeId ?? ''; - const { timeSlots } = useTimeSlots(id); +const UserTeamActivity = ({ showActivity }: { showActivity: boolean }) => { + const { timeSlots } = useTimeSlots(true); const t = useTranslations(); const activityPercent = timeSlots.reduce((acc, el) => acc + el.percentage, 0) / timeSlots.length; @@ -34,42 +34,45 @@ const UserTeamActivity = ({ member, showActivity }: { member: OT_Member | undefi
{t('timer.TIME_ACTIVITY')} -

{activityPercent ? activityPercent.toFixed(2): "00"} %

+

+ {activityPercent ? activityPercent.toFixed(2) : '00'} % +

-
- {Object.values(ActivityFilters) - .filter((el) => el !== 'Tasks') - .map((filter: string) => ( - - clsxm( - 'w-full rounded-lg py-2.5 text-sm font-medium leading-5', - ' focus:outline-none focus:ring-2', - selected - ? 'bg-white dark:bg-dark text-blue-700 shadow' - : ' hover:bg-white/[0.50]' - ) - } - > - {filter} - - ))} + {Object.values(ActivityFilters).map((filter: string) => ( + + clsxm( + 'w-full rounded-lg py-2.5 text-sm font-medium leading-5', + ' focus:outline-none focus:ring-2', + selected + ? 'bg-white dark:bg-dark text-blue-700 shadow' + : ' hover:bg-white/[0.50]' + ) + } + > + {filter} + + ))} - + {/* */} + + + + - + - + diff --git a/apps/web/lib/features/team/user-team-card/user-worked-task.tsx b/apps/web/lib/features/team/user-team-card/user-worked-task.tsx new file mode 100644 index 000000000..9f46000b4 --- /dev/null +++ b/apps/web/lib/features/team/user-team-card/user-worked-task.tsx @@ -0,0 +1,15 @@ +import { useUserDetails } from '@app/hooks/features/useUserDetails'; +import { activityTypeState } from '@app/stores/activity-type'; +import { useTaskFilter } from 'lib/features/task/task-filters'; +import { UserProfileTask } from 'lib/features/user-profile-tasks'; +import { useRecoilValue } from 'recoil'; + +export function UserWorkedTaskTab() { + const activityFilter = useRecoilValue(activityTypeState); + const profile = useUserDetails(activityFilter.member?.employeeId ?? ''); + const hook = useTaskFilter(profile); + + console.log({ hook, profile }); + + return ; +}