From aaf820c8d3c6e0bd1452e76ec5aea71860f6e1c1 Mon Sep 17 00:00:00 2001 From: desperado1802 Date: Wed, 25 Oct 2023 23:22:41 +0300 Subject: [PATCH] added functionality for non manager users to update active task --- .../hooks/features/useTeamMemberCard.ts | 209 +++++++------ .../services/hooks/features/useTeamTasks.ts | 275 ++++++++++-------- 2 files changed, 263 insertions(+), 221 deletions(-) diff --git a/apps/mobile/app/services/hooks/features/useTeamMemberCard.ts b/apps/mobile/app/services/hooks/features/useTeamMemberCard.ts index 6506605b1..5194965fb 100644 --- a/apps/mobile/app/services/hooks/features/useTeamMemberCard.ts +++ b/apps/mobile/app/services/hooks/features/useTeamMemberCard.ts @@ -1,14 +1,14 @@ -import { useCallback, useMemo, useRef, useState } from 'react'; -import { useSyncRef } from '../useSyncRef'; -import { useTeamTasks } from './useTeamTasks'; -import cloneDeep from 'lodash/cloneDeep'; -import { useStores } from '../../../models'; -import { IOrganizationTeamList } from '../../interfaces/IOrganizationTeam'; -import useAuthenticateUser from './useAuthentificateUser'; -import { useIsMemberManager } from './useIsMemberManager'; -import { ITeamTask } from '../../interfaces/ITask'; -import { Nullable } from '../../interfaces/hooks'; -import { useOrganizationTeam } from '../useOrganization'; +import { useCallback, useMemo, useRef, useState } from "react" +import { useSyncRef } from "../useSyncRef" +import { useTeamTasks } from "./useTeamTasks" +import cloneDeep from "lodash/cloneDeep" +import { useStores } from "../../../models" +import { IOrganizationTeamList } from "../../interfaces/IOrganizationTeam" +import useAuthenticateUser from "./useAuthentificateUser" +import { useIsMemberManager } from "./useIsMemberManager" +import { ITeamTask } from "../../interfaces/ITask" +import { Nullable } from "../../interfaces/hooks" +import { useOrganizationTeam } from "../useOrganization" /** * It returns a bunch of data about a team member, including whether or not the user is the team @@ -16,30 +16,37 @@ import { useOrganizationTeam } from '../useOrganization'; * @param {IOrganizationTeamList['members'][number] | undefined} member - * IOrganizationTeamList['members'][number] | undefined */ -export function useTeamMemberCard(member: IOrganizationTeamList['members'][number] | undefined) { +export function useTeamMemberCard(member: IOrganizationTeamList["members"][number] | undefined) { const { - TaskStore: { activeTaskId, setActiveTaskId, activeTask, teamTasks: tasks, tasksStatisticsState }, - teamStore: { activeTeam, activeTeamId } - } = useStores(); - const { updateTask, setActiveTeamTask, deleteEmployeeFromTasks } = useTeamTasks(); + TaskStore: { + activeTaskId, + setActiveTaskId, + setActiveTask, + activeTask, + teamTasks: tasks, + tasksStatisticsState, + }, + teamStore: { activeTeam, activeTeamId }, + } = useStores() + const { updateTask, setActiveTeamTask, deleteEmployeeFromTasks } = useTeamTasks() - const publicTeam = false; + const publicTeam = false - const { user: authUser, isTeamManager: isAuthTeamManager } = useAuthenticateUser(); + const { user: authUser, isTeamManager: isAuthTeamManager } = useAuthenticateUser() - const activeTeamTask = activeTask; + const activeTeamTask = activeTask - const { onUpdateOrganizationTeam } = useOrganizationTeam(); + const { onUpdateOrganizationTeam } = useOrganizationTeam() - const activeTeamRef = useSyncRef(activeTeam); + const activeTeamRef = useSyncRef(activeTeam) - const memberUser = member?.employee.user; + const memberUser = member?.employee.user // const memberUserRef = useSyncRef(memberUser); - const isAuthUser = member?.employee.userId === authUser?.id; - const { isTeamManager, isTeamCreator } = useIsMemberManager(memberUser); + const isAuthUser = member?.employee.userId === authUser?.id + const { isTeamManager, isTeamCreator } = useIsMemberManager(memberUser) - const memberTaskRef = useRef>(null); + const memberTaskRef = useRef>(null) const setActiveUserTask = useCallback( (task: ITeamTask | null) => { @@ -48,116 +55,121 @@ export function useTeamMemberCard(member: IOrganizationTeamList['members'][numbe // taskId: task.id, // userId: authUser.id, // }) - setActiveTaskId(task.id); + setActiveTaskId(task.id) } }, - [authUser] - ); + [authUser], + ) memberTaskRef.current = useMemo(() => { - let cTask; - let find; + let cTask + let find if (!member) { - return null; + return null } if (activeTaskId && isAuthUser) { - cTask = tasks.find((t) => activeTaskId === t.id || publicTeam); - find = cTask; + cTask = tasks.find((t) => activeTaskId === t.id || publicTeam) + find = cTask } else if (member.lastWorkedTask) { - cTask = tasks.find((t) => t.id === member.lastWorkedTask?.id); - find = cTask?.members.some((m) => m.id === member.employee.id); + cTask = tasks.find((t) => t.id === member.lastWorkedTask?.id) + find = cTask?.members.some((m) => m.id === member.employee.id) } else { - cTask = tasks.find((t) => t.members.some((m) => m.userId === member.employee.userId)); - find = cTask?.members.some((m) => m.id === member.employee.id); + cTask = tasks.find((t) => t.members.some((m) => m.userId === member.employee.userId)) + find = cTask?.members.some((m) => m.id === member.employee.id) } if (isAuthUser && member.lastWorkedTask && !activeTaskId) { - setActiveUserTask(member.lastWorkedTask); + setActiveUserTask(member.lastWorkedTask) + setActiveTask(member.lastWorkedTask) } else if (isAuthUser && find && cTask && !activeTaskId) { - setActiveUserTask(cTask); + setActiveUserTask(cTask) + setActiveTask(cTask) } - const responseTask = find ? cloneDeep(cTask) : null; + const responseTask = find ? cloneDeep(cTask) : null if (responseTask) { const taskStatistics = - tasksStatisticsState?.all.find((statistics) => statistics.id === responseTask.id) || []; - responseTask.totalWorkedTime = taskStatistics?.duration || 0; + tasksStatisticsState?.all.find((statistics) => statistics.id === responseTask.id) || + [] + responseTask.totalWorkedTime = taskStatistics?.duration || 0 } - return responseTask; - }, [activeTeamTask, isAuthUser, authUser, member, tasks, publicTeam]); + return responseTask + }, [activeTeamTask, activeTaskId, isAuthUser, authUser, member, tasks, publicTeam]) /** * Give the manager role to the member */ const makeMemberManager = useCallback(() => { - const employeeId = member?.employee?.id; + const employeeId = member?.employee?.id - if (!activeTeamRef.current || !employeeId) return; - const team = activeTeamRef.current; + if (!activeTeamRef.current || !employeeId) return + const team = activeTeamRef.current onUpdateOrganizationTeam({ id: activeTeamId, data: { ...activeTeam, managerIds: team.members - .filter((r) => r.role && r.role.name === 'MANAGER') + .filter((r) => r.role && r.role.name === "MANAGER") .map((r) => r.employee.id) - .concat(employeeId) - } - }); - }, [onUpdateOrganizationTeam, member, activeTeamRef]); + .concat(employeeId), + }, + }) + }, [onUpdateOrganizationTeam, member, activeTeamRef]) /** * remove manager role to the member */ const unMakeMemberManager = useCallback(() => { - const employeeId = member?.employee?.id; + const employeeId = member?.employee?.id - if (!activeTeamRef.current || !employeeId) return; - const team = activeTeamRef.current; + if (!activeTeamRef.current || !employeeId) return + const team = activeTeamRef.current onUpdateOrganizationTeam({ id: activeTeamId, data: { ...activeTeam, managerIds: team.members - .filter((r) => r.role && r.role.name === 'MANAGER') + .filter((r) => r.role && r.role.name === "MANAGER") .filter((r) => r.employee.id !== employeeId) .map((r) => r.employee.id) - .filter((value, index, array) => array.indexOf(value) === index) // To make the array Unique list of ids - } - }); - }, [onUpdateOrganizationTeam, member, activeTeamRef]); + .filter((value, index, array) => array.indexOf(value) === index), // To make the array Unique list of ids + }, + }) + }, [onUpdateOrganizationTeam, member, activeTeamRef]) /** * Remove member from team API call */ const removeMemberFromTeam = useCallback(() => { - const employeeId = member?.employee?.id; + const employeeId = member?.employee?.id - if (!activeTeamRef.current || !employeeId) return; - const team = activeTeamRef.current; + if (!activeTeamRef.current || !employeeId) return + const team = activeTeamRef.current - deleteEmployeeFromTasks(employeeId, team.id); // Unassign all the task + deleteEmployeeFromTasks(employeeId, team.id) // Unassign all the task onUpdateOrganizationTeam({ id: activeTeamId, data: { ...activeTeam, // remove from members - memberIds: team.members.filter((r) => r.employee.id !== employeeId).map((r) => r.employee.id), + memberIds: team.members + .filter((r) => r.employee.id !== employeeId) + .map((r) => r.employee.id), // remove from managers managerIds: team.members - .filter((r) => r.role && r.role.name === 'MANAGER') + .filter((r) => r.role && r.role.name === "MANAGER") .filter((r) => r.employee.id !== employeeId) - .map((r) => r.employee.id) - } - }); - }, [onUpdateOrganizationTeam, member, activeTeamRef]); + .map((r) => r.employee.id), + }, + }) + }, [onUpdateOrganizationTeam, member, activeTeamRef]) /** * Assign task to the member @@ -165,53 +177,56 @@ export function useTeamMemberCard(member: IOrganizationTeamList['members'][numbe const assignTask = useCallback( (task: ITeamTask) => { if (!member?.employeeId) { - return Promise.resolve(); + return Promise.resolve() } return updateTask( { ...task, - members: [...task.members, (member?.employeeId ? { id: member?.employeeId } : {}) as any] + members: [ + ...task.members, + (member?.employeeId ? { id: member?.employeeId } : {}) as any, + ], }, - task.id + task.id, ).then(() => { if (isAuthUser && !activeTeamTask) { - setActiveTeamTask(task); + setActiveTeamTask(task) } - }); + }) }, - [updateTask, member, isAuthUser, setActiveTeamTask, activeTeamTask] - ); + [updateTask, member, isAuthUser, setActiveTeamTask, activeTeamTask], + ) /** * Returns all tasks not assigned to the member */ const memberUnassignTasks = useMemo(() => { - if (!memberUser) return []; + if (!memberUser) return [] return tasks.filter((task) => { - return !task.members.some((m) => m.userId === memberUser.id); - }); - }, [tasks, memberUser, assignTask]); + return !task.members.some((m) => m.userId === memberUser.id) + }) + }, [tasks, memberUser, assignTask]) const unassignTask = useCallback( (task: ITeamTask) => { if (!member?.employeeId) { - return Promise.resolve(); + return Promise.resolve() } return updateTask( { ...task, - members: task.members.filter((m) => m.id !== member.employeeId) + members: task.members.filter((m) => m.id !== member.employeeId), }, - task.id + task.id, ).finally(() => { - isAuthUser && setActiveTeamTask(null); - }); + isAuthUser && setActiveTeamTask(null) + }) }, - [updateTask, member, isAuthUser, setActiveTeamTask] - ); + [updateTask, member, isAuthUser, setActiveTeamTask], + ) return { assignTask, @@ -227,14 +242,14 @@ export function useTeamMemberCard(member: IOrganizationTeamList['members'][numbe unMakeMemberManager, isTeamCreator, unassignTask, - isTeamOwner: activeTeam?.createdBy?.id === memberUser?.id - }; + isTeamOwner: activeTeam?.createdBy?.id === memberUser?.id, + } } export function useTMCardTaskEdit(task: Nullable) { - const [editMode, setEditMode] = useState(false); - const [estimateEditMode, setEstimateEditMode] = useState(false); - const [loading, setLoading] = useState(false); + const [editMode, setEditMode] = useState(false) + const [estimateEditMode, setEstimateEditMode] = useState(false) + const [loading, setLoading] = useState(false) return { editMode, @@ -243,13 +258,13 @@ export function useTMCardTaskEdit(task: Nullable) { estimateEditMode, setEstimateEditMode, loading, - setLoading - }; + setLoading, + } } -export type I_TMCardTaskEditHook = ReturnType; +export type I_TMCardTaskEditHook = ReturnType -export type I_TeamMemberCardHook = ReturnType; +export type I_TeamMemberCardHook = ReturnType // function useOrganizationTeams(): { // activeTeam: any // updateOrganizationTeam: any diff --git a/apps/mobile/app/services/hooks/features/useTeamTasks.ts b/apps/mobile/app/services/hooks/features/useTeamTasks.ts index e045865da..1c63e2cac 100644 --- a/apps/mobile/app/services/hooks/features/useTeamTasks.ts +++ b/apps/mobile/app/services/hooks/features/useTeamTasks.ts @@ -1,35 +1,42 @@ /* eslint-disable camelcase */ -import { useCallback, useEffect, useState } from 'react'; -import { useStores } from '../../../models'; -import useFetchAllTasks from '../../client/queries/task/tasks'; +import { useCallback, useEffect, useState } from "react" +import { useStores } from "../../../models" +import useFetchAllTasks from "../../client/queries/task/tasks" import { createTaskRequest, deleteEmployeeFromTasksRequest, deleteTaskRequest, - updateTaskRequest -} from '../../client/requests/tasks'; -import { ICreateTask, ITeamTask } from '../../interfaces/ITask'; -import { useSyncRef } from '../useSyncRef'; -import { useFirstLoad } from '../useFirstLoad'; -import isEqual from 'lodash/isEqual'; -import { useOrganizationTeam } from '../useOrganization'; + updateTaskRequest, +} from "../../client/requests/tasks" +import { ICreateTask, ITeamTask } from "../../interfaces/ITask" +import { useSyncRef } from "../useSyncRef" +import { useFirstLoad } from "../useFirstLoad" +import isEqual from "lodash/isEqual" +import { useOrganizationTeam } from "../useOrganization" export function useTeamTasks() { const { authenticationStore: { tenantId, organizationId, authToken, user }, teamStore: { activeTeam, activeTeamId }, - TaskStore: { teamTasks, setTeamTasks, setActiveTask, activeTaskId, setActiveTaskId, activeTask } - } = useStores(); + TaskStore: { + teamTasks, + setTeamTasks, + setActiveTask, + activeTaskId, + setActiveTaskId, + activeTask, + }, + } = useStores() - const { updateOrganizationTeamEmployeeActiveTask, currentUser } = useOrganizationTeam(); + const { updateOrganizationTeamEmployeeActiveTask, currentUser } = useOrganizationTeam() - const tasksRef = useSyncRef(teamTasks); - const [tasksFetching, setTasksFetching] = useState(false); - const [isUpdatingActiveTask, setIsUpdatingActiveTask] = useState(false); - const [createLoading, setCreateLoading] = useState(false); - const activeTeamRef = useSyncRef(activeTeam); + const tasksRef = useSyncRef(teamTasks) + const [tasksFetching, setTasksFetching] = useState(false) + const [isUpdatingActiveTask, setIsUpdatingActiveTask] = useState(false) + const [createLoading, setCreateLoading] = useState(false) + const activeTeamRef = useSyncRef(activeTeam) - const { firstLoad, firstLoadData: firstLoadTaskData } = useFirstLoad(); + const { firstLoad, firstLoadData: firstLoadTaskData } = useFirstLoad() // Query Hook const { @@ -37,17 +44,17 @@ export function useTeamTasks() { isSuccess, isFetching, isRefetching, - refetch - } = useFetchAllTasks({ tenantId, organizationId, authToken, activeTeamId }); + refetch, + } = useFetchAllTasks({ tenantId, organizationId, authToken, activeTeamId }) const deepCheckAndUpdateTasks = useCallback( (responseTasks: ITeamTask[], deepCheck?: boolean) => { if (responseTasks && responseTasks.length) { responseTasks.forEach((task) => { if (task.tags && task.tags?.length) { - task.label = task.tags[0].name; + task.label = task.tags[0].name } - }); + }) } /** @@ -58,15 +65,17 @@ export function useTeamTasks() { const latestActiveTeamTasks = responseTasks .filter((task) => { return task.teams.some((tm) => { - return tm.id === activeTeamRef.current?.id; - }); + return tm.id === activeTeamRef.current?.id + }) }) - .sort((a, b) => a.title.localeCompare(b.title)); + .sort((a, b) => a.title.localeCompare(b.title)) - const activeTeamTasks = tasksRef.current.slice().sort((a, b) => a.title.localeCompare(b.title)); + const activeTeamTasks = tasksRef.current + .slice() + .sort((a, b) => a.title.localeCompare(b.title)) if (!isEqual(latestActiveTeamTasks, activeTeamTasks)) { - setTeamTasks(responseTasks); + setTeamTasks(responseTasks) } // Causing extra fetching of active task constantly and inability to change the active task, commented for now // const freshActiveTask = responseTasks.find((t) => t.id === activeTaskId) @@ -75,24 +84,24 @@ export function useTeamTasks() { // console.log("deep check's WORK:", freshActiveTask) // } } else { - setTeamTasks(responseTasks); + setTeamTasks(responseTasks) } }, - [setTeamTasks] - ); + [setTeamTasks], + ) useEffect(() => { if (isSuccess) { - deepCheckAndUpdateTasks(allTasks || [], true); + deepCheckAndUpdateTasks(allTasks || [], true) } - }, [allTasks, isSuccess, isRefetching]); + }, [allTasks, isSuccess, isRefetching]) // Reload tasks after active team changed useEffect(() => { refetch().then((res) => { - deepCheckAndUpdateTasks(res?.data || [], true); - }); - }, [activeTeamId, firstLoad]); + deepCheckAndUpdateTasks(res?.data || [], true) + }) + }, [activeTeamId, firstLoad]) // Delete a Task const deleteTask = useCallback( @@ -100,113 +109,122 @@ export function useTeamTasks() { const { data } = await deleteTaskRequest({ tenantId, taskId: task.id, - bearer_token: authToken - }); + bearer_token: authToken, + }) - const affected = data.affected || 0; + const affected = data.affected || 0 if (affected > 0) { setTeamTasks((ts) => { - return ts.filter((t) => t.id !== task.id); - }); + return ts.filter((t) => t.id !== task.id) + }) } - return data; + return data }, - [setTeamTasks] - ); + [setTeamTasks], + ) const createNewTask = useCallback( - async ({ taskName, issueType }: { taskName: string; issueType?: string }, members?: { id: string }[]) => { + async ( + { taskName, issueType }: { taskName: string; issueType?: string }, + members?: { id: string }[], + ) => { if (taskName.trim().length > 2) { const dataBody: ICreateTask = { title: taskName, - status: 'open', + status: "open", issueType, - description: '', + description: "", members: user?.employee?.id ? [{ id: user.employee.id }] : [], tags: [], teams: [ { - id: activeTeamId - } + id: activeTeamId, + }, ], estimate: 0, organizationId, tenantId, - ...(members ? { members } : {}) - }; - setCreateLoading(true); + ...(members ? { members } : {}), + } + setCreateLoading(true) try { const response = await createTaskRequest({ data: dataBody, - bearer_token: authToken - }); - const { data: created } = response; + bearer_token: authToken, + }) + const { data: created } = response refetch() .then((res) => { - const { data: data_1 } = res; - setTeamTasks(data_1); - const createdTask = data_1.find((t) => t.id === created?.id); - setActiveTeamTask(createdTask); + const { data: data_1 } = res + setTeamTasks(data_1) + const createdTask = data_1.find((t) => t.id === created?.id) + setActiveTeamTask(createdTask) }) - .catch((e) => console.log(e)); + .catch((e) => console.log(e)) } catch (e_1) { - return console.log(e_1); + return console.log(e_1) } - setCreateLoading(false); + setCreateLoading(false) } }, - [activeTeamId] - ); + [activeTeamId], + ) // Global loading state useEffect(() => { if (firstLoad) { - setTasksFetching(isFetching); + setTasksFetching(isFetching) } - }, [isFetching, firstLoad]); + }, [isFetching, firstLoad]) // Update a task const updateTask = async (task: ITeamTask, id: string) => { - const { data, response } = await updateTaskRequest({ data: task, id }, authToken); - refetch(); - return { data, response }; - }; + const { data, response } = await updateTaskRequest({ data: task, id }, authToken) + refetch() + return { data, response } + } - const updateTitle = useCallback((newTitle: string, task?: ITeamTask | null, loader?: boolean) => { - if (task && newTitle !== task.title) { - loader && setTasksFetching(true); - return updateTask( - { - ...task, - title: newTitle - }, - task.id - ).then((res) => { - setTasksFetching(false); - return res; - }); - } - return Promise.resolve(); - }, []); + const updateTitle = useCallback( + (newTitle: string, task?: ITeamTask | null, loader?: boolean) => { + if (task && newTitle !== task.title) { + loader && setTasksFetching(true) + return updateTask( + { + ...task, + title: newTitle, + }, + task.id, + ).then((res) => { + setTasksFetching(false) + return res + }) + } + return Promise.resolve() + }, + [], + ) - const updateDescription = useCallback((newDescription: string, task?: ITeamTask | null, loader?: boolean) => { - if (task && newDescription !== task.description) { - loader && setTasksFetching(true); - return updateTask( - { - ...task, - description: newDescription - }, - task.id - ).then((res) => { - setTasksFetching(false); - return res; - }); - } - return Promise.resolve(); - }, []); + const updateDescription = useCallback( + (newDescription: string, task?: ITeamTask | null, loader?: boolean) => { + if (task && newDescription !== task.description) { + loader && setTasksFetching(true) + return updateTask( + { + ...task, + description: newDescription, + }, + task.id, + ).then((res) => { + setTasksFetching(false) + return res + }) + } + return Promise.resolve() + }, + [], + ) /** * Change active task @@ -214,37 +232,46 @@ export function useTeamTasks() { const setActiveTeamTask = useCallback( async (task: ITeamTask | null) => { try { - setIsUpdatingActiveTask(true); + setIsUpdatingActiveTask(true) const { response } = await updateOrganizationTeamEmployeeActiveTask( currentUser, task?.id, - activeTeamId - ); + activeTeamId, + ) + if (response.ok) { - const synchedActiveTask = allTasks?.find((task) => task.id === currentUser.activeTaskId); - setActiveTask(synchedActiveTask); - setActiveTaskId(synchedActiveTask?.id || ''); + const synchedActiveTask = allTasks?.find( + (task) => task.id === currentUser.activeTaskId, + ) + setActiveTask(synchedActiveTask || task) + setActiveTaskId(synchedActiveTask?.id || task.id) } } catch (error) { - console.log(error); + console.log(error) } finally { - setIsUpdatingActiveTask(false); + setIsUpdatingActiveTask(false) } }, - [setActiveTask, updateOrganizationTeamEmployeeActiveTask, activeTeamId] - ); + [setActiveTask, updateOrganizationTeamEmployeeActiveTask, activeTeamId], + ) useEffect(() => { if (!isUpdatingActiveTask) { const synchedActiveTask = - allTasks && currentUser ? allTasks?.find((task) => task.id === currentUser.activeTaskId) : null; - setActiveTask(synchedActiveTask); - setActiveTaskId(synchedActiveTask?.id || ''); + allTasks && currentUser + ? allTasks?.find((task) => task.id === currentUser.activeTaskId) + : null + const isManager = currentUser?.role?.name === "MANAGER" + + setActiveTask(isManager ? synchedActiveTask : synchedActiveTask || activeTask) + setActiveTaskId( + isManager ? synchedActiveTask?.id || "" : synchedActiveTask?.id || activeTask?.id, + ) } else { - setActiveTask(''); - setActiveTaskId(''); + setActiveTask("") + setActiveTaskId("") } - }, [tasksFetching, isUpdatingActiveTask, isSuccess, currentUser?.activeTaskId, allTasks]); + }, [tasksFetching, isUpdatingActiveTask, isSuccess, currentUser?.activeTaskId, allTasks]) const deleteEmployeeFromTasks = useCallback( (employeeId: string, organizationTeamId: string) => { @@ -252,11 +279,11 @@ export function useTeamTasks() { tenantId, employeeId, organizationTeamId, - bearer_token: authToken - }); + bearer_token: authToken, + }) }, - [deleteEmployeeFromTasksRequest] - ); + [deleteEmployeeFromTasksRequest], + ) return { createNewTask, @@ -272,6 +299,6 @@ export function useTeamTasks() { activeTask, activeTaskId, isRefetching, - firstLoadTaskData - }; + firstLoadTaskData, + } }