diff --git a/package-lock.json b/package-lock.json index 0de4bd689..1d4af7ae4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "dexie-export-import": "^4.0.7", "i18next": "^21.6.11", "i18next-browser-languagedetector": "^6.1.3", - "moment": "^2.29.2", + "moment": "^2.30.1", "react": "^17.0.2", "react-dom": "^17.0.2", "react-i18next": "^11.15.4", @@ -13565,8 +13565,9 @@ } }, "node_modules/moment": { - "version": "2.29.4", - "license": "MIT", + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "engines": { "node": "*" } diff --git a/package.json b/package.json index 407922a96..e45b18cec 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "dexie-export-import": "^4.0.7", "i18next": "^21.6.11", "i18next-browser-languagedetector": "^6.1.3", - "moment": "^2.29.2", + "moment": "^2.30.1", "react": "^17.0.2", "react-dom": "^17.0.2", "react-i18next": "^11.15.4", diff --git a/src/Interfaces/IScheduler.ts b/src/Interfaces/IScheduler.ts index 77c91a472..cba5d7cb9 100644 --- a/src/Interfaces/IScheduler.ts +++ b/src/Interfaces/IScheduler.ts @@ -43,8 +43,8 @@ export interface ISchedulerInputGoal { afterTime?: number; beforeTime?: number; onDays?: string[]; - notOn?: blockedSlotOfTask[]; }; + notOn?: blockedSlotOfTask[]; repeat?: string; budget?: { minPerDay?: number; diff --git a/src/common/Icon.tsx b/src/common/Icon.tsx index 82433e877..3a7fbd33d 100644 --- a/src/common/Icon.tsx +++ b/src/common/Icon.tsx @@ -648,6 +648,14 @@ const Icon: React.FC = ({ title, active }) => { ); + case "Clock": + return ( + + + + + ); + default: return null; } diff --git a/src/components/GoalsComponents/MyGoalActions/MyGoalActions.scss b/src/components/GoalsComponents/MyGoalActions/MyGoalActions.scss index cdcc41036..01d1f580e 100644 --- a/src/components/GoalsComponents/MyGoalActions/MyGoalActions.scss +++ b/src/components/GoalsComponents/MyGoalActions/MyGoalActions.scss @@ -7,7 +7,7 @@ .goal-action { width: unset !important; border: none; - border-radius: 0 !important; + border-radius: 0; cursor: pointer; background: var(--bottom-nav-color) !important; } @@ -37,7 +37,7 @@ border-radius: 0 0 0 28px !important; } .goal-action:last-child { - border-radius: 0 0 28px 0 !important; + border-radius: 0 0 28px 0; } .goal-action-archive { diff --git a/src/components/MyTimeComponents/MyTimeline/MyTimeline.tsx b/src/components/MyTimeComponents/MyTimeline/MyTimeline.tsx index 9e8005b58..5f45dc7ca 100644 --- a/src/components/MyTimeComponents/MyTimeline/MyTimeline.tsx +++ b/src/components/MyTimeComponents/MyTimeline/MyTimeline.tsx @@ -20,6 +20,7 @@ import { displayToast, lastAction, focusTaskTitle } from "@src/store"; import { addTask, completeTask, forgetTask, getTaskByGoalId } from "@src/api/TasksAPI"; import "./index.scss"; +import { displayReschedule } from "@src/store/TaskState"; import { GoalTiming } from "./GoalTiming"; import { TaskOptions } from "./TaskOptions"; import { updateImpossibleGoals } from "./updateImpossibleGoals"; @@ -53,6 +54,7 @@ export const MyTimeline: React.FC = ({ day, myTasks, taskDetail const setShowToast = useSetRecoilState(displayToast); const setLastAction = useSetRecoilState(lastAction); const setTaskTitle = useSetRecoilState(focusTaskTitle); + const setOpenReschedule = useSetRecoilState(displayReschedule); const [displayOptionsIndex, setDisplayOptionsIndex] = useState("root"); @@ -103,25 +105,30 @@ export const MyTimeline: React.FC = ({ day, myTasks, taskDetail if (day === "Today") { const taskItem = await getTaskByGoalId(task.goalid); if (!taskItem) { + console.log("task not found"); + await addTask({ id: uuidv4(), goalId: task.goalid, title: task.title, - completedTodayIds: [task.taskid], + completedTodayIds: [], forgotToday: actionName === "Skip" ? [`${getHrFromDateString(task.start)}-${getHrFromDateString(task.deadline)}`] : [], completedToday: actionName === "Done" ? Number(task.duration) : 0, lastForget: actionName === "Skip" ? new Date().toLocaleDateString() : "", lastCompleted: actionName === "Done" ? new Date().toLocaleDateString() : "", hoursSpent: 0, - completedTodayTimings: [ - { - goalid: task.goalid, - start: task.start, - deadline: task.deadline, - }, - ], - blockedSlots: [], // actionName === "Reschedule" ? [{ start: task.start, end: task.deadline }] : [], + completedTodayTimings: + actionName === "Done" + ? [ + { + goalid: task.goalid, + start: task.start, + deadline: task.deadline, + }, + ] + : [], + blockedSlots: [], }); // if (actionName === "Reschedule") { // setOpenReschedule({ ...task }); @@ -133,7 +140,7 @@ export const MyTimeline: React.FC = ({ day, myTasks, taskDetail } else if (actionName === "Skip") { await forgetTask(taskItem.id, `${getHrFromDateString(task.start)}-${getHrFromDateString(task.deadline)}`, task); } else if (actionName === "Reschedule") { - // setOpenReschedule({ ...task }); + setOpenReschedule(task); } if (actionName === "Done") { await doneSound.play(); @@ -147,6 +154,8 @@ export const MyTimeline: React.FC = ({ day, myTasks, taskDetail } else if (actionName === "Skip") { await forgetSound.play(); setLastAction("TaskSkipped"); + } else if (actionName === "Reschedule") { + setOpenReschedule({ ...task }); } } else { setShowToast({ open: true, message: "Let's focus on Today :)", extra: "" }); diff --git a/src/components/MyTimeComponents/Reschedule/Reschedule.scss b/src/components/MyTimeComponents/Reschedule/Reschedule.scss index cb2f2c388..b992b0319 100644 --- a/src/components/MyTimeComponents/Reschedule/Reschedule.scss +++ b/src/components/MyTimeComponents/Reschedule/Reschedule.scss @@ -1,108 +1,15 @@ -.rescheduleModal { - hr { - margin: 0; - color: #d9d9d9; - opacity: 1; +.interactables-modal.reschedule-modal { + .header-title { + text-align: left; } + .reschedule-options { + display: grid; + grid-template-columns: 2fr 2fr; - .ant-modal-content { - padding: 0 0 24px 0 !important; - border: none !important; - border-radius: 28px !important; - - .selected { - border: none; - background: var(--bottom-nav-color); - box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); - } - .booked { - background: var(--bottom-nav-color); - } - } - - .list { - margin: 0 16px; - padding: 8px 8px 16px 8px; - border-left: 0; - border-right: 0; - flex-wrap: nowrap; - overflow-x: scroll; - display: flex; - gap: 20px; - - .res-card { - border-radius: 8px; - background: transparent; - border: 1px solid #d9d9d9; - padding: 10px 15px; - display: flex; - flex-direction: column; - gap: 10px; - text-align: center; - min-width: 75px; - align-items: center; - } - - p { - margin: 0; - font-size: 1.286em; - font-weight: 500; - white-space: nowrap; - } - - .res-card > p:last-child { - font-weight: 500; - } - - .res-card.selected > p:last-child { - font-weight: 700; - color: var(--bottom-nav-text-color); - } - } - - .slot-batch { - display: flex; - flex-direction: column; - gap: 10px; - - div { - display: flex; - gap: 10px; + .goal-action:last-child { + border-top: 2px solid var(--default-border-color); + grid-column: 1 / -1; + border-radius: 0 0 28px 28px; } } - - .slot-card { - border-radius: 8px; - background: transparent; - border: 1px solid #d9d9d9; - padding: 10px 0; - display: flex; - flex-direction: column; - gap: 10px; - text-align: center; - min-width: 50px; - align-items: center; - - sup { - top: -1em; - font-size: 0.5em; - } - } - - .slot-card.selected { - font-weight: 600; - } - - .action-btn { - margin-top: 16px; - background: var(--primary-background); - } - - div:has(.action-btn) { - text-align: center; - } - - .task-card { - width: 150px; - } } diff --git a/src/components/MyTimeComponents/Reschedule/Reschedule.tsx b/src/components/MyTimeComponents/Reschedule/Reschedule.tsx index c050efbfc..3204b622d 100644 --- a/src/components/MyTimeComponents/Reschedule/Reschedule.tsx +++ b/src/components/MyTimeComponents/Reschedule/Reschedule.tsx @@ -1,168 +1,55 @@ -import { Tooltip } from "antd"; -import { useRecoilState, useRecoilValue } from "recoil"; -import React, { useEffect, useState } from "react"; -import { v4 as uuidv4 } from "uuid"; - -import { darkModeState } from "@src/store"; -import { getMonthByIndex } from "@src/utils"; -import SubHeader from "@src/common/SubHeader"; -import { convertDateToDay } from "@src/utils/SchedulerUtils"; +import { useRecoilState, useSetRecoilState } from "recoil"; +import React from "react"; +import { lastAction } from "@src/store"; import "./Reschedule.scss"; -import { displayReschedule } from "@src/store/TaskState"; -import { ITask } from "@src/Interfaces/Task"; -import { getFromOutbox } from "@src/api/DumpboxAPI"; -import { IFinalOutputSlot } from "@src/Interfaces/IScheduler"; import ZModal from "@src/common/ZModal"; -import ScheduledSlots from "./ScheduledSlots"; +import { addBlockedSlot } from "@src/api/TasksAPI"; +import { displayReschedule } from "@src/store/TaskState"; +import { MILLISECONDS_IN_HOUR, RESCHEDULE_OPTIONS } from "@src/constants/rescheduleOptions"; +import { convertDateToString } from "@src/utils"; +import ActionDiv from "@components/GoalsComponents/MyGoalActions/ActionDiv"; const Reschedule = () => { - const [task, setOpen] = useRecoilState(displayReschedule); - if (!task) { - return null; - } - let today = new Date(); - if (today.getHours() + task.duration > 23) { - today = new Date(today.setDate(today.getDate() + 1)); - } - const currHr = today.getHours(); - const currDate = today.getDate(); - const currMonth = today.getMonth(); - const lastDate = new Date(2023, today.getMonth() + 1, 0).getDate(); + const [task, setDisplayReschedule] = useRecoilState(displayReschedule); + const setLastAction = useSetRecoilState(lastAction); - const [selectedHr, setSelectedHr] = useState(currHr === new Date().getHours() ? 8 : currHr); - const [monthIndex, setMonthIndex] = useState(currMonth); - const [selectedDay, setSelectedDay] = useState(currDate); - const [selectedDate, setSelctedDate] = useState(today); - const [endDate, setEndDate] = useState(today); - const [overridenTasks, setOverridenTasks] = useState([]); - const [schedulerOutput, setSchedulerOutput] = useState<{ [key: string]: IFinalOutputSlot[] }>({}); + if (!task) return null; - const darkModeStatus = useRecoilValue(darkModeState); + const handleReschedule = (hours: number) => { + const startTime = new Date(task.start); + const endTime = new Date(startTime.getTime() + hours * MILLISECONDS_IN_HOUR); + const start = convertDateToString(startTime, false); + const end = convertDateToString(endTime, false); - const getSlots = (start: number, end: number) => - [...Array(end).keys()].slice(start).map((hr) => ( - - )); - - useEffect(() => { - getFromOutbox("scheduler").then((res) => { - if (res) { - const { scheduled } = JSON.parse(JSON.parse(res.value).output); - setEndDate(new Date(scheduled.slice(-1)[0].day)); - const temp: { [key: string]: IFinalOutputSlot[] } = {}; - scheduled.forEach((sod: { day: string; tasks: IFinalOutputSlot[] }) => { - temp[new Date(sod.day).toLocaleDateString()] = sod.tasks; - }); - setSchedulerOutput({ ...temp }); - } + addBlockedSlot(task.goalid, { + start, + end, }); - }, []); - - // useEffect(() => { - // if (task) document.getElementsByClassName("slot-card")[currHr].scrollIntoView({ inline: "center" }); - // }, [task]); - useEffect(() => { - const tmp = new Date(today.getFullYear(), monthIndex, selectedDay); - if (tmp > endDate) { - setOverridenTasks([]); - } - if (tmp > today) { - console.log("sdd"); - setSelectedHr(8); - } - setSelctedDate(tmp); - }, [selectedDay, monthIndex]); + console.log(`Task rescheduled from ${start} to ${end}`); + setDisplayReschedule(null); + setLastAction("TaskRescheduled"); + }; - // console.log(schedulerOutput, selectedDate.toLocaleDateString(), schedulerOutput[selectedDate.toLocaleDateString()]); - return task ? ( - setOpen(null)}> + return ( + setDisplayReschedule(null)}>
-

{task.title}

+

{`Postpone: ${task.title}`}

- today.getMonth()} - showRightNav={monthIndex < 11} - title={getMonthByIndex(monthIndex)} - leftNav={() => setMonthIndex(monthIndex - 1)} - rightNav={() => setMonthIndex(monthIndex + 1)} - /> -
-
- {[...Array(lastDate + 1).keys()].slice(currDate).map((onDate) => ( - + +
))} -
-

- Pick {task.duration} hour{task.duration > 1 ? "s" : ""} -

-
- {schedulerOutput[selectedDate.toLocaleDateString()] ? ( - - ) : ( - getSlots(0, 25) - )} -
-
- {overridenTasks.map((psTask: ITask) => ( - - ))} -
-
- -
- ) : null; + ); }; export default Reschedule; diff --git a/src/constants/rescheduleOptions.ts b/src/constants/rescheduleOptions.ts new file mode 100644 index 000000000..600e96532 --- /dev/null +++ b/src/constants/rescheduleOptions.ts @@ -0,0 +1,9 @@ +export const RESCHEDULE_OPTIONS: { label: string; value: number }[] = [ + { label: "1 hour", value: 1 }, + { label: "3 hours", value: 3 }, + { label: "1 day", value: 24 }, + { label: "3 days", value: 72 }, + { label: "7 days", value: 168 }, +]; + +export const MILLISECONDS_IN_HOUR = 3600000; diff --git a/src/global.scss b/src/global.scss index 870fb2ea2..9808b1ae5 100644 --- a/src/global.scss +++ b/src/global.scss @@ -220,6 +220,7 @@ h6 { border-radius: 28px 28px 0 0 !important; background: var(--secondary-background); box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.25); + font-size: 18px; h4 { margin: 0; diff --git a/src/helpers/MyTimeHelper.ts b/src/helpers/MyTimeHelper.ts index 3c43d9b0a..13e80aeb3 100644 --- a/src/helpers/MyTimeHelper.ts +++ b/src/helpers/MyTimeHelper.ts @@ -39,9 +39,9 @@ export const transformIntoSchInputGoals = ( if (ele.on) { obj.filters.onDays = ele.on.map((day) => day.toLowerCase()); } - if (slotsNotallowed && slotsNotallowed.length > 0) { - obj.filters.notOn = [...slotsNotallowed]; - } + } + if (slotsNotallowed && slotsNotallowed.length > 0) { + obj.notOn = [...slotsNotallowed]; } if (ele.habit) obj.repeat = "weekly"; if (ele.timeBudget) { @@ -168,6 +168,7 @@ export const organizeDataForInptPrep = async (inputGoals: GoalItem[]) => { {}, ); const blockedSlots: { [goalid: string]: blockedSlotOfTask[] } = await getAllBlockedTasks(); + console.log("blockedSlots", blockedSlots); const inputGoalsArr: ISchedulerInputGoal[] = transformIntoSchInputGoals(dbTasks, activeGoals, blockedSlots); schedulerInput.goals = inputGoalsArr;