Merge pull request #90 from TurTaskProject/main

Merge Kanban board
This commit is contained in:
Pattadon L 2023-11-30 09:21:58 +07:00 committed by GitHub
commit fe75c777ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 418 additions and 118 deletions

View File

@ -64,6 +64,8 @@
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.4", "eslint-plugin-react-refresh": "^0.4.4",
"postcss": "^8.4.31", "postcss": "^8.4.31",
"prettier": "^3.1.0",
"prettier-plugin-tailwindcss": "^0.5.7",
"tailwindcss": "^3.3.5", "tailwindcss": "^3.3.5",
"vite": "^4.5.0" "vite": "^4.5.0"
} }

View File

@ -163,6 +163,12 @@ devDependencies:
postcss: postcss:
specifier: ^8.4.31 specifier: ^8.4.31
version: 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: tailwindcss:
specifier: ^3.3.5 specifier: ^3.3.5
version: 3.3.5 version: 3.3.5
@ -3339,6 +3345,67 @@ packages:
engines: {node: '>= 0.8.0'} engines: {node: '>= 0.8.0'}
dev: true 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): /prop-types-extra@1.1.1(react@18.2.0):
resolution: {integrity: sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==} resolution: {integrity: sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==}
peerDependencies: peerDependencies:

View File

@ -1,4 +1,5 @@
import { readTodoTasks } from "src/api/TaskApi"; import { readTodoTasks } from "src/api/TaskApi";
import { axiosInstance } from "src/api/AxiosConfig";
let eventGuid = 0; let eventGuid = 0;

View File

