diff --git a/src/App.tsx b/src/App.tsx index 4df2ae37b..8e959e094 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -45,8 +45,9 @@ const App = () => { message: `${showToast.message}`, description: {() => `${showToast.extra}`}, placement: "top", - className: `toast-${darkModeEnabled ? "dark" : "light"} ${darkModeEnabled ? "dark" : "light"}-theme${theme[darkModeEnabled ? "dark" : "light"] - }`, + className: `toast-${darkModeEnabled ? "dark" : "light"} ${darkModeEnabled ? "dark" : "light"}-theme${ + theme[darkModeEnabled ? "dark" : "light"] + }`, }); }; useEffect(() => { diff --git a/src/components/BottomNavbar/BottomNavbar.tsx b/src/components/BottomNavbar/BottomNavbar.tsx index 57e156a3b..81aab55ca 100644 --- a/src/components/BottomNavbar/BottomNavbar.tsx +++ b/src/components/BottomNavbar/BottomNavbar.tsx @@ -118,7 +118,7 @@ const BottomNavbar = ({ title }: { title: string }) => { title={themeSelection ? "ArrowIcon" : "JournalIcon"} /> {themeSelection ?

Next

:

{t("Journal")}

} - {title !== "myTime" && title !== "Inbox" && } + {title !== "myTime" && title !== "Inbox" && title !== "Focus" && } diff --git a/src/components/MyTimeComponents/Focus.tsx/Focus.tsx b/src/components/MyTimeComponents/Focus.tsx/Focus.tsx new file mode 100644 index 000000000..1710601f9 --- /dev/null +++ b/src/components/MyTimeComponents/Focus.tsx/Focus.tsx @@ -0,0 +1,105 @@ +import React, { useState, useEffect } from "react"; +import { useRecoilValue } from "recoil"; +import { darkModeState, currentScheduledTask, focusTaskTitle } from "@src/store"; +import { Progress } from "antd"; + +import "./focus.scss"; +import { formatTimeDisplay } from "@src/utils"; + +export const Focus = () => { + const darkModeStatus = useRecoilValue(darkModeState); + + const [initialTime, setInitialTime] = useState(25 * 60); + const [time, setTime] = useState(initialTime); + const [isTimerActive, setIsTimerActive] = useState(false); + const [editMode, setEditMode] = useState(false); + const [userEnteredTime, setUserEnteredTime] = useState(""); + const taskTitle = useRecoilValue(focusTaskTitle); + const isActiveTitle = useRecoilValue(currentScheduledTask); + + useEffect(() => { + let interval; + if (isTimerActive && time > 0) { + interval = setInterval(() => { + setTime((prevTime) => prevTime - 1); + }, 1000); + } else if (!isTimerActive && time !== 0) { + clearInterval(interval!); + } + return () => clearInterval(interval!); + }, [isTimerActive, time]); + + const toggle = () => { + setIsTimerActive(!isTimerActive); + }; + const reset = () => { + const resetTime = initialTime; + setTime(resetTime); + setIsTimerActive(false); + }; + + const percentage = ((initialTime - time) / initialTime) * 100; + const { minutes, seconds } = formatTimeDisplay(time); + + const handleEditClick = () => { + setEditMode(!editMode); + }; + + const handleSaveClick = () => { + const newTimeInSeconds = parseInt(userEnteredTime, 10) * 60; + if (!Number.isNaN(newTimeInSeconds)) { + setInitialTime(newTimeInSeconds); + setTime(newTimeInSeconds); + setEditMode(false); + } else setEditMode(false); + }; + + return ( +
+
{isActiveTitle === "" ? taskTitle : isActiveTitle}
+ { + if (minutes >= 1) { + return `${minutes} min`; + } + return `${seconds} sec`; + }} + /> + {editMode ? ( +
+ setUserEnteredTime(e.target.value)} + /> + +
+ ) : ( +
+ + + {time === initialTime && !isTimerActive && ( + + )} +
+ )} +
+ ); +}; diff --git a/src/components/MyTimeComponents/Focus.tsx/focus.scss b/src/components/MyTimeComponents/Focus.tsx/focus.scss new file mode 100644 index 000000000..da6ccf4e8 --- /dev/null +++ b/src/components/MyTimeComponents/Focus.tsx/focus.scss @@ -0,0 +1,54 @@ +.focus { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + margin-top: 20px; + gap: 25px; +} + +.timer { + font-size: 2em; + color: #333; + width: 200px; + height: 200px; + border-radius: 50%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background-color: #fff; + position: relative; + + .timer-fill { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border-radius: 50%; + background-color: #333; + transform-origin: left center; + transform: scaleX(0); /* Initial width of 0 */ + transition: transform 1s linear; + } + + .timer-text { + z-index: 1; + } +} + +.start-button { + margin-top: 2em; + padding: 1em 2em; + font-size: 1em; + color: #fff; + background-color: #007bff; + border: none; + border-radius: 5px; + cursor: pointer; + + &:hover { + background-color: #0056b3; + } +} diff --git a/src/components/MyTimeComponents/MyTimeline.tsx b/src/components/MyTimeComponents/MyTimeline.tsx index 384e9e592..6d60d1411 100644 --- a/src/components/MyTimeComponents/MyTimeline.tsx +++ b/src/components/MyTimeComponents/MyTimeline.tsx @@ -4,7 +4,7 @@ import { v4 as uuidv4 } from "uuid"; import React, { useState } from "react"; import { useLocation, useNavigate } from "react-router-dom"; -import { useRecoilValue, useSetRecoilState } from "recoil"; +import { useRecoilState, useRecoilValue, useSetRecoilState } from "recoil"; import archiveTune from "@assets/archive.mp3"; import forgetTune from "@assets/forget.mp3"; @@ -16,7 +16,7 @@ import { TaskItem } from "@src/models/TaskItem"; import { GoalItem } from "@src/models/GoalItem"; import { displayReschedule } from "@src/store/TaskState"; import { getHrFromDateString } from "@src/utils/SchedulerUtils"; -import { darkModeState, displayToast, lastAction, openDevMode } from "@src/store"; +import { darkModeState, displayToast, lastAction, openDevMode, selectedMyTimeView, focusTaskTitle } from "@src/store"; import { addTask, completeTask, forgetTask, getTaskByGoalId } from "@src/api/TasksAPI"; import "./index.scss"; @@ -48,6 +48,8 @@ export const MyTimeline: React.FC = ({ day, myTasks, taskDetail const setShowToast = useSetRecoilState(displayToast); const setLastAction = useSetRecoilState(lastAction); const setOpenReschedule = useSetRecoilState(displayReschedule); + const setCurrentView = useSetRecoilState(selectedMyTimeView); + const setTaskTitle = useSetRecoilState(focusTaskTitle); const [showScheduled, setShowScheduled] = useState(true); @@ -57,7 +59,7 @@ export const MyTimeline: React.FC = ({ day, myTasks, taskDetail setShowScheduled(!showScheduled); }; // console.log(devMode); - const handleActionClick = async (actionName: "Skip" | "Reschedule" | "Done", task: ITask) => { + const handleActionClick = async (actionName: "Skip" | "Reschedule" | "Done" | "Focus", task: ITask) => { if (day === "Today") { const taskItem = await getTaskByGoalId(task.goalid); if (!taskItem) { @@ -97,6 +99,9 @@ export const MyTimeline: React.FC = ({ day, myTasks, taskDetail } else if (actionName === "Skip") { await forgetSound.play(); setLastAction("TaskSkipped"); + } else if (actionName === "Focus") { + setTaskTitle(task.title); + setCurrentView("focus"); } } else { setShowToast({ open: true, message: "Let's focus on Today :)", extra: "" }); @@ -133,6 +138,21 @@ export const MyTimeline: React.FC = ({ day, myTasks, taskDetail }, }); }; + const currentHour = new Date().getHours(); + const currentActiveTask = myTasks.scheduled.filter((task) => { + if (task.start) { + const startHour = parseInt(task.start.split("T")[1].slice(0, 2), 10); + if (startHour < currentHour) { + return false; + } + } + return true; + }); + if (currentActiveTask[0] === undefined) { + setTaskTitle("No Scheduled Tasks"); + } else { + setTaskTitle(currentActiveTask[0]?.title); + } return ( <> {myTasks.impossible.length > 0 && ( @@ -229,6 +249,10 @@ export const MyTimeline: React.FC = ({ day, myTasks, taskDetail Done
+ +
) : null} diff --git a/src/override.scss b/src/override.scss index 3cfe3683b..ace550deb 100644 --- a/src/override.scss +++ b/src/override.scss @@ -81,3 +81,11 @@ color: white !important; } } + +/*ProgressBar*/ +.progress-dark { + .ant-progress-text { + color: white!important; + } +} + diff --git a/src/pages/MyTimePage/MyTimePage.tsx b/src/pages/MyTimePage/MyTimePage.tsx index e29b2682b..2b3b26491 100644 --- a/src/pages/MyTimePage/MyTimePage.tsx +++ b/src/pages/MyTimePage/MyTimePage.tsx @@ -3,6 +3,8 @@ import React, { useState } from "react"; import { useRecoilState } from "recoil"; import { MyTimeline } from "@components/MyTimeComponents/MyTimeline"; +import { Focus } from "@components/MyTimeComponents/Focus.tsx/Focus"; + import { getOrdinalSuffix } from "@src/utils"; import SubHeader from "@src/common/SubHeader"; import AppLayout from "@src/layouts/AppLayout"; @@ -80,17 +82,17 @@ export const MyTimePage = () => { return ( { - setCurrentView("today"); + setCurrentView(currentView === "thisWeek" ? "today" : "focus"); }} rightNav={() => { - setCurrentView("thisWeek"); + setCurrentView(currentView === "today" ? "thisWeek" : "today"); }} /> - {getDayComponent("Today")} + {currentView === "today" && getDayComponent("Today")} {currentView === "thisWeek" && getDayComponent("Tomorrow")} {currentView === "thisWeek" && [...Array(6).keys()].map((i) => { @@ -101,6 +103,7 @@ export const MyTimePage = () => { } return null; })} + {currentView === "focus" && } ); diff --git a/src/store/index.ts b/src/store/index.ts index d1c29fbdd..313034eaa 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -90,4 +90,14 @@ export const selectedMyTimeView = atom({ default: "today", }); +export const currentScheduledTask = atom({ + key: "currentScheduledTask", + default: "", +}); + +export const focusTaskTitle = atom({ + key: "focusTaskTitle", + default: "No Task Scheduled", +}); + export { darkModeState, languageSelectionState }; diff --git a/src/utils/index.ts b/src/utils/index.ts index bdab5dacd..4ad9be4cf 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -218,3 +218,10 @@ export const getMonthByIndex = (index: number) => { ]; return month[index]; }; + +export const formatTimeDisplay = (timeInSeconds: number) => { + const minutes = Math.floor(timeInSeconds / 60); + const seconds = timeInSeconds % 60; + + return { minutes, seconds }; +};