diff --git a/apps/web/app/hooks/features/useStartStopTimerHandler.ts b/apps/web/app/hooks/features/useStartStopTimerHandler.ts index 1b432a9e7..0968ea4b6 100644 --- a/apps/web/app/hooks/features/useStartStopTimerHandler.ts +++ b/apps/web/app/hooks/features/useStartStopTimerHandler.ts @@ -162,7 +162,7 @@ export function useStartStopTimerHandler() { if (timerStatusFetching || !canRunTimer) return; if (timerStatus?.running) { stopTimer(); - } else if (requirePlan && !isActiveTaskPlaned) { + } else if (requirePlan && hasPlan && !isActiveTaskPlaned) { openEnforcePlannedTaskModal(); } else { if ( diff --git a/apps/web/app/hooks/features/useTimer.ts b/apps/web/app/hooks/features/useTimer.ts index f6c222d2e..2a5de21ca 100644 --- a/apps/web/app/hooks/features/useTimer.ts +++ b/apps/web/app/hooks/features/useTimer.ts @@ -265,9 +265,7 @@ export function useTimer() { user?.isEmailVerified && ((!!activeTeamTask && activeTeamTask.status !== 'closed') || // If timer is running at some other source and user may or may not have selected the task - timerStatusRef.current?.lastLog?.source !== TimerSource.TEAMS) && - // If team settings require to have a plan to be able track - canTrack; + timerStatusRef.current?.lastLog?.source !== TimerSource.TEAMS) // Local time status const { diff --git a/apps/web/lib/components/modal.tsx b/apps/web/lib/components/modal.tsx index 84985fc01..4df9f676f 100644 --- a/apps/web/lib/components/modal.tsx +++ b/apps/web/lib/components/modal.tsx @@ -15,6 +15,7 @@ type Props = { className?: string; alignCloseIcon?: boolean; showCloseIcon?: boolean; + closeOnOutsideClick?: boolean; } & PropsWithChildren; export function Modal({ @@ -27,7 +28,8 @@ export function Modal({ description, className, alignCloseIcon, - showCloseIcon = true + showCloseIcon = true, + closeOnOutsideClick = false }: Props) { const refDiv = useRef(null); @@ -44,7 +46,7 @@ export function Modal({ > null} as="div" className="fixed inset-0 backdrop-brightness-90 backdrop-blur-sm z-[9999] w-full h-full" > @@ -52,28 +54,32 @@ export function Modal({ - {title && {title}} - {description && {description}} - {showCloseIcon && ( -
{ - closeModal(); - customCloseModal?.(); - }} - className={`absolute ${ - alignCloseIcon ? 'right-2 top-3' : 'right-3 top-3' - } md:right-2 md:top-3 cursor-pointer z-50`} - > - close -
- )} - {children} + + {title && {title}} + {description && {description}} + {showCloseIcon && ( +
{ + closeModal(); + customCloseModal?.(); + }} + className={`absolute ${ + alignCloseIcon ? 'right-2 top-3' : 'right-3 top-3' + } md:right-2 md:top-3 cursor-pointer z-50`} + > + close +
+ )} + {children} +
diff --git a/apps/web/lib/components/switch.tsx b/apps/web/lib/components/switch.tsx index e88350c4d..62a32779c 100644 --- a/apps/web/lib/components/switch.tsx +++ b/apps/web/lib/components/switch.tsx @@ -5,6 +5,7 @@ import { Switch } from '@headlessui/react'; import { useCallback, useEffect, useState } from 'react'; import { Text } from './typography'; import { useTranslations } from 'next-intl'; +import { DAILY_PLAN_SUGGESTION_MODAL_DATE } from '@app/constants'; export default function TimeTrackingToggle({ activeManager }: { activeManager: OT_Member | undefined }) { const t = useTranslations(); @@ -129,6 +130,9 @@ export function RequireDailyPlanToTrack() { .filter((value, index, array) => array.indexOf(value) === index) }); setEnabled(!enabled); + if (!enabled) { + localStorage.removeItem(DAILY_PLAN_SUGGESTION_MODAL_DATE); + } } }, [activeTeam, editOrganizationTeam, enabled]); diff --git a/apps/web/lib/features/daily-plan/add-daily-plan-work-hours-modal.tsx b/apps/web/lib/features/daily-plan/add-daily-plan-work-hours-modal.tsx index aa66a0d35..eb8ab7fc1 100644 --- a/apps/web/lib/features/daily-plan/add-daily-plan-work-hours-modal.tsx +++ b/apps/web/lib/features/daily-plan/add-daily-plan-work-hours-modal.tsx @@ -1,4 +1,4 @@ -import { Card, InputField, Modal, Text } from 'lib/components'; +import { Card, InputField, Modal, SpinnerLoader, Text } from 'lib/components'; import { Button } from '@components/ui/button'; import { useCallback, useMemo, useState } from 'react'; import { DAILY_PLAN_ESTIMATE_HOURS_MODAL_DATE } from '@app/constants'; @@ -19,25 +19,38 @@ export function AddDailyPlanWorkHourModal(props: IAddDailyPlanWorkHoursModalProp const { updateDailyPlan } = useDailyPlan(); const { startTimer } = useTimerView(); const { activeTeam } = useTeamTasks(); - const [workTimePlanned, setworkTimePlanned] = useState(plan.workTimePlanned); const currentDate = useMemo(() => new Date().toISOString().split('T')[0], []); const requirePlan = useMemo(() => activeTeam?.requirePlanToTrack, [activeTeam?.requirePlanToTrack]); const hasWorkHours = useMemo(() => plan.workTimePlanned && plan.workTimePlanned > 0, [plan.workTimePlanned]); + const [loading, setLoading] = useState(false); const handleCloseModal = useCallback(() => { localStorage.setItem(DAILY_PLAN_ESTIMATE_HOURS_MODAL_DATE, currentDate); closeModal(); - startTimer(); - }, [closeModal, currentDate, startTimer]); + }, [closeModal, currentDate]); + + const handleSubmit = useCallback(async () => { + try { + setLoading(true); + + // Update the plan work time only if the user changed it + if (plan && plan.workTimePlanned !== workTimePlanned) { + await updateDailyPlan({ workTimePlanned }, plan.id ?? ''); + } + + startTimer(); - const handleSubmit = useCallback(() => { - updateDailyPlan({ workTimePlanned }, plan.id ?? ''); - handleCloseModal(); - }, [handleCloseModal, plan.id, updateDailyPlan, workTimePlanned]); + handleCloseModal(); + } catch (error) { + console.log(error); + } finally { + setLoading(false); + } + }, [handleCloseModal, plan, startTimer, updateDailyPlan, workTimePlanned]); return ( - +
@@ -66,7 +79,7 @@ export function AddDailyPlanWorkHourModal(props: IAddDailyPlanWorkHoursModalProp
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 6c263cda6..22880e1f6 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 @@ -57,11 +57,10 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa const t = useTranslations(); const { updateDailyPlan, myDailyPlans } = useDailyPlan(); const { startTimer, timerStatus } = useTimerView(); - const { activeTeam, activeTeamTask, setActiveTask } = useTeamTasks(); + const { activeTeamTask, setActiveTask } = useTeamTasks(); const [showSearchInput, setShowSearchInput] = useState(false); const [workTimePlanned, setWorkTimePlanned] = useState(plan?.workTimePlanned ?? 0); const currentDate = useMemo(() => new Date().toISOString().split('T')[0], []); - const requirePlan = useMemo(() => activeTeam?.requirePlanToTrack, [activeTeam?.requirePlanToTrack]); const tasksEstimationTimes = useMemo( () => (plan && plan.tasks ? estimatedTotalTime(plan.tasks).timesEstimated / 3600 : 0), [plan] @@ -108,12 +107,24 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa /** * The function that close the Planned tasks modal when the user ignores the modal (Today's plan) */ - const closeModalAndStartTimer = useCallback(() => { - handleCloseModal(); - if (canStartWorking) { - startTimer(); + const closeModalAndSubmit = useCallback(async () => { + try { + setLoading(true); + + // Update the plan work time only if the user changed it + plan && + plan.workTimePlanned !== workTimePlanned && + (await updateDailyPlan({ workTimePlanned }, plan.id ?? '')); + + setPlanEditState({ draft: false, saved: true }); + + handleCloseModal(); + } catch (error) { + console.log(error); + } finally { + setLoading(false); } - }, [canStartWorking, handleCloseModal, startTimer]); + }, [handleCloseModal, plan, updateDailyPlan, workTimePlanned]); /** * The function that opens the Change task modal if conditions are met (or start the timer) @@ -460,19 +471,11 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa
@@ -495,7 +498,7 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa return ( <> {isRenderedInSoftFlow ? ( - + {content} diff --git a/apps/web/lib/features/daily-plan/suggest-daily-plan-modal.tsx b/apps/web/lib/features/daily-plan/suggest-daily-plan-modal.tsx index d0cd0708a..38c5f0b26 100644 --- a/apps/web/lib/features/daily-plan/suggest-daily-plan-modal.tsx +++ b/apps/web/lib/features/daily-plan/suggest-daily-plan-modal.tsx @@ -4,7 +4,7 @@ import { useCallback, useMemo } from 'react'; import { DAILY_PLAN_SUGGESTION_MODAL_DATE, HAS_SEEN_DAILY_PLAN_SUGGESTION_MODAL } from '@app/constants'; import { useTranslations } from 'next-intl'; import Link from 'next/link'; -import { useAuthenticateUser } from '@app/hooks'; +import { useAuthenticateUser, useTeamTasks, useTimer } from '@app/hooks'; import { usePathname } from 'next/navigation'; interface ISuggestDailyPlanModalProps { @@ -14,27 +14,38 @@ interface ISuggestDailyPlanModalProps { export function SuggestDailyPlanModal(props: ISuggestDailyPlanModalProps) { const { isOpen, closeModal } = props; - + const { hasPlan } = useTimer(); + const { activeTeam } = useTeamTasks(); const { user } = useAuthenticateUser(); const name = useMemo( () => user?.name || user?.firstName || user?.lastName || user?.username || '', [user?.firstName, user?.lastName, user?.name, user?.username] ); + const requirePlan = useMemo(() => activeTeam?.requirePlanToTrack, [activeTeam?.requirePlanToTrack]); const path = usePathname(); const t = useTranslations(); const currentDate = useMemo(() => new Date().toISOString().split('T')[0], []); const handleCloseModal = useCallback(() => { - localStorage.setItem(HAS_SEEN_DAILY_PLAN_SUGGESTION_MODAL, currentDate); + if (!requirePlan || (requirePlan && hasPlan)) { + localStorage.setItem(HAS_SEEN_DAILY_PLAN_SUGGESTION_MODAL, currentDate); + } if (path.split('/')[1] == 'profile') { - localStorage.setItem(DAILY_PLAN_SUGGESTION_MODAL_DATE, currentDate); + if (!requirePlan || (requirePlan && hasPlan)) { + localStorage.setItem(DAILY_PLAN_SUGGESTION_MODAL_DATE, currentDate); + } } closeModal(); - }, [closeModal, currentDate, path]); + }, [closeModal, currentDate, hasPlan, path, requirePlan]); return ( - +
diff --git a/apps/web/lib/features/user-profile-plans.tsx b/apps/web/lib/features/user-profile-plans.tsx index 8ad9a78a6..0ec07b9c9 100644 --- a/apps/web/lib/features/user-profile-plans.tsx +++ b/apps/web/lib/features/user-profile-plans.tsx @@ -10,7 +10,14 @@ import { IoCalendarOutline } from 'react-icons/io5'; import { formatDayPlanDate, formatIntegerToHour } from '@app/helpers'; import { handleDragAndDrop } from '@app/helpers/drag-and-drop'; -import { useAuthenticateUser, useCanSeeActivityScreen, useDailyPlan, useUserProfilePage } from '@app/hooks'; +import { + useAuthenticateUser, + useCanSeeActivityScreen, + useDailyPlan, + useTeamTasks, + useTimer, + useUserProfilePage +} from '@app/hooks'; import { useDateRange } from '@app/hooks/useDateRange'; import { filterDailyPlan } from '@app/hooks/useFilterDateRange'; import { useLocalStorageState } from '@app/hooks/useLocalStorageState'; @@ -54,9 +61,7 @@ export function UserProfilePlans() { const { todayPlan, futurePlans, pastPlans, outstandingPlans, sortedPlans, profileDailyPlans } = useDailyPlan(); const fullWidth = useAtomValue(fullWidthState); const [currentOutstanding, setCurrentOutstanding] = useLocalStorageState('outstanding', 'ALL'); - const [currentTab, setCurrentTab] = useLocalStorageState('daily-plan-tab', 'Today Tasks'); - const [currentDataDailyPlan, setCurrentDataDailyPlan] = useAtom(dataDailyPlanState); const { setDate, date } = useDateRange(currentTab); @@ -77,6 +82,9 @@ export function UserProfilePlans() { const dailyPlanSuggestionModalDate = window && window?.localStorage.getItem(DAILY_PLAN_SUGGESTION_MODAL_DATE); const path = usePathname(); const haveSeenDailyPlanSuggestionModal = window?.localStorage.getItem(HAS_SEEN_DAILY_PLAN_SUGGESTION_MODAL); + const { hasPlan } = useTimer(); + const { activeTeam } = useTeamTasks(); + const requirePlan = useMemo(() => activeTeam?.requirePlanToTrack, [activeTeam?.requirePlanToTrack]); // Set the tab plan tab to outstanding if user has no daily plan and there are outstanding tasks (on first load) useEffect(() => { @@ -85,7 +93,12 @@ export function UserProfilePlans() { setCurrentTab('Outstanding'); } if (haveSeenDailyPlanSuggestionModal == new Date().toISOString().split('T')[0]) { - window.localStorage.setItem(DAILY_PLAN_SUGGESTION_MODAL_DATE, new Date().toISOString().split('T')[0]); + if (!requirePlan || (requirePlan && hasPlan)) { + window.localStorage.setItem( + DAILY_PLAN_SUGGESTION_MODAL_DATE, + new Date().toISOString().split('T')[0] + ); + } } } @@ -207,6 +220,9 @@ function AllPlans({ profile, currentTab = 'All Tasks' }: { profile: any; current const [popupOpen, setPopupOpen] = useState(false); const [currentDeleteIndex, setCurrentDeleteIndex] = useState(0); const { date } = useDateRange(currentTab); + const { activeTeam } = useTeamTasks(); + const requirePlan = useMemo(() => activeTeam?.requirePlanToTrack, [activeTeam?.requirePlanToTrack]); + const t = useTranslations(); if (currentTab === 'Today Tasks') { filteredPlans.current = todayPlan; @@ -338,21 +354,28 @@ function AllPlans({ profile, currentTab = 'All Tasks' }: { profile: any; current variant="outline" className="px-4 py-2 text-sm font-medium text-red-600 border border-red-600 rounded-md bg-light--theme-light dark:!bg-dark--theme-light" > - Delete this plan + {t('common.plan.DELETE_THIS_PLAN')} } > {/*button confirm*/} {/*button cancel*/}
diff --git a/apps/web/locales/ar.json b/apps/web/locales/ar.json index 0da4e830a..91c268620 100644 --- a/apps/web/locales/ar.json +++ b/apps/web/locales/ar.json @@ -217,7 +217,8 @@ "EDIT_PLAN": "تعديل الخطة", "TRACKED_TIME": "الوقت المتعقب", "SEE_PLANS": "عرض الخطط", - "ADD_PLAN": "إضافة خطة" + "ADD_PLAN": "إضافة خطة", + "DELETE_THIS_PLAN": "احذف هذه الخطة" }, "timesheets": { "SINGULAR": "ورقة الحضور", diff --git a/apps/web/locales/bg.json b/apps/web/locales/bg.json index f73fbe295..e9ebc3679 100644 --- a/apps/web/locales/bg.json +++ b/apps/web/locales/bg.json @@ -217,7 +217,8 @@ "EDIT_PLAN": "Редактиране на план", "TRACKED_TIME": "Проследено време", "SEE_PLANS": "Виж планове", - "ADD_PLAN": "Добави план" + "ADD_PLAN": "Добави план", + "DELETE_THIS_PLAN": "Изтрийте този план" }, "timesheets": { "SINGULAR": "Работен лист", diff --git a/apps/web/locales/de.json b/apps/web/locales/de.json index b049c9a1c..1b48517ba 100644 --- a/apps/web/locales/de.json +++ b/apps/web/locales/de.json @@ -217,7 +217,8 @@ "EDIT_PLAN": "Plan bearbeiten", "TRACKED_TIME": "Verfolgte Zeit", "SEE_PLANS": "Pläne anzeigen", - "ADD_PLAN": "Plan hinzufügen" + "ADD_PLAN": "Plan hinzufügen", + "DELETE_THIS_PLAN": "Diesen Plan löschen" }, "timesheets": { "SINGULAR": "Stundenzettel", diff --git a/apps/web/locales/en.json b/apps/web/locales/en.json index 54788f7c5..d1213f176 100644 --- a/apps/web/locales/en.json +++ b/apps/web/locales/en.json @@ -217,7 +217,8 @@ "EDIT_PLAN": "Edit Plan", "TRACKED_TIME": "Tracked time", "SEE_PLANS": "See plans", - "ADD_PLAN": "Add Plan" + "ADD_PLAN": "Add Plan", + "DELETE_THIS_PLAN": "Delete this plan" }, "timesheets": { "SINGULAR": "Timesheet", diff --git a/apps/web/locales/es.json b/apps/web/locales/es.json index a62f626cb..06f9b989f 100644 --- a/apps/web/locales/es.json +++ b/apps/web/locales/es.json @@ -217,7 +217,8 @@ "ADD_PLAN": "Agregar Plan", "TRACKED_TIME": "Tiempo registrado", "SEE_PLANS": "Ver planes", - "FOR_TOMORROW": "Plan de mañana" + "FOR_TOMORROW": "Plan de mañana", + "DELETE_THIS_PLAN": "Eliminar este plan" }, "timesheets": { "SINGULAR": "Hoja de horas", diff --git a/apps/web/locales/fr.json b/apps/web/locales/fr.json index 80b432fa3..437192acd 100644 --- a/apps/web/locales/fr.json +++ b/apps/web/locales/fr.json @@ -217,7 +217,8 @@ "EDIT_PLAN": "Modifier le plan", "TRACKED_TIME": "Temps suivi", "SEE_PLANS": "Voir les plans", - "ADD_PLAN": "Ajouter un plan" + "ADD_PLAN": "Ajouter un plan", + "DELETE_THIS_PLAN": "Supprimer ce plan" }, "timesheets": { "SINGULAR": "Feuille de temps", diff --git a/apps/web/locales/he.json b/apps/web/locales/he.json index 5da647fee..8f914ae23 100644 --- a/apps/web/locales/he.json +++ b/apps/web/locales/he.json @@ -217,7 +217,8 @@ "EDIT_PLAN": "ערוך תוכנית", "TRACKED_TIME": "זמן מעקב", "SEE_PLANS": "ראה תוכניות", - "ADD_PLAN": "הוסף תוכנית" + "ADD_PLAN": "הוסף תוכנית", + "DELETE_THIS_PLAN": "מחק את התוכנית הזו" }, "timesheets": { "SINGULAR": "דוח שעות", diff --git a/apps/web/locales/it.json b/apps/web/locales/it.json index 4b4b34e18..0391518dd 100644 --- a/apps/web/locales/it.json +++ b/apps/web/locales/it.json @@ -221,7 +221,8 @@ "EDIT_PLAN": "Modifica Piano", "TRACKED_TIME": "Tempo tracciato", "SEE_PLANS": "Vedi piani", - "ADD_PLAN": "Aggiungi Piano" + "ADD_PLAN": "Aggiungi Piano", + "DELETE_THIS_PLAN": "Elimina questo piano" }, "COPY_ISSUE_LINK": "Copia collegamento problema", "MAKE_A_COPY": "Fai una copia", diff --git a/apps/web/locales/nl.json b/apps/web/locales/nl.json index e3bb4f1d8..73a033cbf 100644 --- a/apps/web/locales/nl.json +++ b/apps/web/locales/nl.json @@ -221,7 +221,8 @@ "EDIT_PLAN": "Plan bewerken", "TRACKED_TIME": "Gegeneraliseerde tijd", "SEE_PLANS": "Plannen bekijken", - "ADD_PLAN": "Plan toevoegen" + "ADD_PLAN": "Plan toevoegen", + "DELETE_THIS_PLAN": "Verwijder dit plan" }, "COPY_ISSUE_LINK": "Probleemlink kopiëren", "MAKE_A_COPY": "Maak een kopie", diff --git a/apps/web/locales/pl.json b/apps/web/locales/pl.json index e8e514ad6..3983423ca 100644 --- a/apps/web/locales/pl.json +++ b/apps/web/locales/pl.json @@ -221,7 +221,8 @@ "EDIT_PLAN": "Edytuj plan", "TRACKED_TIME": "Śledzony czas", "SEE_PLANS": "Zobacz plany", - "ADD_PLAN": "Dodaj plan" + "ADD_PLAN": "Dodaj plan", + "DELETE_THIS_PLAN": "Usuń ten plan" }, "COPY_ISSUE_LINK": "Skopiuj link do zgłoszenia", "MAKE_A_COPY": "Zrób kopię", diff --git a/apps/web/locales/pt.json b/apps/web/locales/pt.json index 8573c0e08..79169e006 100644 --- a/apps/web/locales/pt.json +++ b/apps/web/locales/pt.json @@ -221,7 +221,8 @@ "EDIT_PLAN": "Editar Plano", "TRACKED_TIME": "Tempo rastreado", "SEE_PLANS": "Ver planos", - "ADD_PLAN": "Adicionar Plano" + "ADD_PLAN": "Adicionar Plano", + "DELETE_THIS_PLAN": "Excluir este plano" }, "COPY_ISSUE_LINK": "Copiar link do problema", "MAKE_A_COPY": "Fazer uma cópia", diff --git a/apps/web/locales/ru.json b/apps/web/locales/ru.json index 780f91e95..5ea6dcfce 100644 --- a/apps/web/locales/ru.json +++ b/apps/web/locales/ru.json @@ -221,7 +221,8 @@ "EDIT_PLAN": "Редактировать план", "TRACKED_TIME": "Отслеживаемое время", "SEE_PLANS": "Посмотреть планы", - "ADD_PLAN": "Agregar Plan" + "ADD_PLAN": "Agregar Plan", + "DELETE_THIS_PLAN": "Удалить этот план" }, "COPY_ISSUE_LINK": "Скопировать ссылку на проблему", "MAKE_A_COPY": "Сделать копию", diff --git a/apps/web/locales/zh.json b/apps/web/locales/zh.json index a04ec4398..fa75ac959 100644 --- a/apps/web/locales/zh.json +++ b/apps/web/locales/zh.json @@ -221,7 +221,8 @@ "EDIT_PLAN": "编辑计划", "TRACKED_TIME": "跟踪时间", "SEE_PLANS": "查看计划", - "ADD_PLAN": "添加计划" + "ADD_PLAN": "添加计划", + "DELETE_THIS_PLAN": "删除此计划" }, "COPY_ISSUE_LINK": "复制问题链接", "MAKE_A_COPY": "制作副本",