diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx
index 0170353..a3fead6 100644
--- a/app/dashboard/page.tsx
+++ b/app/dashboard/page.tsx
@@ -1,6 +1,8 @@
"use client";
import { useState } from "react";
+import Column from "@/components/Column";
+import AddTaskForm from "@/components/AddTaskForm";
export default function Dashboard() {
const [tasks, setTasks] = useState({
@@ -22,19 +24,12 @@ export default function Dashboard() {
done: [],
});
- const addTask = () => {
- const title = prompt("Task title:");
- if (!title) return;
-
- const description = prompt("Description:") || "";
- const dueDate = prompt("Due date (YYYY-MM-DD):") || "";
- const topic = prompt("Topic:") || "";
-
- const newTask = { title, description, dueDate, topic };
+ const [showForm, setShowForm] = useState(false);
+ const addTask = (task: any) => {
setTasks((prev) => ({
...prev,
- todo: [...prev.todo, newTask],
+ todo: [...prev.todo, task],
}));
};
@@ -43,70 +38,27 @@ export default function Dashboard() {
Planner Danner
-
+ setShowForm(true)} />
+
+ {showForm && (
+ setShowForm(false)}
+ />
+ )}
);
}
-function Column({
- title,
- tasks,
- onAdd,
-}: {
- title: string;
- tasks: any[];
- onAdd?: () => void;
-}) {
- return (
-
-
-
{title}
-
-
- {onAdd && (
-
- )}
-
- {/* ✅ FIXED: sort now uses SAME button style */}
-
-
-
-
-
-
- {tasks.map((task, index) => (
-
-
-
{task.title}
-
- {/* same exact style */}
-
-
-
-
- {task.description.slice(0, 30)}
- {task.description.length > 30 && "..."}
-
-
-
📅 {task.dueDate}
-
- ))}
-
- );
-}
-
const styles = {
container: {
minHeight: "100vh",
background: "linear-gradient(to bottom, #38bdf8, #0ea5e9)",
padding: "30px",
},
-
title: {
textAlign: "center" as const,
fontSize: "90px",
@@ -119,84 +71,10 @@ const styles = {
`,
marginBottom: "30px",
},
-
board: {
display: "flex",
gap: "20px",
justifyContent: "center",
},
+};
- column: {
- background: "#f8fafc",
- padding: "15px",
- borderRadius: "12px",
- flex: 1,
- minHeight: "65vh",
- maxHeight: "65vh",
- overflowY: "auto",
- 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,
- },
-
- /* ✅ SINGLE SOURCE OF TRUTH FOR ALL ICONS */
- iconButton: {
- background: "transparent",
- border: "none",
- color: "#0f172a",
- fontSize: "22px",
- fontWeight: "600",
- cursor: "pointer",
- padding: "0",
- },
-
- headerActions: {
- display: "flex",
- gap: "10px",
- alignItems: "center",
- },
-
- card: {
- background: "#dae4f0",
- padding: "18px",
- borderRadius: "12px",
- marginBottom: "15px",
- color: "#0f172a",
- fontSize: "16px",
- },
-
- cardHeader: {
- display: "flex",
- justifyContent: "space-between",
- alignItems: "center",
- marginBottom: "6px",
- },
-
- cardTitle: {
- fontWeight: "700",
- marginBottom: "6px",
- },
-
- cardDescription: {
- fontSize: "13px",
- opacity: 0.7,
- marginBottom: "8px",
- },
-
- cardDate: {
- fontSize: "12px",
- opacity: 0.6,
- },
-};
\ No newline at end of file
diff --git a/app/globals.css b/app/globals.css
index a2dc41e..dfb35d2 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -1,26 +1,35 @@
-@import "tailwindcss";
-
:root {
- --background: #ffffff;
- --foreground: #171717;
-}
-
-@theme inline {
- --color-background: var(--background);
- --color-foreground: var(--foreground);
- --font-sans: var(--font-geist-sans);
- --font-mono: var(--font-geist-mono);
-}
-
-@media (prefers-color-scheme: dark) {
- :root {
- --background: #0a0a0a;
- --foreground: #ededed;
- }
+ --primary-bg: linear-gradient(to bottom, #38bdf8, #0ea5e9);
+ --card-bg: #dae4f0;
+ --text-dark: #0f172a;
+ --text-light: #e0f2fe;
+ --accent: #0284c7;
}
body {
- background: var(--background);
- color: var(--foreground);
- font-family: Arial, Helvetica, sans-serif;
+ font-family: 'Inter', sans-serif;
+ margin: 0;
+ padding: 0;
+ background: var(--primary-bg);
}
+
+h1, h2, h3 {
+ font-weight: 900;
+ letter-spacing: 2px;
+}
+
+/* ✅ Consistent placeholder styling */
+input::placeholder,
+textarea::placeholder {
+ color: #6b7280; /* same grey as Title/Description */
+ font-family: inherit;
+ font-size: 14px;
+}
+
+/* ✅ Style the dropdown placeholder option */
+select option[value=""] {
+ color: #6b7280; /* same grey */
+ font-family: inherit;
+ font-size: 14px;
+}
+
diff --git a/components/AddTaskForm.tsx b/components/AddTaskForm.tsx
new file mode 100644
index 0000000..318b808
--- /dev/null
+++ b/components/AddTaskForm.tsx
@@ -0,0 +1,183 @@
+"use client";
+
+import { useState } from "react";
+
+export default function AddTaskForm({
+ onSubmit,
+ onClose,
+}: {
+ onSubmit: (task: any) => void;
+ onClose: () => void;
+}) {
+ const [formData, setFormData] = useState({
+ title: "",
+ description: "",
+ dueDate: "",
+ topic: "",
+ });
+
+ const handleChange = (
+ e: React.ChangeEvent
+ ) => {
+ setFormData({ ...formData, [e.target.name]: e.target.value });
+ };
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ if (!formData.topic) {
+ alert("Please select a topic before saving.");
+ return;
+ }
+ onSubmit(formData);
+ onClose();
+ };
+
+ return (
+
+ );
+}
+
+const styles = {
+ 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: "350px",
+ },
+ title: {
+ marginBottom: "15px",
+ fontSize: "22px",
+ fontWeight: "700",
+ color: "var(--text-dark)",
+ textAlign: "center" as const,
+ },
+ form: {
+ display: "flex",
+ flexDirection: "column" as const,
+ gap: "12px",
+ },
+ input: {
+ padding: "10px",
+ borderRadius: "8px",
+ border: "1px solid #cbd5e1",
+ fontSize: "14px",
+ color: "var(--text-dark)", // dark text
+ fontFamily: "inherit", // same font as title/description
+},
+
+textarea: {
+ padding: "10px",
+ borderRadius: "8px",
+ border: "1px solid #cbd5e1",
+ fontSize: "14px",
+ color: "var(--text-dark)",
+ fontFamily: "inherit",
+ minHeight: "60px",
+},
+
+select: {
+ padding: "10px",
+ borderRadius: "8px",
+ border: "1px solid #cbd5e1",
+ fontSize: "14px",
+ color: "var(--text-dark)",
+ fontFamily: "inherit",
+ backgroundColor: "white", // consistent with inputs
+},
+
+ 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",
+ },
+};
+
+
+
diff --git a/components/Board.tsx b/components/Board.tsx
new file mode 100644
index 0000000..24fbb79
--- /dev/null
+++ b/components/Board.tsx
@@ -0,0 +1,36 @@
+"use client";
+import { useState } from "react";
+import Column from "./Column";
+
+export default function Board() {
+ const [tasks, setTasks] = useState([
+ { id: 1, title: "Task 1", description: "Do something", status: "Todo", dueDate: "2026-08-10", topic: "Lab" },
+ { id: 2, title: "Task 2", description: "Another thing", status: "In-Progress", dueDate: "2026-08-12", topic: "Project" },
+ ]);
+
+ const handleStatusChange = (taskId: number, newStatus: "Todo" | "In-Progress" | "Complete") => {
+ setTasks(prev =>
+ prev.map(t => (t.id === taskId ? { ...t, status: newStatus } : t))
+ );
+ };
+
+ return (
+
+ t.status === "Todo")}
+ onStatusChange={handleStatusChange}
+ />
+ t.status === "In-Progress")}
+ onStatusChange={handleStatusChange}
+ />
+ t.status === "Complete")}
+ onStatusChange={handleStatusChange}
+ />
+
+ );
+}
diff --git a/components/Column.tsx b/components/Column.tsx
new file mode 100644
index 0000000..024ef3c
--- /dev/null
+++ b/components/Column.tsx
@@ -0,0 +1,318 @@
+"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(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 (
+
+
+
{title}
+
+ {onAdd && (
+
+ )}
+
+ {/* Sort button */}
+
+
+ {showMenu && (
+
+
+
+
+
+ )}
+
+
+
+
+
+
+ {sortMode !== "none" && (
+
+ Currently sorted by: {sortMode === "topic" ? "Topic" : "Due Date"}
+
+ )}
+
+ {sortedTasks.map((task, index) => (
+
+
+ {/* Hollow circle */}
+
handleCircleClick(index)}
+ title="Change Status"
+ >
+ {task.status === "In-Progress" || task.status === "Complete" ? "✔" : ""}
+
+
+
{task.title}
+
+
+
+ {task.description.slice(0, 30)}
+ {task.description.length > 30 && "..."}
+
+
📅 {task.dueDate}
+ {task.topic && (
+
+ {task.topic}
+
+ )}
+
+ ))}
+
+ {/* Popup */}
+ {popupTaskIndex !== null && (
+
+
+
+ {tasks[popupTaskIndex].status === "Todo"
+ ? "Are you ready to start the task?"
+ : "Have you finished this task?"}
+
+
+
+
+
+
+
+ )}
+
+ );
+}
+
+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",
+ },
+};
\ No newline at end of file
diff --git a/components/TaskCard.tsx b/components/TaskCard.tsx
new file mode 100644
index 0000000..e69de29