diff --git a/client/src/components/ui/focus/currentTaskTitle.tsx b/client/src/components/ui/focus/currentTaskTitle.tsx new file mode 100644 index 0000000..50954fc --- /dev/null +++ b/client/src/components/ui/focus/currentTaskTitle.tsx @@ -0,0 +1,12 @@ +import React from "react"; + +export default function CurrentTaskTitle(task_name: string) { + return ( +
+

Current Focus

+
+ {task_name} +
+
+ ); +} diff --git a/client/src/components/ui/focus/taskDisplay.tsx b/client/src/components/ui/focus/taskDisplay.tsx new file mode 100644 index 0000000..9fc30ee --- /dev/null +++ b/client/src/components/ui/focus/taskDisplay.tsx @@ -0,0 +1,34 @@ +interface Task { + id: number; + name: string; + description: string; +} + +interface Time { + id: number; + day: number; + start_time: string; + end_time: string; + repeating: boolean; + task: number; +} + +export default function TaskDisplay({ + task, + time, +}: { + task: Task; + time: Time; +}) { + return ( +
+
+
{task.name}:
+
+ {time.start_time} - {time.end_time} +
+
+
{task.description}
+
+ ); +} diff --git a/client/src/components/ui/focus/timeDisplay.tsx b/client/src/components/ui/focus/timeDisplay.tsx new file mode 100644 index 0000000..965ffd9 --- /dev/null +++ b/client/src/components/ui/focus/timeDisplay.tsx @@ -0,0 +1,61 @@ +import React, { useEffect, useState } from "react"; + +import Timer from "./timer"; + +interface TimeDisplayProps { + statusSignal: (data: string) => void; + time: string; + current: boolean; +} + +export default function TimeDisplay({ + statusSignal, + time, + current, +}: TimeDisplayProps) { + const [timeRemaining, setTimeRemaining] = useState(0); + + useEffect(() => { + const getTimeRemaining = setInterval(() => { + const [Hours, Mins, Sec] = time.split(":").map(Number); + + const d = new Date(); + const time_serial = Hours * 60 * 60 + Mins * 60 + Sec; + const cur_time = + d.getHours() * 60 * 60 + d.getMinutes() * 60 + d.getSeconds(); + + let remaining_time = 0; + + remaining_time = time_serial - cur_time; + if (remaining_time <= 1) { + clearInterval(getTimeRemaining); + } + + setTimeRemaining(remaining_time); + }, 1000); + + return () => clearInterval(getTimeRemaining); + }, [time, timeRemaining]); + + useEffect(() => { + if (timeRemaining <= 1) { + statusSignal("finished"); + } + }, [timeRemaining, statusSignal]); + + return ( +
+ {current ? ( + <> +

Timer

+ + ) : ( + <> +

Next Task

+ + )} +
{Timer(timeRemaining)}
+
+
+ ); +} diff --git a/client/src/components/ui/focus/timer.tsx b/client/src/components/ui/focus/timer.tsx new file mode 100644 index 0000000..353ac7a --- /dev/null +++ b/client/src/components/ui/focus/timer.tsx @@ -0,0 +1,33 @@ +const Timer = (timeRemaining: number) => { + const formatTime = (time: number) => { + const seconds = Math.floor(time % 60); + const minutes = Math.floor((time / 60) % 60); + const hours = Math.floor((time / (60 * 60)) % 24); + + return ( +
+
+ {hours > 0 ? ( + <> + {hours.toString()} + : + + ) : ( + <> + )} + {minutes.toString().padStart(2, "0")} + : + {seconds.toString().padStart(2, "0")} +
+
+ ); + }; + + return ( +
+ {formatTime(timeRemaining)} +
+ ); +}; + +export default Timer; diff --git a/client/src/components/ui/focus/upcomingTasks.tsx b/client/src/components/ui/focus/upcomingTasks.tsx new file mode 100644 index 0000000..95d3236 --- /dev/null +++ b/client/src/components/ui/focus/upcomingTasks.tsx @@ -0,0 +1,71 @@ +import { useEffect, useState } from "react"; + +import TaskDisplay from "./taskDisplay"; + +interface Task { + id: number; + name: string; + description: string; +} + +interface Time { + id: number; + day: number; + start_time: string; + end_time: string; + repeating: boolean; + task: number; +} + +interface Tasks { + tasks: Task[]; + times: Time[]; +} + +function serializeTime(time: string) { + const [Hours, Mins, Sec] = time.split(":").map(Number); + return Hours * 60 * 60 + Mins * 60 + Sec; +} + +export default function UpcomingTasks({ tasks, times }: Tasks) { + const [upcomingTimes, setUpcomingTimes] = useState([]); + + useEffect(() => { + function sortTimes() { + const d = new Date(); + const cur_time = + d.getHours() * 60 * 60 + d.getMinutes() * 60 + d.getSeconds(); + const cur_d = d.getDay(); + let ut = times.filter( + (time) => + !(cur_d == time.day && serializeTime(time.end_time) <= cur_time), + ); + + ut = ut.sort((a, b) => { + return ( + serializeTime(a.start_time) + + ((a.day + 8 - cur_d) % 8) * 24 * 60 * 60 - + (serializeTime(b.start_time) + + ((b.day + 8 - cur_d) % 8) * 24 * 60 * 60) + ); + }); + setUpcomingTimes(ut); + } + + sortTimes(); + }, [times, tasks]); + + return ( +
+
+ {upcomingTimes.map((time: Time) => ( + task.id === time.task)[0]} + time={time} + key={time.id} + /> + ))} +
+
+ ); +} diff --git a/client/src/components/ui/refresh.tsx b/client/src/components/ui/refresh.tsx new file mode 100644 index 0000000..871586d --- /dev/null +++ b/client/src/components/ui/refresh.tsx @@ -0,0 +1,46 @@ +interface requi { + loading: boolean; + update: () => void; +} + +export default function Refresh({ loading, update }: requi) { + return ( +
+ {loading ? ( +
+ +
+ ) : ( + + )} +
+ ); +} diff --git a/client/src/pages/focus.tsx b/client/src/pages/focus.tsx new file mode 100644 index 0000000..fd4fc5f --- /dev/null +++ b/client/src/pages/focus.tsx @@ -0,0 +1,177 @@ +import React, { useEffect, useState } from "react"; + +import CurrentTaskTitle from "@/components/ui/focus/currentTaskTitle"; +import TimeDisplay from "@/components/ui/focus/timeDisplay"; +import UpcomingTasks from "@/components/ui/focus/upcomingTasks"; +import Refresh from "@/components/ui/refresh"; + +interface Time { + id: number; + day: number; + start_time: string; + end_time: string; + repeating: boolean; + task: number; +} + +interface Task { + id: number; + name: string; + description: string; +} + +function serializeTime(time: string) { + const [Hours, Mins, Sec] = time.split(":").map(Number); + return Hours * 60 * 60 + Mins * 60 + Sec; +} + +const CountdownTimer = () => { + const [times, setTimes] = useState([]); + const [tasks, setTasks] = useState([]); + const [currentTime, setCurrentTime] = useState