diff --git a/apps/web/app/hooks/features/useDailyPlan.ts b/apps/web/app/hooks/features/useDailyPlan.ts
index de33fb21a..7f46646fa 100644
--- a/apps/web/app/hooks/features/useDailyPlan.ts
+++ b/apps/web/app/hooks/features/useDailyPlan.ts
@@ -108,11 +108,11 @@ export function useDailyPlan() {
user?.tenantId || ''
);
//Check if there is an existing plan
- const isPlanExist = profileDailyPlans.items.find((plan) =>
+ const isPlanExist = [...(profileDailyPlans.items ? profileDailyPlans.items : [])].find((plan) =>
plan.date?.toString()?.startsWith(new Date(data.date)?.toISOString().split('T')[0])
);
if (isPlanExist) {
- const updatedPlans = profileDailyPlans.items.map((plan) => {
+ const updatedPlans = [...(profileDailyPlans.items ? profileDailyPlans.items : [])].map((plan) => {
if (plan.date?.toString()?.startsWith(new Date(data.date)?.toISOString().split('T')[0])) {
return res.data;
}
@@ -127,11 +127,11 @@ export function useDailyPlan() {
} else {
setProfileDailyPlans({
total: profileDailyPlans.total + 1,
- items: [...profileDailyPlans.items, res.data]
+ items: [...(profileDailyPlans.items ? profileDailyPlans.items : []), res.data]
});
}
- setEmployeePlans([...employeePlans, res.data]);
+ setEmployeePlans([...(employeePlans ? employeePlans : []), res.data]);
getMyDailyPlans();
return res;
}
@@ -151,8 +151,10 @@ export function useDailyPlan() {
const updateDailyPlan = useCallback(
async (data: IUpdateDailyPlan, planId: string) => {
const res = await updateQueryCall(data, planId);
- const updated = profileDailyPlans.items.filter((plan) => plan.id != planId);
- const updatedEmployee = employeePlans.filter((plan) => plan.id != planId);
+ const updated = [...(profileDailyPlans.items ? profileDailyPlans.items : [])].filter(
+ (plan) => plan.id != planId
+ );
+ const updatedEmployee = [...(employeePlans ? employeePlans : [])].filter((plan) => plan.id != planId);
setProfileDailyPlans({
total: profileDailyPlans.total,
items: [...updated, res.data]
@@ -178,8 +180,10 @@ export function useDailyPlan() {
const addTaskToPlan = useCallback(
async (data: IDailyPlanTasksUpdate, planId: string) => {
const res = await addTaskToPlanQueryCall(data, planId);
- const updated = profileDailyPlans.items.filter((plan) => plan.id != planId);
- const updatedEmployee = employeePlans.filter((plan) => plan.id != planId);
+ const updated = [...(profileDailyPlans.items ? profileDailyPlans.items : [])].filter(
+ (plan) => plan.id != planId
+ );
+ const updatedEmployee = [...(employeePlans ? employeePlans : [])].filter((plan) => plan.id != planId);
setProfileDailyPlans({
total: profileDailyPlans.total,
items: [...updated, res.data]
@@ -201,8 +205,10 @@ export function useDailyPlan() {
const removeTaskFromPlan = useCallback(
async (data: IDailyPlanTasksUpdate, planId: string) => {
const res = await removeTAskFromPlanQueryCall(data, planId);
- const updated = profileDailyPlans.items.filter((plan) => plan.id != planId);
- const updatedEmployee = employeePlans.filter((plan) => plan.id != planId);
+ const updated = [...(profileDailyPlans.items ? profileDailyPlans.items : [])].filter(
+ (plan) => plan.id != planId
+ );
+ const updatedEmployee = [...(employeePlans ? employeePlans : [])].filter((plan) => plan.id != planId);
setProfileDailyPlans({
total: profileDailyPlans.total,
items: [...updated, res.data]
@@ -224,14 +230,14 @@ export function useDailyPlan() {
const removeManyTaskPlans = useCallback(
async (data: IRemoveTaskFromManyPlans, taskId: string) => {
const res = await removeManyTaskPlanQueryCall({ taskId, data });
- const updatedProfileDailyPlans = profileDailyPlans.items
+ const updatedProfileDailyPlans = [...(profileDailyPlans.items ? profileDailyPlans.items : [])]
.map((plan) => {
const updatedTasks = plan.tasks ? plan.tasks.filter((task) => task.id !== taskId) : [];
return { ...plan, tasks: updatedTasks };
})
.filter((plan) => plan.tasks && plan.tasks.length > 0);
// Delete plans without tasks
- const updatedEmployeePlans = employeePlans
+ const updatedEmployeePlans = [...(employeePlans ? employeePlans : [])]
.map((plan) => {
const updatedTasks = plan.tasks ? plan.tasks.filter((task) => task.id !== taskId) : [];
return { ...plan, tasks: updatedTasks };
@@ -259,8 +265,10 @@ export function useDailyPlan() {
const deleteDailyPlan = useCallback(
async (planId: string) => {
const res = await deleteDailyPlanQueryCall(planId);
- const updated = profileDailyPlans.items.filter((plan) => plan.id != planId);
- const updatedEmployee = employeePlans.filter((plan) => plan.id != planId);
+ const updated = [...(profileDailyPlans.items ? profileDailyPlans.items : [])].filter(
+ (plan) => plan.id != planId
+ );
+ const updatedEmployee = [...(employeePlans ? employeePlans : [])].filter((plan) => plan.id != planId);
setProfileDailyPlans({ total: updated.length, items: [...updated] });
setEmployeePlans([...updatedEmployee]);
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 669c5c7b9..1413d496d 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
@@ -21,6 +21,7 @@ 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';
+import { UnplanActiveTaskModal } from './unplan-active-task-modal';
/**
* A modal that allows user to add task estimation / planned work time, etc.
@@ -76,6 +77,10 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa
[activeTeamTask?.id, plan.tasks]
);
const [isWorkingTimeInputFocused, setWorkingTimeInputFocused] = useState(false);
+ const [planEditState, setPlanEditState] = useState<{ draft: boolean; saved: boolean }>({
+ draft: false,
+ saved: false
+ });
const canStartWorking = useMemo(() => {
const isTodayPlan =
@@ -139,7 +144,9 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa
// Update the plan work time only if the user changed it
plan.workTimePlanned !== workTimePlanned && (await updateDailyPlan({ workTimePlanned }, plan.id ?? ''));
- if (canStartWorking) {
+ setPlanEditState({ draft: false, saved: true });
+
+ if (canStartWorking && !timerStatus?.running) {
handleChangeActiveTask();
if (isRenderedInSoftFlow) {
@@ -152,14 +159,15 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa
setLoading(false);
}
}, [
+ plan.workTimePlanned,
+ plan.id,
+ workTimePlanned,
+ updateDailyPlan,
canStartWorking,
+ timerStatus?.running,
handleChangeActiveTask,
- handleCloseModal,
- plan.id,
- plan.workTimePlanned,
isRenderedInSoftFlow,
- updateDailyPlan,
- workTimePlanned
+ handleCloseModal
]);
/**
@@ -235,7 +243,7 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa
const StartWorkingButton = (
) : canStartWorking ? (
- t('timer.todayPlanSettings.START_WORKING_BUTTON')
+ timerStatus?.running && planEditState.draft ? (
+ 'Save Changes'
+ ) : (
+ t('timer.todayPlanSettings.START_WORKING_BUTTON')
+ )
) : (
'Edit plan'
)}
@@ -301,6 +313,14 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa
!isNaN(parseInt(e.target.value))
? setWorkTimePlanned(parseInt(e.target.value))
: setWorkTimePlanned(0);
+
+ if (
+ parseInt(e.target.value) !== parseInt(plan.workTimePlanned.toString())
+ ) {
+ setPlanEditState({ draft: true, saved: false });
+ } else {
+ setPlanEditState({ draft: false, saved: false });
+ }
}}
required
noWrapper
@@ -621,6 +641,11 @@ function TaskCard(props: ITaskCardProps) {
closeModal: closeTaskDetailsModal,
openModal: openTaskDetailsModal
} = useModal();
+ const {
+ isOpen: isUnplanActiveTaskModalOpen,
+ closeModal: closeUnplanActiveTaskModal,
+ openModal: openUnplanActiveTaskModal
+ } = useModal();
const handleOpenTaskDetailsModal = useCallback(() => {
// Update the detailed task state
@@ -629,8 +654,8 @@ function TaskCard(props: ITaskCardProps) {
}, [getTaskById, openTaskDetailsModal, task.id]);
const t = useTranslations();
-
const status = useTaskStatus();
+ const { activeTeamTask } = useTeamTasks();
/**
* The function that adds the task to the selected plan
@@ -647,8 +672,6 @@ function TaskCard(props: ITaskCardProps) {
}
}, [addTaskToPlan, plan.id, task.id]);
- console.log(status.taskStatus);
-
return (
@@ -704,6 +728,14 @@ function TaskCard(props: ITaskCardProps) {
)}
+ {activeTeamTask && (
+
+ )}
);
}
@@ -712,6 +744,7 @@ interface ITaskCardActionsProps {
task: ITeamTask;
selectedPlan: IDailyPlan;
openTaskDetailsModal: () => void;
+ openUnplanActiveTaskModal: () => void;
}
/**
@@ -721,15 +754,17 @@ interface ITaskCardActionsProps {
* @param {ITeamTask} props.task - The task on which actions will be performed
* @param {IDailyPlan} props.selectedPlan - The currently selected plan
* @param {() => void} props.openTaskDetailsModal - A function that opens a task details modal
+ * @param {() => void} props.openUnplanActiveTaskModal - A function to open the unplanActiveTask modal
*
* @returns {JSX.Element} The Popover component.
*/
function TaskCardActions(props: ITaskCardActionsProps) {
- const { task, selectedPlan, openTaskDetailsModal } = props;
+ const { task, selectedPlan, openTaskDetailsModal, openUnplanActiveTaskModal } = props;
const { user } = useAuthenticateUser();
const { futurePlans, todayPlan, removeTaskFromPlan, removeTaskFromPlanLoading } = useDailyPlan();
-
+ const { activeTeamTask } = useTeamTasks();
+ const { timerStatus } = useTimerView();
const otherPlanIds = useMemo(
() =>
[...futurePlans, ...todayPlan]
@@ -739,6 +774,12 @@ function TaskCardActions(props: ITaskCardActionsProps) {
.map((plan) => plan.id!),
[futurePlans, selectedPlan.id, task.id, todayPlan]
);
+ const isTodayPLan = useMemo(
+ () =>
+ new Date(selectedPlan?.date).toLocaleDateString('en') ==
+ new Date(todayPlan[0]?.date).toLocaleDateString('en'),
+ [selectedPlan.date, todayPlan]
+ );
/**
* Unplan selected task
@@ -746,15 +787,39 @@ function TaskCardActions(props: ITaskCardActionsProps) {
const unplanSelectedDate = useCallback(
async (closePopover: () => void) => {
try {
- selectedPlan?.id &&
- (await removeTaskFromPlan({ taskId: task.id, employeeId: user?.employee.id }, selectedPlan?.id));
+ if (task.id == activeTeamTask?.id) {
+ if (timerStatus?.running && isTodayPLan) {
+ openUnplanActiveTaskModal();
+ } else {
+ selectedPlan?.id &&
+ (await removeTaskFromPlan(
+ { taskId: task.id, employeeId: user?.employee.id },
+ selectedPlan?.id
+ ));
+ }
+ } else {
+ selectedPlan?.id &&
+ (await removeTaskFromPlan(
+ { taskId: task.id, employeeId: user?.employee.id },
+ selectedPlan?.id
+ ));
+ }
closePopover();
} catch (error) {
console.log(error);
}
},
- [removeTaskFromPlan, selectedPlan.id, task.id, user?.employee.id]
+ [
+ activeTeamTask?.id,
+ isTodayPLan,
+ openUnplanActiveTaskModal,
+ removeTaskFromPlan,
+ selectedPlan.id,
+ task.id,
+ timerStatus?.running,
+ user?.employee.id
+ ]
);
return (
@@ -795,6 +860,9 @@ function TaskCardActions(props: ITaskCardActionsProps) {
selectedPlanId={selectedPlan.id}
planIds={[selectedPlan.id, ...otherPlanIds]}
closeActionPopover={close}
+ openUnplanActiveTaskModal={openUnplanActiveTaskModal}
+ unplanSelectedDate={unplanSelectedDate}
+ unPlanSelectedDateLoading={removeTaskFromPlanLoading}
/>
) : (
void;
+ unplanSelectedDate: (closePopover: () => void) => Promise;
+ unPlanSelectedDateLoading: boolean;
+ openUnplanActiveTaskModal: () => void;
}
/**
@@ -841,32 +912,30 @@ interface IUnplanTaskProps {
* @param {string} props.selectedPlanId - The currently selected plan id
* @param {string[]} [props.planIds] - Others plans's ids
* @param {() => void} props.closeActionPopover - The function to close the task card actions popover
+ * @param {(taskIds: string[]) => void} props.wantsToUnplanActiveTask - Will be called when the user wants to unplan the activeTas
+ * @param {() => void} props.openUnplanActiveTaskModal - A function to open the unplanActiveTask modal
+ *
+ *
*
* @returns {JSX.Element} The Popover component.
*/
function UnplanTask(props: IUnplanTaskProps) {
- const { taskId, selectedPlanId, planIds, closeActionPopover } = props;
+ const {
+ taskId,
+ planIds,
+ closeActionPopover,
+ unplanSelectedDate,
+ openUnplanActiveTaskModal,
+ unPlanSelectedDateLoading
+ } = props;
const { user } = useAuthenticateUser();
- const { removeTaskFromPlan, removeTaskFromPlanLoading, removeManyTaskPlans, removeManyTaskFromPlanLoading } =
- useDailyPlan();
-
- /**
- * Unplan selected task
- */
- const unplanSelectedDate = useCallback(
- async (closePopover: () => void) => {
- try {
- await removeTaskFromPlan({ taskId: taskId, employeeId: user?.employee.id }, selectedPlanId);
-
- closePopover();
- // Close the task card actions popover as well
- closeActionPopover();
- } catch (error) {
- console.log(error);
- }
- },
- [closeActionPopover, removeTaskFromPlan, selectedPlanId, taskId, user?.employee.id]
+ const { removeManyTaskPlans, removeManyTaskFromPlanLoading, todayPlan } = useDailyPlan();
+ const { activeTeamTask } = useTeamTasks();
+ const { timerStatus } = useTimerView();
+ const isActiveTaskPlannedToday = useMemo(
+ () => todayPlan[0].tasks?.find((task) => task.id === activeTeamTask?.id),
+ [activeTeamTask?.id, todayPlan]
);
/**
@@ -875,7 +944,17 @@ function UnplanTask(props: IUnplanTaskProps) {
const unplanAll = useCallback(
async (closePopover: () => void) => {
try {
- await removeManyTaskPlans({ plansIds: planIds, employeeId: user?.employee.id }, taskId);
+ // First, check if the user wants to unplan the active task
+ if (taskId == activeTeamTask?.id) {
+ if (timerStatus?.running && isActiveTaskPlannedToday) {
+ openUnplanActiveTaskModal();
+ // TODO: Unplan from all plans after clicks 'YES'
+ } else {
+ await removeManyTaskPlans({ plansIds: planIds, employeeId: user?.employee.id }, taskId);
+ }
+ } else {
+ await removeManyTaskPlans({ plansIds: planIds, employeeId: user?.employee.id }, taskId);
+ }
closePopover();
// Close the task card actions popover as well
@@ -884,7 +963,17 @@ function UnplanTask(props: IUnplanTaskProps) {
console.log(error);
}
},
- [closeActionPopover, planIds, removeManyTaskPlans, taskId, user?.employee.id]
+ [
+ activeTeamTask?.id,
+ closeActionPopover,
+ isActiveTaskPlannedToday,
+ openUnplanActiveTaskModal,
+ planIds,
+ removeManyTaskPlans,
+ taskId,
+ timerStatus?.running,
+ user?.employee.id
+ ]
);
return (
@@ -914,10 +1003,10 @@ function UnplanTask(props: IUnplanTaskProps) {
onClick={() => unplanSelectedDate(close)}
className={clsxm(
'shrink-0',
- !removeTaskFromPlanLoading && 'hover:font-semibold hover:transition-all '
+ !unPlanSelectedDateLoading && 'hover:font-semibold hover:transition-all '
)}
>
- {removeTaskFromPlanLoading ? (
+ {unPlanSelectedDateLoading ? (
) : (
'Unplan selected date'
diff --git a/apps/web/lib/features/daily-plan/unplan-active-task-modal.tsx b/apps/web/lib/features/daily-plan/unplan-active-task-modal.tsx
new file mode 100644
index 000000000..4b95c1364
--- /dev/null
+++ b/apps/web/lib/features/daily-plan/unplan-active-task-modal.tsx
@@ -0,0 +1,83 @@
+import { useAuthenticateUser, useDailyPlan, useTimerView } from '@app/hooks';
+import { IDailyPlan, ITeamTask } from '@app/interfaces';
+import { Button, Card, Modal, Text } from 'lib/components';
+import { useCallback } from 'react';
+
+interface UnplanActiveTaskModalProps {
+ open: boolean;
+ closeModal: () => void;
+ task: ITeamTask;
+ plan: IDailyPlan;
+}
+
+/**
+ * A Modal that gives the possibility to unplan the active task.
+ *
+ * @param {Object} props - The props Object
+ * @param {boolean} props.open - If true open the modal otherwise close the modal
+ * @param {() => void} props.closeModal - A function to close the modal
+ * @param {ITeamTask} props.task - The task to unplan
+ * @param {IDailyPlan} props.plan - The today's plan
+ *
+ * @returns {JSX.Element} The modal element
+ */
+export function UnplanActiveTaskModal(props: UnplanActiveTaskModalProps) {
+ const { closeModal, task, open, plan } = props;
+ const { user } = useAuthenticateUser();
+ const { removeTaskFromPlan, removeTaskFromPlanLoading } = useDailyPlan();
+ const { stopTimer, timerStatus } = useTimerView();
+
+ const handleCloseModal = useCallback(() => {
+ closeModal();
+ }, [closeModal]);
+
+ const handleUnplanTask = useCallback(async () => {
+ try {
+ plan.id && (await removeTaskFromPlan({ taskId: task.id, employeeId: user?.employee.id }, plan.id));
+ } catch (error) {
+ console.log(error);
+ }
+ }, [plan.id, removeTaskFromPlan, task.id, user?.employee.id]);
+
+ // The function that will be called when the user clicks on 'YES' button
+ const onYes = useCallback(async () => {
+ if (timerStatus?.running) {
+ stopTimer();
+ }
+ await handleUnplanTask();
+ handleCloseModal();
+ }, [handleCloseModal, handleUnplanTask, stopTimer, timerStatus?.running]);
+
+ return (
+
+
+
+
+ You are about to unplan the current active task, please confirm the action
+
+
+
+
+
+
+
+
+ );
+}