diff --git a/apps/web/app/api/daily-plan/[id]/route.ts b/apps/web/app/api/daily-plan/[id]/route.ts index 121c01bae..85b714044 100644 --- a/apps/web/app/api/daily-plan/[id]/route.ts +++ b/apps/web/app/api/daily-plan/[id]/route.ts @@ -10,14 +10,22 @@ export async function GET(req: Request, { params }: INextParams) { return; } - const { $res, user, tenantId, organizationId, access_token } = await authenticatedGuard(req, res); + const { + $res, + user, + tenantId, + organizationId, + teamId: organizationTeamId, + access_token + } = await authenticatedGuard(req, res); if (!user) return $res('Unauthorized'); const response = await getDayPlansByEmployee({ bearer_token: access_token, employeeId: id, organizationId, - tenantId + tenantId, + organizationTeamId }); return $res(response.data); diff --git a/apps/web/app/api/daily-plan/me/route.ts b/apps/web/app/api/daily-plan/me/route.ts index 0624e2d6a..bcb6e6df7 100644 --- a/apps/web/app/api/daily-plan/me/route.ts +++ b/apps/web/app/api/daily-plan/me/route.ts @@ -5,14 +5,22 @@ 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); + const { + $res, + user, + tenantId, + organizationId, + teamId: organizationTeamId, + access_token + } = await authenticatedGuard(req, res); if (!user) return $res('Unauthorized'); const response = await getMyDailyPlansRequest({ bearer_token: access_token, organizationId, - tenantId + tenantId, + organizationTeamId }); return $res(response.data); diff --git a/apps/web/app/api/daily-plan/route.ts b/apps/web/app/api/daily-plan/route.ts index e186506c8..fd3b0dfe3 100644 --- a/apps/web/app/api/daily-plan/route.ts +++ b/apps/web/app/api/daily-plan/route.ts @@ -19,13 +19,21 @@ export async function POST(req: Request) { export async function GET(req: Request) { const res = new NextResponse(); - const { $res, user, tenantId, organizationId, access_token } = await authenticatedGuard(req, res); + const { + $res, + user, + tenantId, + organizationId, + teamId: organizationTeamId, + access_token + } = await authenticatedGuard(req, res); if (!user) return $res('Unauthorized'); const response = await getAllDayPlans({ bearer_token: access_token, organizationId, - tenantId + tenantId, + organizationTeamId }); return $res(response.data); diff --git a/apps/web/app/api/daily-plan/task/[taskId]/route.ts b/apps/web/app/api/daily-plan/task/[taskId]/route.ts index 3ee5a4878..b5f696ecb 100644 --- a/apps/web/app/api/daily-plan/task/[taskId]/route.ts +++ b/apps/web/app/api/daily-plan/task/[taskId]/route.ts @@ -10,7 +10,14 @@ export async function GET(req: Request, { params }: INextParams) { return; } - const { $res, user, tenantId, organizationId, access_token } = await authenticatedGuard(req, res); + const { + $res, + user, + tenantId, + organizationId, + teamId: organizationTeamId, + access_token + } = await authenticatedGuard(req, res); if (!user) return $res('Unauthorized'); @@ -18,7 +25,8 @@ export async function GET(req: Request, { params }: INextParams) { taskId, bearer_token: access_token, organizationId, - tenantId + tenantId, + organizationTeamId }); return $res(response.data); diff --git a/apps/web/app/constants.ts b/apps/web/app/constants.ts index 82adf0d6b..1b7659de6 100644 --- a/apps/web/app/constants.ts +++ b/apps/web/app/constants.ts @@ -284,6 +284,8 @@ export const DAILY_PLAN_SUGGESTION_MODAL_DATE = 'daily-plan-suggestion-modal-dat export const TASKS_ESTIMATE_HOURS_MODAL_DATE = 'tasks-estimate-hours-modal-date'; export const DAILY_PLAN_ESTIMATE_HOURS_MODAL_DATE = 'daily-plan-estimate-hours-modal'; export const DEFAULT_PLANNED_TASK_ID = 'default-planned-task-id'; +export const LAST_OPTION__CREATE_DAILY_PLAN_MODAL = 'last-option--create-daily-plan-modal'; +export const HAS_VISITED_OUTSTANDING_TAB = 'has-visited-outstanding-tab'; // OAuth provider's keys diff --git a/apps/web/app/hooks/features/useAuthTeamTasks.ts b/apps/web/app/hooks/features/useAuthTeamTasks.ts index 79650d83f..19c4be674 100644 --- a/apps/web/app/hooks/features/useAuthTeamTasks.ts +++ b/apps/web/app/hooks/features/useAuthTeamTasks.ts @@ -29,7 +29,7 @@ export function useAuthTeamTasks(user: IUser | undefined) { const planned = useMemo(() => { const outStandingTasksCount = estimatedTotalTime( - outstandingPlans.map((plan) => plan.tasks?.map((task) => task)) + outstandingPlans?.map((plan) => plan.tasks?.map((task) => task)) ).totalTasks; const todayTasksCOunt = getTotalTasks(todayPlan); diff --git a/apps/web/app/hooks/features/useDailyPlan.ts b/apps/web/app/hooks/features/useDailyPlan.ts index a8be0524c..611475022 100644 --- a/apps/web/app/hooks/features/useDailyPlan.ts +++ b/apps/web/app/hooks/features/useDailyPlan.ts @@ -301,6 +301,7 @@ export function useDailyPlan() { ); }, [profileDailyPlans]); + const todayTasks = useMemo(() => { return todayPlan .map((plan) => { @@ -355,8 +356,9 @@ export function useDailyPlan() { if (firstLoad) { getMyDailyPlans(); getAllDayPlans(); + getEmployeeDayPlans(user?.employee.id || ''); } - }, [getMyDailyPlans, getAllDayPlans, firstLoad]); + }, [getMyDailyPlans, activeTeam?.id, getAllDayPlans, firstLoad, getEmployeeDayPlans, user?.employee.id]); return { dailyPlan, diff --git a/apps/web/app/interfaces/index.ts b/apps/web/app/interfaces/index.ts index c2a823b22..7f0e71466 100644 --- a/apps/web/app/interfaces/index.ts +++ b/apps/web/app/interfaces/index.ts @@ -33,7 +33,8 @@ export * from './ITheme'; export * from './IRolePermissions'; export * from './ITimer'; export * from './IProject'; -export * from './ILiveKiteCredentials' +export * from './ILiveKiteCredentials'; +export * from './IBaseModel'; export * from './integrations/IGithubRepositories'; export * from './integrations/IGithubMetadata'; diff --git a/apps/web/app/services/client/api/daily-plan.ts b/apps/web/app/services/client/api/daily-plan.ts index 7ae9886d7..3a5a09d6e 100644 --- a/apps/web/app/services/client/api/daily-plan.ts +++ b/apps/web/app/services/client/api/daily-plan.ts @@ -3,23 +3,26 @@ import { deleteApi, get, post, put } from '../axios'; import { DeleteResponse, ICreateDailyPlan, + ID, IDailyPlan, IDailyPlanTasksUpdate, IRemoveTaskFromManyPlans, IUpdateDailyPlan, PaginationResponse } from '@app/interfaces'; -import { getOrganizationIdCookie, getTenantIdCookie } from '@app/helpers'; +import { getActiveTeamIdCookie, getOrganizationIdCookie, getTenantIdCookie } from '@app/helpers'; -export function getAllDayPlansAPI() { +export function getAllDayPlansAPI(activeTeamId?: ID) { const organizationId = getOrganizationIdCookie(); const tenantId = getTenantIdCookie(); + const organizationTeamId = getActiveTeamIdCookie(); const relations = ['employee', 'tasks', 'employee.user', 'tasks.members', 'tasks.members.user']; const obj = { 'where[organizationId]': organizationId, - 'where[tenantId]': tenantId + 'where[tenantId]': tenantId, + 'where[organizationTeamId]': activeTeamId || organizationTeamId } as Record; relations.forEach((relation, i) => { @@ -30,15 +33,17 @@ export function getAllDayPlansAPI() { return get>(`/daily-plan?${query}`, { tenantId }); } -export function getMyDailyPlansAPI() { +export function getMyDailyPlansAPI(activeTeamId?: ID) { const organizationId = getOrganizationIdCookie(); const tenantId = getTenantIdCookie(); + const organizationTeamId = getActiveTeamIdCookie(); const relations = ['employee', 'tasks', 'employee.user', 'tasks.members', 'tasks.members.user']; const obj = { 'where[organizationId]': organizationId, - 'where[tenantId]': tenantId + 'where[tenantId]': tenantId, + 'where[organizationTeamId]': activeTeamId || organizationTeamId } as Record; relations.forEach((relation, i) => { @@ -49,15 +54,17 @@ export function getMyDailyPlansAPI() { return get>(`/daily-plan/me?${query}`, { tenantId }); } -export function getDayPlansByEmployeeAPI(employeeId?: string) { +export function getDayPlansByEmployeeAPI(employeeId?: string, activeTeamId?: ID) { const organizationId = getOrganizationIdCookie(); const tenantId = getTenantIdCookie(); + const organizationTeamId = getActiveTeamIdCookie(); const relations = ['employee', 'tasks', 'employee.user', 'tasks.members', 'tasks.members.user']; const obj = { 'where[organizationId]': organizationId, - 'where[tenantId]': tenantId + 'where[tenantId]': tenantId, + 'where[organizationTeamId]': activeTeamId || organizationTeamId } as Record; relations.forEach((relation, i) => { @@ -71,10 +78,12 @@ export function getDayPlansByEmployeeAPI(employeeId?: string) { export function getPlansByTaskAPI(taskId?: string) { const organizationId = getOrganizationIdCookie(); const tenantId = getTenantIdCookie(); + const organizationTeamId = getActiveTeamIdCookie(); const obj = { 'where[organizationId]': organizationId, - 'where[tenantId]': tenantId + 'where[tenantId]': tenantId, + 'where[organizationTeamId]': organizationTeamId } as Record; const query = qs.stringify(obj); @@ -106,9 +115,9 @@ export function removeTaskFromPlanAPI(data: IDailyPlanTasksUpdate, planId: strin return put(`/daily-plan/${planId}/task`, { ...data, organizationId }, { tenantId }); } -export function removeManyTaskFromPlansAPI({ taskId, data }: { taskId: string, data: IRemoveTaskFromManyPlans }) { +export function removeManyTaskFromPlansAPI({ taskId, data }: { taskId: string; data: IRemoveTaskFromManyPlans }) { const organizationId = getOrganizationIdCookie(); - return put(`/daily-plan/${taskId}/remove`, { ...data, organizationId }) + return put(`/daily-plan/${taskId}/remove`, { ...data, organizationId }); } export function deleteDailyPlanAPI(planId: string) { diff --git a/apps/web/app/services/server/requests/daily-plan.ts b/apps/web/app/services/server/requests/daily-plan.ts index cfe4655c7..1470e5c11 100644 --- a/apps/web/app/services/server/requests/daily-plan.ts +++ b/apps/web/app/services/server/requests/daily-plan.ts @@ -1,22 +1,31 @@ import qs from 'qs'; -import { ICreateDailyPlan, IDailyPlan, IDailyPlanTasksUpdate, IRemoveTaskFromManyPlans, IUpdateDailyPlan } from '@app/interfaces/IDailyPlan'; +import { + ICreateDailyPlan, + IDailyPlan, + IDailyPlanTasksUpdate, + IRemoveTaskFromManyPlans, + IUpdateDailyPlan +} from '@app/interfaces/IDailyPlan'; import { serverFetch } from '../fetch'; -import { DeleteResponse } from '@app/interfaces'; +import { DeleteResponse, ID } from '@app/interfaces'; export function getAllDayPlans({ organizationId, tenantId, + organizationTeamId, bearer_token, relations = ['employee', 'tasks', 'employee.user', 'tasks.members', 'tasks.members.user'] }: { - organizationId: string; - tenantId: string; + organizationId: ID; + tenantId: ID; + organizationTeamId: ID; bearer_token: string; relations?: string[]; }) { const obj = { 'where[organizationId]': organizationId, - 'where[tenantId]': tenantId + 'where[tenantId]': tenantId, + 'where[organizationTeamId]': organizationTeamId } as Record; relations.forEach((relation, i) => { @@ -35,17 +44,20 @@ export function getAllDayPlans({ export function getMyDailyPlansRequest({ organizationId, tenantId, + organizationTeamId, bearer_token, relations = ['employee', 'tasks', 'employee.user', 'tasks.members', 'tasks.members.user'] }: { - organizationId: string; - tenantId: string; + organizationId: ID; + tenantId: ID; + organizationTeamId: ID; bearer_token: string; relations?: string[]; }) { const obj = { 'where[organizationId]': organizationId, - 'where[tenantId]': tenantId + 'where[tenantId]': tenantId, + 'where[organizationTeamId]': organizationTeamId } as Record; relations.forEach((relation, i) => { @@ -65,18 +77,21 @@ export function getDayPlansByEmployee({ employeeId, organizationId, tenantId, + organizationTeamId, bearer_token, relations = ['employee', 'tasks', 'employee.user', 'tasks.members', 'tasks.members.user'] }: { - employeeId: string; - organizationId: string; - tenantId: string; + employeeId: ID; + organizationId: ID; + tenantId: ID; + organizationTeamId: ID; bearer_token: string; relations?: string[]; }) { const obj = { 'where[organizationId]': organizationId, - 'where[tenantId]': tenantId + 'where[tenantId]': tenantId, + 'where[organizationTeamId]': organizationTeamId } as Record; relations.forEach((relation, i) => { @@ -96,16 +111,19 @@ export function getPlansByTask({ taskId, organizationId, tenantId, + organizationTeamId, bearer_token }: { taskId: string; - organizationId: string; - tenantId: string; + organizationId: ID; + tenantId: ID; + organizationTeamId: ID; bearer_token: string; }) { const obj = { 'where[organizationId]': organizationId, - 'where[tenantId]': tenantId + 'where[tenantId]': tenantId, + 'where[organizationTeamId]': organizationTeamId } as Record; const query = qs.stringify(obj); @@ -203,11 +221,19 @@ export function deleteDailyPlanRequest({ planId, bearer_token }: { planId: strin }); } -export function deleteDailyPlansManyRequest({ bearer_token, taskId, data }: { bearer_token?: string, taskId: string, data: IRemoveTaskFromManyPlans }) { +export function deleteDailyPlansManyRequest({ + bearer_token, + taskId, + data +}: { + bearer_token?: string; + taskId: string; + data: IRemoveTaskFromManyPlans; +}) { return serverFetch({ method: 'PUT', path: `/daily-plan/${taskId}/remove`, body: data, - bearer_token, + bearer_token }); } diff --git a/apps/web/lib/app/init-state.tsx b/apps/web/lib/app/init-state.tsx index da6b9bbd4..e2bbf4574 100644 --- a/apps/web/lib/app/init-state.tsx +++ b/apps/web/lib/app/init-state.tsx @@ -54,7 +54,7 @@ function InitState() { const { firstLoadIssueTypeData } = useIssueType(); const { firstLoadTaskRelatedIssueTypeData, loadTaskRelatedIssueTypeData } = useTaskRelatedIssueType(); - const { firstLoadDailyPlanData } = useDailyPlan(); + const { firstLoadDailyPlanData, getAllDayPlans, getMyDailyPlans, getEmployeeDayPlans } = useDailyPlan(); const { firstLoadTimeLogs } = useTimeLogs(); const { firstLoadDataEmployee } = useEmployee(); @@ -125,6 +125,10 @@ function InitState() { useRefreshIntervalV2(loadTaskRelatedIssueTypeData, sixty_two_seconds, true); useRefreshIntervalV2(loadTaskVersionData, sixty_two_seconds, true); + useRefreshIntervalV2(getAllDayPlans, sixty_two_seconds, true); + useRefreshIntervalV2(getMyDailyPlans, sixty_two_seconds, true); + useRefreshIntervalV2(getEmployeeDayPlans, sixty_two_seconds, true); + return <>; }; return Component; diff --git a/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx b/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx index 92acf67a9..8d57812f8 100644 --- a/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx +++ b/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx @@ -20,6 +20,7 @@ import { TaskDetailsModal } from './task-details-modal'; import { Popover, Transition } from '@headlessui/react'; import { ScrollArea, ScrollBar } from '@components/ui/scroll-bar'; import { Cross2Icon } from '@radix-ui/react-icons'; +import { checkPastDate } from 'lib/utils'; /** * A modal that allows user to add task estimation / planned work time, etc. @@ -58,6 +59,15 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa const currentDate = useMemo(() => new Date().toISOString().split('T')[0], []); const requirePlan = useMemo(() => activeTeam?.requirePlanToTrack, [activeTeam?.requirePlanToTrack]); const tasksEstimationTimes = useMemo(() => estimatedTotalTime(plan.tasks).timesEstimated / 3600, [plan.tasks]); + const totalWorkedTime = useMemo( + () => + plan.tasks?.reduce((acc, cur) => { + const totalWorkedTime = cur.totalWorkedTime ?? 0; + + return acc + totalWorkedTime; + }, 0), + [plan] + ); const [warning, setWarning] = useState(''); const [loading, setLoading] = useState(false); const [defaultTask, setDefaultTask] = useState(null); @@ -221,7 +231,7 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa // Update the working planned time useEffect(() => { setWorkTimePlanned(plan.workTimePlanned); - }, [plan]); + }, [plan.id, plan.workTimePlanned]); const StartWorkingButton = ( +
+
+ {checkPastDate(plan.date) ? ( + {t('dailyPlan.PLANNED_TIME')} + ) : ( + + {t('timer.todayPlanSettings.WORK_TIME_PLANNED')}{' '} + * + + )} +
+ {checkPastDate(plan.date) ? ( +
+ {formatIntegerToHour(tasksEstimationTimes)} +
+ ) : ( + { + !isNaN(parseInt(e.target.value)) + ? setWorkTimePlanned(parseInt(e.target.value)) + : setWorkTimePlanned(0); + }} + required + noWrapper + min={0} + value={ + !isNaN(workTimePlanned) && workTimePlanned.toString() !== '0' + ? workTimePlanned.toString().replace(/^0+/, '') + : isWorkingTimeInputFocused + ? '' + : 0 + } + onFocus={() => setWorkingTimeInputFocused(true)} + onBlur={() => setWorkingTimeInputFocused(false)} + defaultValue={ + plan.workTimePlanned ? parseInt(plan.workTimePlanned.toString()) : 0 + } + readOnly={checkPastDate(plan.date)} + /> + )} + + {!checkPastDate(plan.date) && ( + + )} +
+ {checkPastDate(plan.date) && ( +
+ Tracked time +
+ {formatIntegerToHour(totalWorkedTime ?? 0)} +
+
+ )}
)} @@ -308,10 +350,14 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa
{t('task.TITLE_PLURAL')} - * + {!checkPastDate(plan.date) && *}
- {t('dailyPlan.TOTAL_ESTIMATED')} : + {checkPastDate(plan.date) ? ( + {t('dailyPlan.ESTIMATED')} : + ) : ( + {t('dailyPlan.TOTAL_ESTIMATED')} : + )} {formatIntegerToHour(tasksEstimationTimes)}
@@ -333,7 +379,7 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa
- {warning && ( + {!checkPastDate(plan.date) && warning && ( <> {warning} @@ -584,6 +630,8 @@ function TaskCard(props: ITaskCardProps) { const t = useTranslations(); + const status = useTaskStatus(); + /** * The function that adds the task to the selected plan */ @@ -599,11 +647,13 @@ function TaskCard(props: ITaskCardProps) { } }, [addTaskToPlan, plan.id, task.id]); + console.log(status.taskStatus); + return (