Files
coms3011a-lab1/components/Column.tsx

318 lines
7.9 KiB
TypeScript
Raw Normal View History

"use client";
import { useState } from "react";
export default function Column({
title,
tasks,
onAdd,
onStatusChange,
}: {
title: string;
tasks: any[];
onAdd?: () => void;
onStatusChange?: (index: number, newStatus: "Todo" | "In-Progress" | "Complete") => void;
}) {
const [popupTaskIndex, setPopupTaskIndex] = useState<number | null>(null);
const [sortMode, setSortMode] = useState<"none" | "topic" | "dueDate">("none");
const [showMenu, setShowMenu] = useState(false);
const handleCircleClick = (index: number) => {
const task = tasks[index];
if (task.status !== "Complete") {
setPopupTaskIndex(index);
}
};
const handleConfirm = (yes: boolean) => {
if (popupTaskIndex !== null && onStatusChange) {
const task = tasks[popupTaskIndex];
if (task.status === "Todo" && yes) {
onStatusChange(popupTaskIndex, "In-Progress");
} else if (task.status === "In-Progress" && yes) {
onStatusChange(popupTaskIndex, "Complete");
}
}
setPopupTaskIndex(null);
};
const handleSort = (mode: "none" | "topic" | "dueDate") => {
setSortMode(mode);
setShowMenu(false);
};
const sortedTasks = [...tasks].sort((a, b) => {
if (sortMode === "topic") {
return a.topic.localeCompare(b.topic);
}
if (sortMode === "dueDate") {
return new Date(a.dueDate).getTime() - new Date(b.dueDate).getTime();
}
return 0;
});
return (
<div style={styles.column}>
<div style={styles.columnHeader}>
<h2 style={styles.columnTitle}>{title}</h2>
<div style={styles.headerActions}>
{onAdd && (
<button onClick={onAdd} style={styles.iconButton} title="Add Task">
+
</button>
)}
{/* Sort button */}
<div style={{ position: "relative" }}>
<button
onClick={() => setShowMenu(!showMenu)}
style={styles.iconButton}
title="Sort Tasks"
>
</button>
{showMenu && (
<div style={styles.dropdown}>
<button onClick={() => handleSort("topic")} style={styles.dropdownItem}>
Sort by Topic
</button>
<button onClick={() => handleSort("dueDate")} style={styles.dropdownItem}>
Sort by Due Date
</button>
<button onClick={() => handleSort("none")} style={styles.dropdownItem}>
Clear Sorting
</button>
</div>
)}
</div>
<button style={styles.iconButton} title="More Options"></button>
</div>
</div>
{sortMode !== "none" && (
<div style={styles.sortLabel}>
Currently sorted by: {sortMode === "topic" ? "Topic" : "Due Date"}
</div>
)}
{sortedTasks.map((task, index) => (
<div key={index} style={styles.card}>
<div style={styles.cardHeader}>
{/* Hollow circle */}
<div
style={{
...styles.circle,
cursor: task.status === "Complete" ? "default" : "pointer",
}}
onClick={() => handleCircleClick(index)}
title="Change Status"
>
{task.status === "In-Progress" || task.status === "Complete" ? "✔" : ""}
</div>
<div style={styles.cardTitle}>{task.title}</div>
<button style={styles.iconButton}></button>
</div>
<div style={styles.cardDescription}>
{task.description.slice(0, 30)}
{task.description.length > 30 && "..."}
</div>
<div style={styles.cardDate}>📅 {task.dueDate}</div>
{task.topic && (
<div style={styles.cardTopic}>
<em>{task.topic}</em>
</div>
)}
</div>
))}
{/* Popup */}
{popupTaskIndex !== null && (
<div style={styles.overlay}>
<div style={styles.modal}>
<h3 style={styles.title}>
{tasks[popupTaskIndex].status === "Todo"
? "Are you ready to start the task?"
: "Have you finished this task?"}
</h3>
<div style={styles.actions}>
<button onClick={() => handleConfirm(true)} style={styles.saveButton}>
Yes
</button>
<button onClick={() => handleConfirm(false)} style={styles.cancelButton}>
No
</button>
</div>
</div>
</div>
)}
</div>
);
}
const styles = {
column: {
background: "#f8fafc",
padding: "15px",
borderRadius: "12px",
flex: 1,
minHeight: "65vh",
maxHeight: "65vh",
overflowY: "auto" as const,
boxShadow: "0 10px 25px rgba(0,0,0,0.1)",
},
columnHeader: {
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "15px",
},
columnTitle: {
color: "#0f172a",
fontWeight: "700",
fontSize: "25px",
margin: 0,
},
iconButton: {
background: "transparent",
border: "none",
color: "#0f172a",
fontSize: "22px",
fontWeight: "600",
cursor: "pointer",
padding: "0",
display: "flex",
alignItems: "center",
justifyContent: "center",
},
headerActions: {
display: "flex",
gap: "10px",
alignItems: "center",
},
sortLabel: {
fontSize: "13px",
opacity: 0.7,
marginBottom: "10px",
fontStyle: "italic",
color: "#0f172a",
},
card: {
background: "#dae4f0",
padding: "18px",
borderRadius: "12px",
marginBottom: "15px",
color: "#0f172a",
fontSize: "16px",
},
cardHeader: {
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "6px",
gap: "10px",
},
cardTitle: {
fontWeight: "700",
flex: 1,
},
cardDescription: {
fontSize: "13px",
opacity: 0.7,
marginBottom: "8px",
},
cardDate: {
fontSize: "12px",
opacity: 0.6,
},
cardTopic: {
fontSize: "12px",
opacity: 0.7,
textAlign: "right" as const,
marginTop: "6px",
fontStyle: "italic",
},
circle: {
width: "20px",
height: "20px",
border: "2px solid black",
borderRadius: "50%",
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: "14px",
fontWeight: "bold",
backgroundColor: "white",
},
overlay: {
position: "fixed" as const,
top: 0,
left: 0,
width: "100%",
height: "100%",
background: "rgba(0,0,0,0.4)",
display: "flex",
justifyContent: "center",
alignItems: "center",
zIndex: 1000,
},
modal: {
background: "#f8fafc",
padding: "25px",
borderRadius: "12px",
boxShadow: "0 10px 25px rgba(0,0,0,0.2)",
width: "300px",
textAlign: "center" as const,
},
title: {
marginBottom: "15px",
fontSize: "18px",
fontWeight: "600",
color: "#0f172a",
},
actions: {
display: "flex",
justifyContent: "space-between",
marginTop: "15px",
},
saveButton: {
background: "#0ea5e9",
color: "white",
border: "none",
padding: "8px 16px",
borderRadius: "8px",
cursor: "pointer",
fontWeight: "600",
},
cancelButton: {
background: "#e2e8f0",
color: "#0f172a",
border: "none",
padding: "8px 16px",
borderRadius: "8px",
cursor: "pointer",
fontWeight: "600",
},
dropdown: {
position: "absolute" as const,
top: "30px",
right: 0,
background: "#f8fafc",
border: "1px solid #cbd5e1",
borderRadius: "8px",
boxShadow: "0 5px 15px rgba(0,0,0,0.1)",
display: "flex",
flexDirection: "column" as const,
zIndex: 100,
},
dropdownItem: {
padding: "8px 12px",
background: "transparent",
border: "none",
textAlign: "left" as const,
cursor: "pointer",
fontSize: "14px",
color: "#0f172a",
},
};