@ -13,6 +13,14 @@ export class Calendar extends React.Component {
currentEvents: [], currentEvents: [],
}; };
async handleGoogleClick() {
try {
const response = await axiosInstance.get("calendar-events/");
} catch (error) {
console.error("Error fetching data from Google:", error);
}
}
render() { render() {
return ( return (
<div className="flex font-sans w-full h-screen"> <div className="flex font-sans w-full h-screen">
@ -47,10 +55,7 @@ export class Calendar extends React.Component {
<div className="w-72 bg-blue-100 border-r border-blue-200 p-8 flex flex-col"> <div className="w-72 bg-blue-100 border-r border-blue-200 p-8 flex flex-col">
{/* Description Zone */} {/* Description Zone */}
<div className="mb-8"> <div className="mb-8">
<h2 className="text-xl font-bold">Instructions</h2>
<ul className="list-disc pl-4"> <ul className="list-disc pl-4">
<li>Select dates and you will be prompted to create a new event</li>
<li>Drag, drop, and resize events</li>
<li>Click an event to delete it</li> <li>Click an event to delete it</li>
</ul> </ul>
</div> </div>
@ -66,7 +71,7 @@ export class Calendar extends React.Component {
/> />
Toggle weekends Toggle weekends
</label> </label>
<button className="btn btn-info" onClick={() => alert("Commit soon🥺")}> <button className="btn btn-info" onClick={() => this.handleGoogleClick()}>
Load Data from Google Load Data from Google
</button> </button>
</div> </div>

View File

@ -1,5 +1,6 @@
import { KanbanBoard } from "./kanbanBoard"; import { KanbanBoard } from "./kanbanBoard";
import { useState } from "react"; import { useState } from "react";
import { TableBoard } from "./tableBoard";
export const KanbanPage = () => { export const KanbanPage = () => {
const [activeTab, setActiveTab] = useState("kanban"); const [activeTab, setActiveTab] = useState("kanban");
@ -16,19 +17,21 @@ export const KanbanPage = () => {
<a <a
id="kanban" id="kanban"
className={`tab ${activeTab === "kanban" ? "tab-active" : ""}`} className={`tab ${activeTab === "kanban" ? "tab-active" : ""}`}
onClick={() => handleTabClick("kanban")}> onClick={() => handleTabClick("kanban")}
>
Kanban Kanban
</a> </a>
{/* <a <a
id="table" id="table"
className={`tab ${activeTab === "table" ? "tab-active" : ""}`} className={`tab ${activeTab === "table" ? "tab-active" : ""}`}
onClick={() => handleTabClick("table")}> onClick={() => handleTabClick("table")}
>
Table Table
</a> */} </a>
</div> </div>
</div> </div>
</div> </div>
<KanbanBoard /> {activeTab === "kanban" ? <KanbanBoard /> : <TableBoard />}
</div> </div>
); );
}; };

View File

@ -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 (
<div className="overflow-x-auto">
<table className="table">
{/* head */}
<thead>
<tr>
<th></th>
<th>Title</th>
<th>Description</th>
<th>Priority</th>
<th>Due Date</th>
</tr>
</thead>
<tbody>
{/* BODY */}
{tasks.map((task, index) => (
<tr key={index}>
<th>
<label></label>
</th>
<td>
<div className="flex items-center gap-3">
<div>
<div className="font-bold">{task.content}</div>
<div className="text-sm opacity-50">{task.content}</div>
</div>
</div>
</td>
<td>
{task.notes}
<br />
<span className="badge badge-ghost badge-sm">Description</span>
</td>
<td>{task.priority}</td>
<th>
<button className="btn btn-ghost btn-xs">{task.end_event}</button>
</th>
</tr>
))}
{/* END BODY */}
</tbody>
{/* foot */}
<tfoot>
<tr>
<th></th>
<th>Title</th>
<th>Description</th>
<th>Priority</th>
<th>Due Date</th>
</tr>
</tfoot>
</table>
</div>
);
}

View File

@ -50,12 +50,12 @@ export function TaskCard({ task, deleteTask, updateTask }) {
task.difficulty === 1 task.difficulty === 1
? "bg-blue-200 text-blue-700" ? "bg-blue-200 text-blue-700"
: task.difficulty === 2 : task.difficulty === 2
? "bg-green-200 text-green-700" ? "bg-green-200 text-green-700"
: task.difficulty === 3 : task.difficulty === 3
? "bg-yellow-200 text-yellow-700" ? "bg-yellow-200 text-yellow-700"
: task.difficulty === 4 : task.difficulty === 4
? "bg-red-200 text-red-700" ? "bg-red-200 text-red-700"
: "bg-purple-200 text-purple-700" : "bg-purple-200 text-purple-700"
}`}> }`}>
difficulty difficulty
</span> </span>
@ -71,12 +71,12 @@ export function TaskCard({ task, deleteTask, updateTask }) {
daysUntilDue >= 365 daysUntilDue >= 365
? "gray-200" ? "gray-200"
: daysUntilDue >= 30 : daysUntilDue >= 30
? "blue-200" ? "blue-200"
: daysUntilDue >= 7 : daysUntilDue >= 7
? "green-200" ? "green-200"
: daysUntilDue > 0 : daysUntilDue > 0
? "yellow-200" ? "yellow-200"
: "red-200"; : "red-200";
const formattedDueDate = const formattedDueDate =
daysUntilDue >= 365 daysUntilDue >= 365
@ -85,7 +85,10 @@ export function TaskCard({ task, deleteTask, updateTask }) {
month: "short", month: "short",
year: "numeric", 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 ( return (
<span className={`bg-${colorClass} text-[10px] font-xl font-bold px-2 py-1 rounded-full`}> <span className={`bg-${colorClass} text-[10px] font-xl font-bold px-2 py-1 rounded-full`}>
@ -122,13 +125,15 @@ export function TaskCard({ task, deleteTask, updateTask }) {
<TaskDetailModal <TaskDetailModal
taskId={task.id} taskId={task.id}
title={task.content} title={task.content}
description={task.description} description={task.notes}
tags={task.tags} tags={task.tags}
difficulty={task.difficulty} difficulty={task.difficulty}
challenge={task.challenge} challenge={task.challenge}
importance={task.importance} importance={task.importance}
updateTask={updateTask} updateTask={updateTask}
completed={task.completed} completed={task.completed}
start_event={task.start_event}
end_event={task.end_event}
/> />
{/* -------- Task Card -------- */} {/* -------- Task Card -------- */}

View File

@ -16,24 +16,48 @@ export function TaskDetailModal({
challenge, challenge,
importance, importance,
taskId, taskId,
start_event,
end_event,
updateTask, updateTask,
completed, 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 [isChallengeChecked, setChallengeChecked] = useState(challenge);
const [isImportantChecked, setImportantChecked] = useState(importance); const [isImportantChecked, setImportantChecked] = useState(importance);
const [currentDifficulty, setCurrentDifficulty] = useState((difficulty - 1) * 25); const [currentDifficulty, setCurrentDifficulty] = useState((difficulty - 1) * 25);
const [selectedTags, setSelectedTags] = useState([]); const [selectedTags, setSelectedTags] = useState([]);
const [dateStart, setDateStart] = useState(new Date()); const [dateStart, setDateStart] = useState(new Date(start_event));
const [dateEnd, setDateEnd] = useState(new Date()); const [dateEnd, setDateEnd] = useState(new Date(end_event));
const [startDateEnabled, setStartDateEnabled] = useState(false); const [startDateEnabled, setStartDateEnabled] = useState(false);
const [endDateEnabled, setEndDateEnabled] = useState(false); const [endDateEnabled, setEndDateEnabled] = useState(false);
const [isTaskComplete, setTaskComplete] = useState(completed); const [isTaskComplete, setTaskComplete] = useState(completed);
const [starteventValue, setStartEventValue] = useState("10:00 PM"); const [starteventValue, setStartEventValue] = useState(e);
const [endeventValue, setEndEventValue] = useState("11:00 AM"); const [endeventValue, setEndEventValue] = useState(s);
const [subtaskText, setSubtaskText] = useState(""); const [subtaskText, setSubtaskText] = useState("");
const [subtasks, setSubtasks] = useState([]); const [subtasks, setSubtasks] = useState([]);
const [currentTitle, setTitle] = useState(title); const [currentTitle, setTitle] = useState(title);
const [isTitleEditing, setTitleEditing] = useState(false); 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 handleTitleChange = async () => {
const data = { const data = {
@ -43,43 +67,113 @@ export function TaskDetailModal({
setTitleEditing(false); setTitleEditing(false);
}; };
const handleStartEventTimeChange = async (timeValue) => { {
const formattedTime = convertToFormattedTime(timeValue); /* -------- Time -------- */
setStartEventValue(formattedTime); }
console.log(formattedTime);
const data = {
startTime: formattedTime,
};
await updateTodoTaskPartial(taskId, data);
};
const handleEndEventTimeChange = async (timeValue) => { function formatAMPM(date) {
const inputTime = event.target.value; var hours = date.getHours();
// Validate the input time format var minutes = date.getMinutes();
if (!validateTimeFormat(inputTime)) { var ampm = hours >= 12 ? "PM" : "AM";
// Display an error message or handle invalid format hours = hours % 12;
console.error("Invalid time format. Please use HH:mm AM/PM"); 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; return;
} }
const formattedTime = convertToFormattedTime(timeValue); // Format incoming date
setEndEventValue(formattedTime); 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 = { 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); await updateTodoTaskPartial(taskId, data);
}; };
const convertToFormattedDate = (dateValue) => {
const formattedDate = format(dateValue, "yyyy-MM-dd'T'", {
timeZone: "UTC",
});
return formattedDate;
};
const convertToFormattedTime = (timeValue) => { const convertToFormattedTime = (timeValue) => {
const formattedTime = format(timeValue, "HH:mm:ss.SSSX", { timeZone: "UTC" }); // 2023-11-28 T [04:37:48.000000Z]
return formattedTime; // 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 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); return timeFormatRegex.test(time);
}; };
{
/* -------- END Time -------- */
}
const handleChallengeChange = async () => { const handleChallengeChange = async () => {
setChallengeChecked(!isChallengeChecked); setChallengeChecked(!isChallengeChecked);
const data = { const data = {
@ -89,9 +183,14 @@ export function TaskDetailModal({
}; };
const handleImportantChange = async () => { const handleImportantChange = async () => {
let important_num = 0;
if (isImportantChecked) {
important_num = 5;
}
setImportantChecked(!isImportantChecked); setImportantChecked(!isImportantChecked);
const data = { const data = {
important: !isImportantChecked, important: important_num,
}; };
await updateTodoTaskPartial(taskId, data); 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 () => { const handleTaskCompleteChange = async () => {
let completed = false; let completed = false;
if (isTaskComplete) { if (isTaskComplete) {
@ -183,7 +243,9 @@ export function TaskDetailModal({
try { try {
const updatedSubtasks = [...subtasks]; const updatedSubtasks = [...subtasks];
updatedSubtasks[index].completed = !updatedSubtasks[index].completed; 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); setSubtasks(updatedSubtasks);
} catch (error) { } catch (error) {
console.error("Error updating subtask:", error); console.error("Error updating subtask:", error);
@ -321,35 +383,37 @@ export function TaskDetailModal({
onChange={handleStartDateChange} onChange={handleStartDateChange}
/> />
<div className={`rounded p-2 shadow border-2 ${!startDateEnabled && "opacity-50"}`}> <div className={`rounded p-2 shadow border-2 ${!startDateEnabled && "opacity-50"}`}>
<DatePicker selected={dateStart} onChange={handleStartDateValueChange} disabled={!startDateEnabled} /> <DatePicker
selected={dateStart}
onChange={(dateStart) => setDateStart(dateStart)}
disabled={!startDateEnabled}
/>
</div> </div>
</div> </div>
</div> </div>
{/* Start event time picker */} {/* Start event time picker */}
<div className="rounded p-2 shadow border-2 ml-2 mt-4"> <div className="rounded mx-2 mt-4 flex flex-row items-center">
{/* handleStartEventTimeChange */} {/* handleStartEventTimeChange */}
<input <input
type="text" type="text"
placeholder="10:00 AM" placeholder={starteventValue}
value={starteventValue}
onChange={(starteventValue) => setStartEventValue(starteventValue)}
className="input input-bordered w-full max-w-xs" className="input input-bordered w-full max-w-xs"
onClick={handleStartEventTimeChange} disabled={!startDateEnabled}
/> />
</div> <div className="rounded mx-2">
<button
{/* Complete? */} className="btn btn-sm"
<div className="mx-4"> disabled={isTaskComplete}
<div className="flex items-center space-x-2 mt-4"> onClick={() => handleStartEventTimeChange(dateStart, starteventValue)}>
<div className="flex-1 flex-row card shadow border-2 p-2 pr-2"> Update Time
<p className="text-md mx-2">Complete</p> </button>
<input type="checkbox" checked={isTaskComplete} className="checkbox checkbox-xl bg-gray-400" />
<button className="btn btn-sm mt-2" onClick={handleStartEventTimeChange}>
Update Start Time
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
{/* End */} {/* End */}
<div> <div>
<p className="text-xs font-bold">End At</p> <p className="text-xs font-bold">End At</p>
@ -361,25 +425,62 @@ export function TaskDetailModal({
onChange={handleEndDateChange} onChange={handleEndDateChange}
/> />
<div className={`rounded p-2 shadow border-2 ${!endDateEnabled && "opacity-50"}`}> <div className={`rounded p-2 shadow border-2 ${!endDateEnabled && "opacity-50"}`}>
<DatePicker selected={dateEnd} onChange={handleEndDateValueChange} disabled={!endDateEnabled} /> <DatePicker selected={dateEnd} onChange={(dateEnd) => setDateEnd(dateEnd)} disabled={!endDateEnabled} />
</div> </div>
{/* End event time picker */} {/* End event time picker */}
<div className="rounded p-2 shadow border-2">this is time picker</div> <div className="rounded mx-2 flex flex-row items-center">
{/* handleEndEventTimeChange */}
<input
type="text"
placeholder={endeventValue}
className="input input-bordered w-full max-w-xs"
disabled={!endDateEnabled}
/>
<div className="rounded mx-2">
<button
className="btn btn-sm"
disabled={isTaskComplete}
onClick={() => handleEndEventTimeChange(dateEnd, endeventValue)}>
Update Time
</button>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
{/* Description */} {/* Description */}
<div className="flex flex-col gap-2"> {isDescriptionEditing ? (
<h2 className="font-bold"> <div className="flex flex-col gap-2">
<span className="flex gap-2"> <h2 className="font-bold">
<FaRegListAlt className="my-1" /> <span className="flex gap-2">
Description <FaRegListAlt className="my-1" />
</span> Description
</h2> </span>
<textarea className="textarea w-full" disabled> </h2>
{description} <textarea
</textarea> className="textarea w-full"
</div> value={updatedDescription}
onChange={(e) => setUpdatedDescription(e.target.value)}
/>
<button className="btn btn-sm" onClick={handleDescriptionChange}>
Save
</button>
</div>
) : (
<div className="flex flex-col gap-2">
<h2 className="font-bold">
<span className="flex gap-2">
<FaRegListAlt className="my-1" />
Description
</span>
</h2>
<textarea className="textarea w-full" disabled>
{description}
</textarea>
<FaPencil className="cursor-pointer" onClick={handleDescriptionEditToggle} />
</div>
)}
{/* Difficulty, Challenge, and Importance */} {/* Difficulty, Challenge, and Importance */}
<div className="flex flex-row space-x-3 my-4"> <div className="flex flex-row space-x-3 my-4">
<div className="flex-1 card shadow border-2 p-2"> <div className="flex-1 card shadow border-2 p-2">
@ -430,6 +531,20 @@ export function TaskDetailModal({
</label> </label>
</div> </div>
</div> </div>
{/* Complete? */}
<div className="card shadow border-2 p-2">
<div className="form-control">
<label className="label cursor-pointer space-x-2">
<span className="label-text">Complete</span>
<input
type="checkbox"
checked={isTaskComplete}
className="checkbox bg-black"
onChange={handleTaskCompleteChange}
/>
</label>
</div>
</div>
</div> </div>
{/* Subtask */} {/* Subtask */}
<div className="flex flex-col pt-2"> <div className="flex flex-col pt-2">