diff --git a/frontend/package.json b/frontend/package.json
index 618e30c..5c9d96e 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -64,6 +64,8 @@
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.4",
"postcss": "^8.4.31",
+ "prettier": "^3.1.0",
+ "prettier-plugin-tailwindcss": "^0.5.7",
"tailwindcss": "^3.3.5",
"vite": "^4.5.0"
}
diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml
index b514b56..dc65ed8 100644
--- a/frontend/pnpm-lock.yaml
+++ b/frontend/pnpm-lock.yaml
@@ -163,6 +163,12 @@ devDependencies:
postcss:
specifier: ^8.4.31
version: 8.4.31
+ prettier:
+ specifier: ^3.1.0
+ version: 3.1.0
+ prettier-plugin-tailwindcss:
+ specifier: ^0.5.7
+ version: 0.5.7(prettier@3.1.0)
tailwindcss:
specifier: ^3.3.5
version: 3.3.5
@@ -3339,6 +3345,67 @@ packages:
engines: {node: '>= 0.8.0'}
dev: true
+ /prettier-plugin-tailwindcss@0.5.7(prettier@3.1.0):
+ resolution: {integrity: sha512-4v6uESAgwCni6YF6DwJlRaDjg9Z+al5zM4JfngcazMy4WEf/XkPS5TEQjbD+DZ5iNuG6RrKQLa/HuX2SYzC3kQ==}
+ engines: {node: '>=14.21.3'}
+ peerDependencies:
+ '@ianvs/prettier-plugin-sort-imports': '*'
+ '@prettier/plugin-pug': '*'
+ '@shopify/prettier-plugin-liquid': '*'
+ '@shufo/prettier-plugin-blade': '*'
+ '@trivago/prettier-plugin-sort-imports': '*'
+ prettier: ^3.0
+ prettier-plugin-astro: '*'
+ prettier-plugin-css-order: '*'
+ prettier-plugin-import-sort: '*'
+ prettier-plugin-jsdoc: '*'
+ prettier-plugin-marko: '*'
+ prettier-plugin-organize-attributes: '*'
+ prettier-plugin-organize-imports: '*'
+ prettier-plugin-style-order: '*'
+ prettier-plugin-svelte: '*'
+ prettier-plugin-twig-melody: '*'
+ peerDependenciesMeta:
+ '@ianvs/prettier-plugin-sort-imports':
+ optional: true
+ '@prettier/plugin-pug':
+ optional: true
+ '@shopify/prettier-plugin-liquid':
+ optional: true
+ '@shufo/prettier-plugin-blade':
+ optional: true
+ '@trivago/prettier-plugin-sort-imports':
+ optional: true
+ prettier-plugin-astro:
+ optional: true
+ prettier-plugin-css-order:
+ optional: true
+ prettier-plugin-import-sort:
+ optional: true
+ prettier-plugin-jsdoc:
+ optional: true
+ prettier-plugin-marko:
+ optional: true
+ prettier-plugin-organize-attributes:
+ optional: true
+ prettier-plugin-organize-imports:
+ optional: true
+ prettier-plugin-style-order:
+ optional: true
+ prettier-plugin-svelte:
+ optional: true
+ prettier-plugin-twig-melody:
+ optional: true
+ dependencies:
+ prettier: 3.1.0
+ dev: true
+
+ /prettier@3.1.0:
+ resolution: {integrity: sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==}
+ engines: {node: '>=14'}
+ hasBin: true
+ dev: true
+
/prop-types-extra@1.1.1(react@18.2.0):
resolution: {integrity: sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==}
peerDependencies:
diff --git a/frontend/src/components/calendar/TaskDataHandler.jsx b/frontend/src/components/calendar/TaskDataHandler.jsx
index 793ca4b..9e06f62 100644
--- a/frontend/src/components/calendar/TaskDataHandler.jsx
+++ b/frontend/src/components/calendar/TaskDataHandler.jsx
@@ -1,4 +1,5 @@
import { readTodoTasks } from "src/api/TaskApi";
+import { axiosInstance } from "src/api/AxiosConfig";
let eventGuid = 0;
diff --git a/frontend/src/components/calendar/calendar.jsx b/frontend/src/components/calendar/calendar.jsx
index c03637a..17327c0 100644
--- a/frontend/src/components/calendar/calendar.jsx
+++ b/frontend/src/components/calendar/calendar.jsx
@@ -13,6 +13,14 @@ export class Calendar extends React.Component {
currentEvents: [],
};
+ async handleGoogleClick() {
+ try {
+ const response = await axiosInstance.get("calendar-events/");
+ } catch (error) {
+ console.error("Error fetching data from Google:", error);
+ }
+ }
+
render() {
return (
@@ -47,10 +55,7 @@ export class Calendar extends React.Component {
{/* Description Zone */}
-
Instructions
- - Select dates and you will be prompted to create a new event
- - Drag, drop, and resize events
- Click an event to delete it
@@ -66,7 +71,7 @@ export class Calendar extends React.Component {
/>
Toggle weekends
-
diff --git a/frontend/src/components/kanbanBoard/kanbanPage.jsx b/frontend/src/components/kanbanBoard/kanbanPage.jsx
index 92a8536..ceb57a0 100644
--- a/frontend/src/components/kanbanBoard/kanbanPage.jsx
+++ b/frontend/src/components/kanbanBoard/kanbanPage.jsx
@@ -1,5 +1,6 @@
import { KanbanBoard } from "./kanbanBoard";
import { useState } from "react";
+import { TableBoard } from "./tableBoard";
export const KanbanPage = () => {
const [activeTab, setActiveTab] = useState("kanban");
@@ -16,19 +17,21 @@ export const KanbanPage = () => {
handleTabClick("kanban")}>
+ onClick={() => handleTabClick("kanban")}
+ >
Kanban
- {/*
handleTabClick("table")}>
+ onClick={() => handleTabClick("table")}
+ >
Table
- */}
+
-
+ {activeTab === "kanban" ? : }
);
};
diff --git a/frontend/src/components/kanbanBoard/tableBoard.jsx b/frontend/src/components/kanbanBoard/tableBoard.jsx
new file mode 100644
index 0000000..cb6a62f
--- /dev/null
+++ b/frontend/src/components/kanbanBoard/tableBoard.jsx
@@ -0,0 +1,102 @@
+import { useState, useEffect } from "react";
+import { axiosInstance } from "src/api/AxiosConfig";
+
+export function TableBoard() {
+ const [tasks, setTasks] = useState([]);
+
+ // ---------------- Fetch Data ----------------
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ const tasksResponse = await axiosInstance.get("/todo");
+
+ // Transform
+ const transformedTasks = tasksResponse.data.map((task) => ({
+ id: task.id,
+ columnId: task.list_board,
+ content: task.title,
+ difficulty: task.difficulty,
+ notes: task.notes,
+ importance: task.importance,
+ challenge: task.challenge,
+ fromSystem: task.fromSystem,
+ creation_date: task.creation_date,
+ last_update: task.last_update,
+ is_active: task.is_active,
+ is_full_day_event: task.is_full_day_event,
+ start_event: task.start_event,
+ end_event: task.end_event,
+ google_calendar_id: task.google_calendar_id,
+ completed: task.completed,
+ completion_date: task.completion_date,
+ priority: task.priority,
+ user: task.user,
+ list_board: task.list_board,
+ tags: task.tags,
+ subtaskCount: task.sub_task_count,
+ }));
+ setTasks(transformedTasks);
+ } catch (error) {
+ console.error("Error fetching data from API:", error);
+ }
+ };
+ fetchData();
+ }, []);
+
+ // ---------------- END Fetch Data ----------------
+
+ return (
+
+
+ {/* head */}
+
+
+ |
+ Title |
+ Description |
+ Priority |
+ Due Date |
+
+
+
+ {/* BODY */}
+ {tasks.map((task, index) => (
+
+ |
+
+ |
+
+
+
+ {task.content}
+ {task.content}
+
+
+ |
+
+ {task.notes}
+
+ Description
+ |
+ {task.priority} |
+
+ {task.end_event}
+ |
+
+ ))}
+ {/* END BODY */}
+
+ {/* foot */}
+
+
+ |
+ Title |
+ Description |
+ Priority |
+ Due Date |
+
+
+
+
+ );
+}
diff --git a/frontend/src/components/kanbanBoard/taskCard.jsx b/frontend/src/components/kanbanBoard/taskCard.jsx
index 4c40a9e..ae4407f 100644
--- a/frontend/src/components/kanbanBoard/taskCard.jsx
+++ b/frontend/src/components/kanbanBoard/taskCard.jsx
@@ -50,12 +50,12 @@ export function TaskCard({ task, deleteTask, updateTask }) {
task.difficulty === 1
? "bg-blue-200 text-blue-700"
: task.difficulty === 2
- ? "bg-green-200 text-green-700"
- : task.difficulty === 3
- ? "bg-yellow-200 text-yellow-700"
- : task.difficulty === 4
- ? "bg-red-200 text-red-700"
- : "bg-purple-200 text-purple-700"
+ ? "bg-green-200 text-green-700"
+ : task.difficulty === 3
+ ? "bg-yellow-200 text-yellow-700"
+ : task.difficulty === 4
+ ? "bg-red-200 text-red-700"
+ : "bg-purple-200 text-purple-700"
}`}>
difficulty
@@ -71,12 +71,12 @@ export function TaskCard({ task, deleteTask, updateTask }) {
daysUntilDue >= 365
? "gray-200"
: daysUntilDue >= 30
- ? "blue-200"
- : daysUntilDue >= 7
- ? "green-200"
- : daysUntilDue > 0
- ? "yellow-200"
- : "red-200";
+ ? "blue-200"
+ : daysUntilDue >= 7
+ ? "green-200"
+ : daysUntilDue > 0
+ ? "yellow-200"
+ : "red-200";
const formattedDueDate =
daysUntilDue >= 365
@@ -85,7 +85,10 @@ export function TaskCard({ task, deleteTask, updateTask }) {
month: "short",
year: "numeric",
})
- : new Date(task.end_event).toLocaleDateString("en-US", { day: "numeric", month: "short" });
+ : new Date(task.end_event).toLocaleDateString("en-US", {
+ day: "numeric",
+ month: "short",
+ });
return (
@@ -122,13 +125,15 @@ export function TaskCard({ task, deleteTask, updateTask }) {
{/* -------- Task Card -------- */}
diff --git a/frontend/src/components/kanbanBoard/taskDetailModal.jsx b/frontend/src/components/kanbanBoard/taskDetailModal.jsx
index 3941ba1..5beb5ed 100644
--- a/frontend/src/components/kanbanBoard/taskDetailModal.jsx
+++ b/frontend/src/components/kanbanBoard/taskDetailModal.jsx
@@ -16,24 +16,48 @@ export function TaskDetailModal({
challenge,
importance,
taskId,
+ start_event,
+ end_event,
updateTask,
completed,
}) {
+ if (importance >= 3) {
+ importance = true;
+ } else {
+ importance = false;
+ }
+
+ const s = formatAMPM(new Date(start_event));
+ const e = formatAMPM(new Date(end_event));
const [isChallengeChecked, setChallengeChecked] = useState(challenge);
const [isImportantChecked, setImportantChecked] = useState(importance);
const [currentDifficulty, setCurrentDifficulty] = useState((difficulty - 1) * 25);
const [selectedTags, setSelectedTags] = useState([]);
- const [dateStart, setDateStart] = useState(new Date());
- const [dateEnd, setDateEnd] = useState(new Date());
+ const [dateStart, setDateStart] = useState(new Date(start_event));
+ const [dateEnd, setDateEnd] = useState(new Date(end_event));
const [startDateEnabled, setStartDateEnabled] = useState(false);
const [endDateEnabled, setEndDateEnabled] = useState(false);
const [isTaskComplete, setTaskComplete] = useState(completed);
- const [starteventValue, setStartEventValue] = useState("10:00 PM");
- const [endeventValue, setEndEventValue] = useState("11:00 AM");
+ const [starteventValue, setStartEventValue] = useState(e);
+ const [endeventValue, setEndEventValue] = useState(s);
const [subtaskText, setSubtaskText] = useState("");
const [subtasks, setSubtasks] = useState([]);
const [currentTitle, setTitle] = useState(title);
const [isTitleEditing, setTitleEditing] = useState(false);
+ const [isDescriptionEditing, setDescriptionEditing] = useState(false);
+ const [updatedDescription, setUpdatedDescription] = useState(description);
+
+ const handleDescriptionEditToggle = () => {
+ setDescriptionEditing(!isDescriptionEditing);
+ };
+
+ const handleDescriptionChange = async () => {
+ const data = {
+ notes: updatedDescription,
+ };
+ await updateTodoTaskPartial(taskId, data);
+ setDescriptionEditing(false);
+ };
const handleTitleChange = async () => {
const data = {
@@ -43,43 +67,113 @@ export function TaskDetailModal({
setTitleEditing(false);
};
- const handleStartEventTimeChange = async (timeValue) => {
- const formattedTime = convertToFormattedTime(timeValue);
- setStartEventValue(formattedTime);
- console.log(formattedTime);
- const data = {
- startTime: formattedTime,
- };
- await updateTodoTaskPartial(taskId, data);
- };
+ {
+ /* -------- Time -------- */
+ }
- const handleEndEventTimeChange = async (timeValue) => {
- const inputTime = event.target.value;
- // Validate the input time format
- if (!validateTimeFormat(inputTime)) {
- // Display an error message or handle invalid format
- console.error("Invalid time format. Please use HH:mm AM/PM");
+ function formatAMPM(date) {
+ var hours = date.getHours();
+ var minutes = date.getMinutes();
+ var ampm = hours >= 12 ? "PM" : "AM";
+ hours = hours % 12;
+ hours = hours ? hours : 12; // the hour '0' should be '12'
+ minutes = minutes < 10 ? "0" + minutes : minutes;
+ var strTime = hours + ":" + minutes + " " + ampm;
+ return strTime;
+ }
+
+ const handleStartEventTimeChange = async (startdate, starttime) => {
+ if (!validateTimeFormat(starttime)) {
+ console.error("Wrong time format");
return;
}
- const formattedTime = convertToFormattedTime(timeValue);
- setEndEventValue(formattedTime);
+ // Format incoming date
+ setDateStart(startdate);
+ setStartEventValue(starttime);
+ const formatDate = convertToFormattedDate(startdate);
+ // Format incoming time
+ const formatTime = convertToFormattedTime(starttime).slice(0, -1) + ":00.000000Z";
+ console.log(formatTime);
+ // Combine both of them
+ const formatDateTime = formatDate + formatTime;
+
const data = {
- endTime: formattedTime,
+ startTime: formatDateTime,
+ };
+ console.log(formatDateTime);
+ await updateTodoTaskPartial(taskId, data);
+ };
+
+ const handleEndEventTimeChange = async (enddate, endtime) => {
+ if (!validateTimeFormat(endtime)) {
+ console.error("Wrong time format");
+ return;
+ }
+
+ // Format incoming date
+ setDateEnd(enddate);
+ setEndEventValue(endtime);
+ const formatDate = convertToFormattedDate(enddate);
+ // Format incoming time
+ const formatTime = convertToFormattedTime(endtime);
+
+ // Combine both of them
+ const formatDateTime = formatDate + formatTime;
+
+ const data = {
+ startTime: formatDateTime,
};
await updateTodoTaskPartial(taskId, data);
};
+ const convertToFormattedDate = (dateValue) => {
+ const formattedDate = format(dateValue, "yyyy-MM-dd'T'", {
+ timeZone: "UTC",
+ });
+ return formattedDate;
+ };
+
const convertToFormattedTime = (timeValue) => {
- const formattedTime = format(timeValue, "HH:mm:ss.SSSX", { timeZone: "UTC" });
- return formattedTime;
+ // 2023-11-28 T [04:37:48.000000Z]
+ // 12:00 AM -> 12:00:00.000000Z
+ // 10:00 PM -> 22:00:00.000000Z
+
+ var hours = parseInt(timeValue.substr(0, 2));
+ if (timeValue.indexOf("AM") != -1 && hours == 12) {
+ timeValue = timeValue.replace("12", "0");
+ }
+ if (timeValue.indexOf("PM") != -1 && hours < 12) {
+ timeValue = timeValue.replace(hours, hours + 12);
+ }
+ const formattedTime = timeValue.replace(/(AM|PM)/, "");
+ const [formattedHours, restOfTime] = formattedTime.split(":");
+ const paddedHours = formattedHours.length === 1 ? `0${formattedHours}` : formattedHours;
+ console.log(`${paddedHours}:${restOfTime}`);
+ return `${paddedHours}:${restOfTime}`;
+ };
+
+ const handleStartDateChange = () => {
+ if (!isTaskComplete) {
+ setStartDateEnabled(!startDateEnabled);
+ }
+ };
+
+ const handleEndDateChange = () => {
+ if (!isTaskComplete) {
+ setEndDateEnabled(!endDateEnabled);
+ }
};
const validateTimeFormat = (time) => {
- const timeFormatRegex = /^(0[1-9]|1[0-2]):[0-5][0-9] (AM|PM)$/i;
+ const timeFormatRegex = /^([1-9]|1[0-2]):[0-5][0-9]( |)(AM|PM)$/i;
return timeFormatRegex.test(time);
};
+ {
+ /* -------- END Time -------- */
+ }
+
const handleChallengeChange = async () => {
setChallengeChecked(!isChallengeChecked);
const data = {
@@ -89,9 +183,14 @@ export function TaskDetailModal({
};
const handleImportantChange = async () => {
+ let important_num = 0;
+ if (isImportantChecked) {
+ important_num = 5;
+ }
+
setImportantChecked(!isImportantChecked);
const data = {
- important: !isImportantChecked,
+ important: important_num,
};
await updateTodoTaskPartial(taskId, data);
};
@@ -111,45 +210,6 @@ export function TaskDetailModal({
``;
};
- const handleStartDateValueChange = (date) => {
- if (!isTaskComplete) {
- setDateStart(date);
- const formattedStartDate = convertToFormattedDate(date);
- const data = {
- startTime: formattedStartDate,
- };
- updateTodoTaskPartial(taskId, data);
- }
- };
-
- const handleEndDateValueChange = (date) => {
- if (!isTaskComplete) {
- setDateEnd(date);
- const formattedEndDate = convertToFormattedDate(date);
- const data = {
- endTime: formattedEndDate,
- };
- updateTodoTaskPartial(taskId, data);
- }
- };
-
- const convertToFormattedDate = (dateValue) => {
- const formattedDate = format(dateValue, "yyyy-MM-dd'T'", { timeZone: "UTC" });
- return formattedDate;
- };
-
- const handleStartDateChange = () => {
- if (!isTaskComplete) {
- setStartDateEnabled(!startDateEnabled);
- }
- };
-
- const handleEndDateChange = () => {
- if (!isTaskComplete) {
- setEndDateEnabled(!endDateEnabled);
- }
- };
-
const handleTaskCompleteChange = async () => {
let completed = false;
if (isTaskComplete) {
@@ -183,7 +243,9 @@ export function TaskDetailModal({
try {
const updatedSubtasks = [...subtasks];
updatedSubtasks[index].completed = !updatedSubtasks[index].completed;
- await updateSubtask(updatedSubtasks[index].id, { completed: updatedSubtasks[index].completed });
+ await updateSubtask(updatedSubtasks[index].id, {
+ completed: updatedSubtasks[index].completed,
+ });
setSubtasks(updatedSubtasks);
} catch (error) {
console.error("Error updating subtask:", error);
@@ -321,35 +383,37 @@ export function TaskDetailModal({
onChange={handleStartDateChange}
/>
-
+ setDateStart(dateStart)}
+ disabled={!startDateEnabled}
+ />
{/* Start event time picker */}
-
+
{/* handleStartEventTimeChange */}
setStartEventValue(starteventValue)}
className="input input-bordered w-full max-w-xs"
- onClick={handleStartEventTimeChange}
+ disabled={!startDateEnabled}
/>
-
-
- {/* Complete? */}
-
-
-
-
Complete
-
-
- Update Start Time
-
-
+
+ handleStartEventTimeChange(dateStart, starteventValue)}>
+ Update Time
+
+
{/* End */}
End At
@@ -361,25 +425,62 @@ export function TaskDetailModal({
onChange={handleEndDateChange}
/>
-
+ setDateEnd(dateEnd)} disabled={!endDateEnabled} />
{/* End event time picker */}
-
this is time picker
+
+ {/* handleEndEventTimeChange */}
+
+
+ handleEndEventTimeChange(dateEnd, endeventValue)}>
+ Update Time
+
+
+
{/* Description */}
-
-
-
-
- Description
-
-
-
-
+ {isDescriptionEditing ? (
+
+
+
+
+ Description
+
+
+
+ ) : (
+
+
+
+
+ Description
+
+
+
+
+
+ )}
+
{/* Difficulty, Challenge, and Importance */}
@@ -430,6 +531,20 @@ export function TaskDetailModal({
+ {/* Complete? */}
+
{/* Subtask */}