From 1a1d0c677377db3c1f3aa57501465ab7fb85c248 Mon Sep 17 00:00:00 2001 From: AKILIMAILI CIZUNGU Innocent <51681130+Innocent-Akim@users.noreply.github.com> Date: Mon, 11 Nov 2024 23:37:52 +0200 Subject: [PATCH 01/13] [Feat]: Timesheet Filter Calendar (#3305) * refactor: Add FilterCalendar component for improved timesheet selection in calendar view * feat: add clear filter button to reset selected items * fix:coderabbitai[bot] * fix: deepscan-disable-line * fix: logic * fix: logic --- .../components/TimeSheetFilterPopover.tsx | 29 +++-- .../[memberId]/components/TimesheetCard.tsx | 2 +- .../[memberId]/components/TimesheetFilter.tsx | 14 +-- .../components/TimesheetFilterDate.tsx | 107 ++++++++++++++++-- .../[memberId]/components/TimesheetView.tsx | 4 +- .../[locale]/timesheet/[memberId]/page.tsx | 29 +++-- apps/web/app/hooks/features/useTimesheet.ts | 39 ++++--- apps/web/app/interfaces/IProject.ts | 1 + apps/web/app/interfaces/ITask.ts | 2 +- apps/web/app/interfaces/timer/ITimerLog.ts | 3 + .../services/client/api/timer/timer-log.ts | 3 +- .../components/custom-select/multi-select.tsx | 24 ++++ .../calendar/table-time-sheet.tsx | 15 ++- apps/web/lib/features/task/task-issue.tsx | 18 ++- 14 files changed, 229 insertions(+), 61 deletions(-) diff --git a/apps/web/app/[locale]/timesheet/[memberId]/components/TimeSheetFilterPopover.tsx b/apps/web/app/[locale]/timesheet/[memberId]/components/TimeSheetFilterPopover.tsx index 364542878..743a0e4ab 100644 --- a/apps/web/app/[locale]/timesheet/[memberId]/components/TimeSheetFilterPopover.tsx +++ b/apps/web/app/[locale]/timesheet/[memberId]/components/TimeSheetFilterPopover.tsx @@ -1,7 +1,8 @@ import { useOrganizationTeams, useTeamTasks } from "@app/hooks"; import { Button } from "@components/ui/button"; import { statusOptions } from "@app/constants"; -import { MultiSelect } from "lib/components/custom-select/multi-select"; +import { MultiSelect } from "lib/components/custom-select"; +import React, { useEffect } from "react"; import { Popover, PopoverContent, @@ -9,13 +10,22 @@ import { } from "@components/ui/popover"; import { SettingFilterIcon } from "@/assets/svg"; import { useTranslations } from "next-intl"; +import { clsxm } from "@/app/utils"; export function TimeSheetFilterPopover() { + const [shouldRemoveItems, setShouldRemoveItems] = React.useState(false); const { activeTeam } = useOrganizationTeams(); const { tasks } = useTeamTasks(); const t = useTranslations(); + + useEffect(() => { + if (shouldRemoveItems) { + setShouldRemoveItems(false); + } + }, [shouldRemoveItems]); + return ( <> @@ -36,9 +46,10 @@ export function TimeSheetFilterPopover() {
(members ? members.employee.fullName : '')} itemId={(item) => item.id} @@ -50,11 +61,12 @@ export function TimeSheetFilterPopover() {
(members ? members.employee.fullName : '')} + removeItems={shouldRemoveItems} + items={activeTeam?.projects ?? []} + itemToString={(project) => (activeTeam?.projects ? project.name! : '')} itemId={(item) => item.id} onValueChange={(selectedItems) => console.log(selectedItems)} multiSelect={true} @@ -64,9 +76,10 @@ export function TimeSheetFilterPopover() {
task} itemId={(task) => (task ? task.id : '')} @@ -78,9 +91,10 @@ export function TimeSheetFilterPopover() {
(status ? status.value : '')} itemId={(item) => item.value} @@ -91,6 +105,7 @@ export function TimeSheetFilterPopover() {
+ +
+
+ + + ); +} diff --git a/apps/web/lib/settings/task-statuses-form.tsx b/apps/web/lib/settings/task-statuses-form.tsx index 99ef5c37f..678e9b569 100644 --- a/apps/web/lib/settings/task-statuses-form.tsx +++ b/apps/web/lib/settings/task-statuses-form.tsx @@ -1,5 +1,5 @@ /* eslint-disable no-mixed-spaces-and-tabs */ -import { useModal, useRefetchData, useTaskStatus } from '@app/hooks'; +import { useModal, useRefetchData, useTaskStatus, useTeamTasks } from '@app/hooks'; import { IIcon, ITaskStatusItemList } from '@app/interfaces'; import { userState } from '@app/stores'; import { clsxm } from '@app/utils'; @@ -15,6 +15,7 @@ import IconPopover from './icon-popover'; import { StatusesListCard } from './list-card'; import SortTasksStatusSettings from '@components/pages/kanban/sort-tasks-status-settings'; import { StandardTaskStatusDropDown } from 'lib/features'; +import { DeleteTaskStatusConfirmationModal } from '../features/task-status/delete-status-confirmation-modal'; type StatusForm = { formOnly?: boolean; @@ -152,6 +153,9 @@ export const TaskStatusesForm = ({ ? updateArray.sort((a: any, b: any) => a.order - b.order) : []; const { isOpen, closeModal, openModal } = useModal(); + const {isOpen : isDeleteConfirmationOpen , closeModal : closeDeleteConfirmationModal, openModal : openDeleteConfirmationModal} = useModal() + const [statusToDelete, setStatusToDelete] = useState(null) + const {tasks} = useTeamTasks() return ( <> @@ -282,8 +286,20 @@ export const TaskStatusesForm = ({ setCreateNew(false); setEdit(status); }} - onDelete={() => { - deleteTaskStatus(status.id); + onDelete={async () => { + try { + const isStatusUsed = tasks.find( + (t) => t.status?.toLowerCase() === status.name?.toLowerCase() + ); + if (isStatusUsed) { + setStatusToDelete(status); + openDeleteConfirmationModal(); + } else { + await deleteTaskStatus(status.id); + } + } catch (error) { + console.error(error); + } }} isStatus={true} /> @@ -298,6 +314,7 @@ export const TaskStatusesForm = ({
+ {statusToDelete && setStatusToDelete(null)} status={statusToDelete} open={isDeleteConfirmationOpen} closeModal={closeDeleteConfirmationModal}/>} ); }; diff --git a/apps/web/locales/ar.json b/apps/web/locales/ar.json index d977cdb04..e05bce974 100644 --- a/apps/web/locales/ar.json +++ b/apps/web/locales/ar.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "لا يمكن تغيير نوع المهمة الملحمية.", "TASK_HAS_PARENT": "لا يمكن تغيير نوع المهمة حيث أن لديها بالفعل والد." }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "أنت على وشك حذف الحالة {statusName} التي يتم استخدامها من قبل المهام النشطة؛ يرجى تأكيد الإجراء" + }, "auth": { "SEND_CODE": "إرسال الرمز", "RESEND_CODE": "إعادة إرسال الرمز", diff --git a/apps/web/locales/bg.json b/apps/web/locales/bg.json index 8042b15b4..70bebf88c 100644 --- a/apps/web/locales/bg.json +++ b/apps/web/locales/bg.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "Епичният тип задача не може да бъде променен.", "TASK_HAS_PARENT": "Типът задача не може да бъде променен, тъй като задачата вече има родител." }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "Вие сте на път да изтриете състоянието {statusName}, което се използва от активни задачи; моля, потвърдете действието" + }, "auth": { "SEND_CODE": "изпрати код", "RESEND_CODE": "Изпрати код отново", diff --git a/apps/web/locales/de.json b/apps/web/locales/de.json index 43c1d47a4..e4938bb7b 100644 --- a/apps/web/locales/de.json +++ b/apps/web/locales/de.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "Epische Aufgabentypen können nicht geändert werden.", "TASK_HAS_PARENT": "Der Aufgabentyp kann nicht geändert werden, da die Aufgabe bereits ein übergeordnetes Element hat." }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "Sie sind dabei, den Status {statusName} zu löschen, der von aktiven Aufgaben verwendet wird; bitte bestätigen Sie die Aktion" + }, "auth": { "SEND_CODE": "Code senden", "RESEND_CODE": "Code erneut senden", diff --git a/apps/web/locales/en.json b/apps/web/locales/en.json index 52974d479..03961d2a3 100644 --- a/apps/web/locales/en.json +++ b/apps/web/locales/en.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "Epic Task Type can not be changed.", "TASK_HAS_PARENT": "Task Type can not be changed as Task has already Parent." }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "You are about to Delete the Status {statusName} that is used by active tasks; please confirm action" + }, "auth": { "SEND_CODE": "send code", "RESEND_CODE": "Resend Code", diff --git a/apps/web/locales/es.json b/apps/web/locales/es.json index 98ed6351d..ed2e54490 100644 --- a/apps/web/locales/es.json +++ b/apps/web/locales/es.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "La tarea de tipo Épico no se puede cambiar.", "TASK_HAS_PARENT": "El tipo de tarea no se puede cambiar porque la tarea ya tiene un padre." }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "Está a punto de eliminar el estado {statusName} que está siendo utilizado por tareas activas; por favor confirme la acción" + }, "auth": { "SEND_CODE": "enviar código", "RESEND_CODE": "Reenviar código", diff --git a/apps/web/locales/fr.json b/apps/web/locales/fr.json index e0ce01e92..69179edce 100644 --- a/apps/web/locales/fr.json +++ b/apps/web/locales/fr.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "Le type de tâche épique ne peut pas être modifié.", "TASK_HAS_PARENT": "Le type de tâche ne peut pas être modifié car la tâche a déjà un parent." }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "Vous êtes sur le point de supprimer le statut {statusName} qui est utilisé par des tâches actives ; veuillez confirmer l'action" + }, "auth": { "SEND_CODE": "envoyer le code", "RESEND_CODE": "Renvoyer le code", diff --git a/apps/web/locales/he.json b/apps/web/locales/he.json index 2762188c2..83c9b1312 100644 --- a/apps/web/locales/he.json +++ b/apps/web/locales/he.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "לא ניתן לשנות סוג משימה אפית.", "TASK_HAS_PARENT": "לא ניתן לשנות סוג משימה מכיוון שלמשימה כבר יש הורה." }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "אתה עומד למחוק את המצב {statusName} המשמש במשימות פעילות; נא לאשר פעולה" + }, "auth": { "SEND_CODE": "שלח קוד", "RESEND_CODE": "שלח קוד מחדש", diff --git a/apps/web/locales/it.json b/apps/web/locales/it.json index 79e55f0d3..90b440c68 100644 --- a/apps/web/locales/it.json +++ b/apps/web/locales/it.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "Il tipo di Compito Epico non può essere cambiato.", "TASK_HAS_PARENT": "Il tipo di Compito non può essere cambiato poiché il Compito ha già un Genitore." }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "Stai per eliminare lo stato {statusName} utilizzato da attività attive; per favore conferma l'azione" + }, "auth": { "SEND_CODE": "Invia Codice", "RESEND_CODE": "Reinvia Codice", diff --git a/apps/web/locales/nl.json b/apps/web/locales/nl.json index 8919befc3..90b22103b 100644 --- a/apps/web/locales/nl.json +++ b/apps/web/locales/nl.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "Epic taaktype kan niet worden gewijzigd.", "TASK_HAS_PARENT": "Taaktype kan niet worden gewijzigd omdat de taak al een bovenliggende taak heeft." }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "U staat op het punt de status {statusName} te verwijderen die wordt gebruikt door actieve taken; bevestig alstublieft de actie" + }, "auth": { "SEND_CODE": "code verzenden", "RESEND_CODE": "Code opnieuw verzenden", diff --git a/apps/web/locales/pl.json b/apps/web/locales/pl.json index fb9a1170a..afabe281f 100644 --- a/apps/web/locales/pl.json +++ b/apps/web/locales/pl.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "Typ Zadania jako 'Epic' nie może zostać zmieniony.", "TASK_HAS_PARENT": "Typ Zadania nie może zostać zmieniony, ponieważ Zadanie ma już przypisanego rodzica." }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "Zaraz usuniesz status {statusName}, który jest używany przez aktywne zadania; proszę potwierdzić akcję" + }, "auth": { "SEND_CODE": "wyślij kod", "RESEND_CODE": "Wyślij ponownie kod", diff --git a/apps/web/locales/pt.json b/apps/web/locales/pt.json index f1a2201b2..63c3f6cf7 100644 --- a/apps/web/locales/pt.json +++ b/apps/web/locales/pt.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "O Tipo de Tarefa Épica não pode ser alterado.", "TASK_HAS_PARENT": "O Tipo de Tarefa não pode ser alterado, pois a Tarefa já possui um Pai." }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "Está prestes a eliminar o status {statusName} que está a ser usado por tarefas ativas; por favor, confirme a ação" + }, "auth": { "SEND_CODE": "enviar código", "RESEND_CODE": "Reenviar Código", diff --git a/apps/web/locales/ru.json b/apps/web/locales/ru.json index f0afd994e..8f2e0f7e5 100644 --- a/apps/web/locales/ru.json +++ b/apps/web/locales/ru.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "Тип задачи 'Epic' не может быть изменен.", "TASK_HAS_PARENT": "Тип задачи не может быть изменен, так как у задачи уже есть родитель." }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "Вы собираетесь удалить статус {statusName}, который используется активными задачами; пожалуйста, подтвердите действие" + }, "auth": { "SEND_CODE": "отправить код", "RESEND_CODE": "Отправить код повторно", diff --git a/apps/web/locales/zh.json b/apps/web/locales/zh.json index 1b63a666f..4484dfded 100644 --- a/apps/web/locales/zh.json +++ b/apps/web/locales/zh.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "史诗类型任务无法更改。", "TASK_HAS_PARENT": "任务已有父任务,无法更改任务类型。" }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "您即将删除正在被活跃任务使用的状态 {statusName};请确认操作" + }, "auth": { "SEND_CODE": "发送验证码", "RESEND_CODE": "重新发送验证码", From 47212e412448ba02c62c56666815c020e69ac606 Mon Sep 17 00:00:00 2001 From: "Thierry CH." Date: Thu, 14 Nov 2024 23:35:52 +0200 Subject: [PATCH 08/13] change position of 'Delete this plan' button (#3322) --- apps/web/lib/features/user-profile-plans.tsx | 141 ++++++++++--------- 1 file changed, 76 insertions(+), 65 deletions(-) diff --git a/apps/web/lib/features/user-profile-plans.tsx b/apps/web/lib/features/user-profile-plans.tsx index fae576c33..e9c8a5153 100644 --- a/apps/web/lib/features/user-profile-plans.tsx +++ b/apps/web/lib/features/user-profile-plans.tsx @@ -58,7 +58,16 @@ export function UserProfilePlans() { const t = useTranslations(); const profile = useUserProfilePage(); - const { todayPlan, futurePlans, pastPlans, outstandingPlans, sortedPlans, profileDailyPlans } = useDailyPlan(); + const { + todayPlan, + futurePlans, + pastPlans, + outstandingPlans, + sortedPlans, + profileDailyPlans, + deleteDailyPlan, + deleteDailyPlanLoading + } = useDailyPlan(); const fullWidth = useAtomValue(fullWidthState); const [currentOutstanding, setCurrentOutstanding] = useLocalStorageState('outstanding', 'ALL'); const [currentTab, setCurrentTab] = useLocalStorageState('daily-plan-tab', 'Today Tasks'); @@ -85,6 +94,8 @@ export function UserProfilePlans() { const { hasPlan } = useTimer(); const { activeTeam } = useTeamTasks(); const requirePlan = useMemo(() => activeTeam?.requirePlanToTrack, [activeTeam?.requirePlanToTrack]); + const [popupOpen, setPopupOpen] = useState(false); + const canSeeActivity = useCanSeeActivityScreen(); // Set the tab plan tab to outstanding if user has no daily plan and there are outstanding tasks (on first load) useEffect(() => { @@ -132,7 +143,7 @@ export function UserProfilePlans() { {profileDailyPlans?.items?.length > 0 ? (
-
+
{Object.keys(tabsScreens).map((filter, i) => (
{i !== 0 && } @@ -173,6 +184,66 @@ export function UserProfilePlans() { ))}
+ {currentTab === 'Today Tasks' && todayPlan[0] && ( + <> + {canSeeActivity ? ( +
+ { + setPopupOpen((prev) => !prev); + }} + 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" + > + {t('common.plan.DELETE_THIS_PLAN')} + + } + > + {/*button confirm*/} + + {/*button cancel*/} + + +
+ ) : ( + <> + )} + + )} {currentTab === 'Outstanding' && ( table.getColumn('title')?.setFilterValue(event.target.value)} - className="max-w-sm pl-10 bg-transparent border-none placeholder:font-normal" + className="max-w-sm pl-10 bg-transparent border-none dark:focus-visible:!border-[#c8c8c8] transition-all duration-200 placeholder:font-normal" />
diff --git a/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilterDate.tsx b/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilterDate.tsx index db5d266a8..9d5888f63 100644 --- a/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilterDate.tsx +++ b/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilterDate.tsx @@ -175,7 +175,7 @@ export function TimesheetFilterDate({
- - {/*
- - { - activeTeam ? - setTeam(team)} - itemId={(team) => (team ? team.id : '')} - itemToString={(team) => (team ? team.name : '')} - triggerClassName="border-gray-300 dark:border-slate-600" - /> - : - <> - } -
*/} - {params === 'AddManuelTime' ? ( <> Date: Thu, 14 Nov 2024 23:47:34 +0200 Subject: [PATCH 13/13] improve arrow navigation on 'see plans' modal (#3337) * improve arrow navigation on 'see plans' modal * add requested changes --- .../features/daily-plan/all-plans-modal.tsx | 95 ++++++++++++++++--- 1 file changed, 80 insertions(+), 15 deletions(-) diff --git a/apps/web/lib/features/daily-plan/all-plans-modal.tsx b/apps/web/lib/features/daily-plan/all-plans-modal.tsx index 63bebcaef..0fb7db8cd 100644 --- a/apps/web/lib/features/daily-plan/all-plans-modal.tsx +++ b/apps/web/lib/features/daily-plan/all-plans-modal.tsx @@ -19,7 +19,8 @@ interface IAllPlansModal { isOpen: boolean; } -type CalendarTab = 'Today' | 'Tomorrow' | 'Calendar'; +type TCalendarTab = 'Today' | 'Tomorrow' | 'Calendar'; +type TNavigationMode = 'DATE' | 'PLAN'; /** * A modal that displays all the plans available to the user (Today, Tomorrow and Future). @@ -32,17 +33,41 @@ type CalendarTab = 'Today' | 'Tomorrow' | 'Calendar'; * @returns {JSX.Element} The modal element */ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal) { + // Utility function for checking if two dates are the same + const isSameDate = useCallback((date1: Date | number | string, date2: Date | number | string) => { + return moment(date1).toISOString().split('T')[0] === moment(date2).toISOString().split('T')[0]; + }, []); + const { isOpen, closeModal } = props; const [showCalendar, setShowCalendar] = useState(false); const [showCustomPlan, setShowCustomPlan] = useState(false); const [customDate, setCustomDate] = useState(moment().toDate()); const { myDailyPlans, pastPlans } = useDailyPlan(); const t = useTranslations(); - - // Utility function for checking if two dates are the same - const isSameDate = useCallback((date1: Date | number | string, date2: Date | number | string) => { - return moment(date1).toISOString().split('T')[0] === moment(date2).toISOString().split('T')[0]; - }, []); + const [navigationMode, setNavigationMode] = useState('PLAN'); + const sortedPlans = useMemo( + () => + [...myDailyPlans.items].sort((plan1, plan2) => + new Date(plan1.date).getTime() > new Date(plan2.date).getTime() ? 1 : -1 + ), + [myDailyPlans.items] + ); + const currentPlanIndex = useMemo( + () => sortedPlans.findIndex((plan) => isSameDate(plan.date, moment(customDate).toDate())), + // eslint-disable-next-line react-hooks/exhaustive-deps + [customDate, myDailyPlans.items] + ); + const nextPlan = useMemo( + () => + currentPlanIndex >= 0 && currentPlanIndex < myDailyPlans.items.length - 1 + ? sortedPlans[currentPlanIndex + 1] + : null, + [currentPlanIndex, myDailyPlans.items.length, sortedPlans] + ); + const previousPlan = useMemo( + () => (currentPlanIndex > 0 ? sortedPlans[currentPlanIndex - 1] : null), + [currentPlanIndex, sortedPlans] + ); // Memoize today, tomorrow, and future plans const todayPlan = useMemo( @@ -70,21 +95,27 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal) }, [closeModal]); // Define tabs for plan selection - const tabs: CalendarTab[] = useMemo(() => ['Today', 'Tomorrow', 'Calendar'], []); + const tabs: TCalendarTab[] = useMemo(() => ['Today', 'Tomorrow', 'Calendar'], []); // State to track the active tab const [selectedTab, setSelectedTab] = useState(tabs[0]); // Handle tab switching - const handleTabClick = (tab: CalendarTab) => { + const handleTabClick = (tab: TCalendarTab) => { if (tab === 'Today') { setCustomDate(moment().toDate()); + setNavigationMode('PLAN'); } else if (tab === 'Tomorrow') { setCustomDate(moment().add(1, 'days').toDate()); + setNavigationMode('PLAN'); } setSelectedTab(tab); setShowCalendar(tab === 'Calendar'); setShowCustomPlan(false); + + if (tab === 'Calendar') { + setNavigationMode('DATE'); + } }; // Determine which plan to display based on the selected tab @@ -117,6 +148,8 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal) setShowCalendar(false); setShowCustomPlan(true); } + + setNavigationMode('PLAN'); } }, [customDate, isSameDate]); @@ -155,12 +188,12 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal) if (selectedPlan) { if (isSameDate(date, moment().startOf('day').toDate())) { - setSelectedTab('Today'); + navigationMode == 'PLAN' && setSelectedTab('Today'); } else if (isSameDate(date, moment().add(1, 'days').startOf('day').toDate())) { - setSelectedTab('Tomorrow'); + navigationMode == 'PLAN' && setSelectedTab('Tomorrow'); } else { setSelectedTab('Calendar'); - if (existPlan) { + if (existPlan && navigationMode == 'PLAN') { setShowCalendar(false); setShowCustomPlan(true); } else { @@ -171,11 +204,35 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal) } } }, - [isSameDate, myDailyPlans.items, selectedPlan] + [isSameDate, myDailyPlans.items, navigationMode, selectedPlan] + ); + + // Handle navigation between plans + const moveBetweenPlans = useCallback( + (direction: boolean) => { + if (direction) { + // Select the next plan + if (nextPlan) { + setCustomDate(moment(nextPlan.date).toDate()); + setSelectedTab('Calendar'); + setShowCalendar(false); + setShowCustomPlan(true); + } + } else { + // Select the previous plan + if (previousPlan) { + setCustomDate(moment(previousPlan.date).toDate()); + setSelectedTab('Calendar'); + setShowCalendar(false); + setShowCustomPlan(true); + } + } + }, + [nextPlan, previousPlan] ); // A handler function to display the plan title - const displayPlanTitle = (selectedTab: CalendarTab, selectedPlan?: IDailyPlan) => { + const displayPlanTitle = (selectedTab: TCalendarTab, selectedPlan?: IDailyPlan) => { const isCalendarTab = selectedTab === 'Calendar'; const planDate = selectedPlan?.date ? new Date(selectedPlan.date).toLocaleDateString('en-GB') : ''; const hasTasks = selectedPlan?.tasks?.length; @@ -247,14 +304,22 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal)
arrowNavigationHandler(moment(customDate).subtract(1, 'days').toDate())} + onClick={() => + navigationMode === 'DATE' + ? arrowNavigationHandler(moment(customDate).subtract(1, 'days').toDate()) + : moveBetweenPlans(false) + } className="rotate-180 cursor-pointer px-2 h-full flex items-center justify-center" > arrowNavigationHandler(moment(customDate).add(1, 'days').toDate())} + onClick={() => + navigationMode === 'DATE' + ? arrowNavigationHandler(moment(customDate).add(1, 'days').toDate()) + : moveBetweenPlans(true) + } className=" h-full cursor-pointer flex px-2 items-center justify-center" >