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 */} + + + + + + + + + + + {/* BODY */} + {tasks.map((task, index) => ( + + + + + + + + ))} + {/* END BODY */} + + {/* foot */} + + + + + + + + + +
TitleDescriptionPriorityDue Date
+ + +
+
+
{task.content}
+
{task.content}
+
+
+
+ {task.notes} +
+ Description +
{task.priority} + +
TitleDescriptionPriorityDue 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

- - -
+
+
+ {/* 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 */} + +
+ +
+
{/* Description */} -
-

- - - Description - -

- -
+ {isDescriptionEditing ? ( +
+

+ + + Description + +

+ + +
+ )} + {/* Difficulty, Challenge, and Importance */}
@@ -430,6 +531,20 @@ export function TaskDetailModal({
+ {/* Complete? */} +
+
+ +
+
{/* Subtask */}