From c42e830ffe332295883484244a0b9ea3e18fb6e2 Mon Sep 17 00:00:00 2001 From: "DANUSHKA\\danus" Date: Fri, 23 May 2025 09:10:28 +0200 Subject: [PATCH 01/15] taskform initiated --- package.json | 6 +++++- src/Components/TaskForm.jsx | 17 +++++++++++++++++ src/Components/TaskItem.jsx | 0 src/Components/TaskList.jsx | 0 src/Components/TaskStats.jsx | 0 src/data/useTaskData.js | 0 6 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 src/Components/TaskForm.jsx create mode 100644 src/Components/TaskItem.jsx create mode 100644 src/Components/TaskList.jsx create mode 100644 src/Components/TaskStats.jsx create mode 100644 src/data/useTaskData.js diff --git a/package.json b/package.json index caf6289..003b8a3 100644 --- a/package.json +++ b/package.json @@ -11,17 +11,21 @@ }, "dependencies": { "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "zustand": "^5.0.5" }, "devDependencies": { "@eslint/js": "^9.21.0", "@types/react": "^19.0.10", "@types/react-dom": "^19.0.4", "@vitejs/plugin-react": "^4.3.4", + "autoprefixer": "^10.4.21", "eslint": "^9.21.0", "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-refresh": "^0.4.19", "globals": "^15.15.0", + "postcss": "^8.5.3", + "tailwindcss": "^4.1.7", "vite": "^6.2.0" } } diff --git a/src/Components/TaskForm.jsx b/src/Components/TaskForm.jsx new file mode 100644 index 0000000..861ff75 --- /dev/null +++ b/src/Components/TaskForm.jsx @@ -0,0 +1,17 @@ +export default function TaskForm({ onSubmit, input, setInput }) { + return ( +
+ setInput(e.target.value)} + aria-label="New task" + /> + +
+ ); +} \ No newline at end of file diff --git a/src/Components/TaskItem.jsx b/src/Components/TaskItem.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/Components/TaskList.jsx b/src/Components/TaskList.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/Components/TaskStats.jsx b/src/Components/TaskStats.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/data/useTaskData.js b/src/data/useTaskData.js new file mode 100644 index 0000000..e69de29 From 0c5b0f1aec4b25e7d12977bc094e9b655db27be5 Mon Sep 17 00:00:00 2001 From: "DANUSHKA\\danus" Date: Fri, 23 May 2025 09:11:06 +0200 Subject: [PATCH 02/15] taskitem initiated --- src/Components/TaskItem.jsx | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/Components/TaskItem.jsx b/src/Components/TaskItem.jsx index e69de29..3cbda21 100644 --- a/src/Components/TaskItem.jsx +++ b/src/Components/TaskItem.jsx @@ -0,0 +1,22 @@ +export default function TaskItem({ task, onToggle, onRemove }) { + return ( +
  • + onToggle(task.id)} + onKeyDown={(e) => e.key === 'Enter' && onToggle(task.id)} + > + {task.text} + + +
  • + ); +} \ No newline at end of file From 12ec77201aeaec3b42daad1e25c4d9d0e7a81c35 Mon Sep 17 00:00:00 2001 From: "DANUSHKA\\danus" Date: Fri, 23 May 2025 09:11:43 +0200 Subject: [PATCH 03/15] tasklist initiated --- src/Components/TaskList.jsx | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/Components/TaskList.jsx b/src/Components/TaskList.jsx index e69de29..c6a992a 100644 --- a/src/Components/TaskList.jsx +++ b/src/Components/TaskList.jsx @@ -0,0 +1,22 @@ +import TaskItem from './TaskItem'; + +export default function TaskList({ tasks, onToggle, onRemove }) { + if (tasks.length === 0) { + return ( +
    No tasks yet. Start by adding one!
    + ); + } + + return ( +
      + {tasks.map((task) => ( + + ))} +
    + ); +} \ No newline at end of file From f248cdb03c6c18b353b61c8a187e70397ec0e6c7 Mon Sep 17 00:00:00 2001 From: "DANUSHKA\\danus" Date: Fri, 23 May 2025 09:12:31 +0200 Subject: [PATCH 04/15] taskstat initiated --- src/Components/TaskStats.jsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Components/TaskStats.jsx b/src/Components/TaskStats.jsx index e69de29..ffc0e2a 100644 --- a/src/Components/TaskStats.jsx +++ b/src/Components/TaskStats.jsx @@ -0,0 +1,10 @@ +export default function TaskStats({ tasks }) { + const total = tasks.length; + const uncompleted = tasks.filter((t) => !t.completed).length; + + return ( +
    + Total: {total} | Uncompleted: {uncompleted} +
    + ); +} \ No newline at end of file From 99a5ae057c50ede9a6b84306eba914782fa9da94 Mon Sep 17 00:00:00 2001 From: "DANUSHKA\\danus" Date: Fri, 23 May 2025 13:00:54 +0200 Subject: [PATCH 05/15] import tailwind --- index.css | 5 +++++ package.json | 2 ++ src/App.jsx | 31 +++++++++++++++++++++++++++---- src/data/useTaskData.js | 21 +++++++++++++++++++++ src/index.css | 1 + src/main.jsx | 2 +- vite.config.js | 4 ++-- 7 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 index.css diff --git a/index.css b/index.css new file mode 100644 index 0000000..fa10bf9 --- /dev/null +++ b/index.css @@ -0,0 +1,5 @@ +/* @tailwind base; +@tailwind components; +@tailwind utilities; */ + +@import "tailwindcss"; \ No newline at end of file diff --git a/package.json b/package.json index 003b8a3..4f237ec 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,8 @@ }, "devDependencies": { "@eslint/js": "^9.21.0", + "@tailwindcss/postcss": "^4.1.7", + "@tailwindcss/vite": "^4.1.7", "@types/react": "^19.0.10", "@types/react-dom": "^19.0.4", "@vitejs/plugin-react": "^4.3.4", diff --git a/src/App.jsx b/src/App.jsx index 5427540..b212646 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,5 +1,28 @@ -export const App = () => { +import { useState } from 'react'; +import useTaskStore from './data/useTaskData'; +import TaskForm from './Components/TaskForm'; +import TaskList from './Components/TaskList'; +import TaskStats from './Components/TaskStats'; + +export default function App() { + const { tasks, addTask, removeTask, toggleTask } = useTaskStore(); + const [input, setInput] = useState(''); + + const handleSubmit = (e) => { + e.preventDefault(); + if (!input.trim()) return; + addTask(input.trim()); + setInput(''); + }; + return ( -

    React Boilerplate

    - ) -} +
    +

    Task Manager

    +
    + + + +
    +
    + ); +} \ No newline at end of file diff --git a/src/data/useTaskData.js b/src/data/useTaskData.js index e69de29..43379cb 100644 --- a/src/data/useTaskData.js +++ b/src/data/useTaskData.js @@ -0,0 +1,21 @@ +import { create } from 'zustand'; + +const useTaskStore = create((set) => ({ + tasks: [], + addTask: (text) => + set((state) => ({ + tasks: [...state.tasks, { id: Date.now(), text, completed: false }], + })), + removeTask: (id) => + set((state) => ({ + tasks: state.tasks.filter((task) => task.id !== id), + })), + toggleTask: (id) => + set((state) => ({ + tasks: state.tasks.map((task) => + task.id === id ? { ...task, completed: !task.completed } : task + ), + })), +})); + +export default useTaskStore; \ No newline at end of file diff --git a/src/index.css b/src/index.css index f7c0aef..9161031 100644 --- a/src/index.css +++ b/src/index.css @@ -1,3 +1,4 @@ :root { font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; } +@import "tailwindcss"; \ No newline at end of file diff --git a/src/main.jsx b/src/main.jsx index 1b8ffe9..cbf4411 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,7 +1,7 @@ import React from 'react' import ReactDOM from 'react-dom/client' -import { App } from './App.jsx' +import App from './App.jsx' import './index.css' diff --git a/vite.config.js b/vite.config.js index ba24244..87f6aae 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,7 +1,7 @@ import react from '@vitejs/plugin-react' import { defineConfig } from 'vite' - +import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ - plugins: [react()] + plugins: [react(),tailwindcss()] }) From fe03c1cdd3f4319101f313558dbc07acd4b6b8f3 Mon Sep 17 00:00:00 2001 From: "DANUSHKA\\danus" Date: Fri, 23 May 2025 13:54:55 +0200 Subject: [PATCH 06/15] duedate feild added to the form --- package.json | 1 + src/App.jsx | 15 ++++++++++++--- src/Components/TaskForm.jsx | 9 ++++++++- src/Components/TaskItem.jsx | 15 ++++++++++++++- src/data/useTaskData.js | 17 +++++++++++++---- 5 files changed, 48 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 4f237ec..75972e1 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "date-fns": "^4.1.0", "react": "^19.0.0", "react-dom": "^19.0.0", "zustand": "^5.0.5" diff --git a/src/App.jsx b/src/App.jsx index b212646..2c101fe 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -7,11 +7,12 @@ import TaskStats from './Components/TaskStats'; export default function App() { const { tasks, addTask, removeTask, toggleTask } = useTaskStore(); const [input, setInput] = useState(''); + const [dueDate, setDueDate] = useState(''); // date state const handleSubmit = (e) => { e.preventDefault(); if (!input.trim()) return; - addTask(input.trim()); + addTask(input.trim(),dueDate); // pass dueDate to addTask setInput(''); }; @@ -19,8 +20,16 @@ export default function App() {

    Task Manager

    - - + +
    diff --git a/src/Components/TaskForm.jsx b/src/Components/TaskForm.jsx index 861ff75..80d26a0 100644 --- a/src/Components/TaskForm.jsx +++ b/src/Components/TaskForm.jsx @@ -1,4 +1,4 @@ -export default function TaskForm({ onSubmit, input, setInput }) { +export default function TaskForm({ onSubmit, input, setInput,dueDate, setDueDate }) { return (
    setInput(e.target.value)} aria-label="New task" /> + setDueDate(e.target.value)} + aria-label="Due date" + className="border rounded px-3 py-2" + /> diff --git a/src/Components/TaskItem.jsx b/src/Components/TaskItem.jsx index 3cbda21..aef402a 100644 --- a/src/Components/TaskItem.jsx +++ b/src/Components/TaskItem.jsx @@ -1,3 +1,6 @@ +import { format } from 'date-fns'; + + export default function TaskItem({ task, onToggle, onRemove }) { return (
  • @@ -8,7 +11,17 @@ export default function TaskItem({ task, onToggle, onRemove }) { onClick={() => onToggle(task.id)} onKeyDown={(e) => e.key === 'Enter' && onToggle(task.id)} > - {task.text} + {task.text}{task.dueDate && ( + + {format(new Date(task.dueDate), 'dd/MM/yyyy')} + + )} +{task.createdAt && ( + + Added: {format(new Date(task.createdAt), 'dd MMM yyyy, p')} + +)} + ); -} \ No newline at end of file +} diff --git a/src/Components/TaskList.jsx b/src/Components/TaskList.jsx index c6a992a..d14a24b 100644 --- a/src/Components/TaskList.jsx +++ b/src/Components/TaskList.jsx @@ -1,13 +1,31 @@ import TaskItem from './TaskItem'; -export default function TaskList({ tasks, onToggle, onRemove }) { +export default function TaskList({ tasks, onToggle, onRemove, onCompleteAll }) { if (tasks.length === 0) { return (
    No tasks yet. Start by adding one!
    ); } + const allCompleted = tasks.every(task => task.completed); + + return ( +
    +
    + +
    +
      {tasks.map((task) => ( ))}
    +
    ); } \ No newline at end of file diff --git a/src/data/useTaskData.js b/src/data/useTaskData.js index b6b00ff..7ff51ad 100644 --- a/src/data/useTaskData.js +++ b/src/data/useTaskData.js @@ -25,6 +25,10 @@ const useTaskStore = create((set) => ({ task.id === id ? { ...task, completed: !task.completed } : task ), })), + completeAllTasks: () => + set((state) => ({ + tasks: state.tasks.map((task) => ({ ...task, completed: true })), + })), })); export default useTaskStore; From fddfca9f92f33aa288b316e2bb71edc9665b401c Mon Sep 17 00:00:00 2001 From: "DANUSHKA\\danus" Date: Mon, 2 Jun 2025 13:07:04 +0200 Subject: [PATCH 08/15] complete button --- src/Components/TaskList.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/TaskList.jsx b/src/Components/TaskList.jsx index d14a24b..fb840f2 100644 --- a/src/Components/TaskList.jsx +++ b/src/Components/TaskList.jsx @@ -22,7 +22,7 @@ export default function TaskList({ tasks, onToggle, onRemove, onCompleteAll }) { : 'bg-blue-500 hover:bg-blue-600 text-white' }`} > - compplete all + Complete all From c8bb1812b092b653024c7eb2a5fa36adb959c43c Mon Sep 17 00:00:00 2001 From: "DANUSHKA\\danus" Date: Fri, 13 Jun 2025 12:24:26 +0200 Subject: [PATCH 09/15] styles changed --- package.json | 6 ++-- postcss.config.js | 6 ++++ src/App.jsx | 32 +++++++++++++++++-- src/Components/TaskItem.jsx | 1 + src/Components/TaskList.jsx | 63 +++++++++++++++++++++---------------- src/data/useTaskData.js | 10 +++--- src/index.css | 12 +++++-- tailwind.config.js | 9 ++++++ 8 files changed, 98 insertions(+), 41 deletions(-) create mode 100644 postcss.config.js create mode 100644 tailwind.config.js diff --git a/package.json b/package.json index 75972e1..8cd844c 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ }, "devDependencies": { "@eslint/js": "^9.21.0", - "@tailwindcss/postcss": "^4.1.7", + "@tailwindcss/postcss": "^4.1.8", "@tailwindcss/vite": "^4.1.7", "@types/react": "^19.0.10", "@types/react-dom": "^19.0.4", @@ -27,8 +27,8 @@ "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-refresh": "^0.4.19", "globals": "^15.15.0", - "postcss": "^8.5.3", - "tailwindcss": "^4.1.7", + "postcss": "^8.5.4", + "tailwindcss": "^4.1.8", "vite": "^6.2.0" } } diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..44d2119 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + '@tailwindcss/postcss': {}, + autoprefixer: {}, + }, +}; \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx index 9b3148b..5d312f8 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useState, useEffect } from "react"; import useTaskStore from "./data/useTaskData"; import TaskForm from "./Components/TaskForm"; import TaskList from "./Components/TaskList"; @@ -10,6 +10,19 @@ export default function App() { const [input, setInput] = useState(""); const [dueDate, setDueDate] = useState(""); // date state + const [darkMode, setDarkMode] = useState(false); + + + +useEffect(() => { + const root = window.document.documentElement; + if (darkMode) { + root.classList.add('dark'); + } else { + root.classList.remove('dark'); + } + }, [darkMode]); + const handleSubmit = (e) => { e.preventDefault(); if (!input.trim()) return; @@ -18,7 +31,13 @@ export default function App() { }; return ( -
    +
    +

    Task Manager

    - +
    +
    + No tasks yet. Start by adding one! +
    ); } - const allCompleted = tasks.every(task => task.completed); - + const allCompleted = tasks.every((task) => task.completed); return ( -
    -
    - -
    + +
    -
      - {tasks.map((task) => ( - - ))} -
    - +
      + {tasks.map((task) => ( + + ))} +
    + ); -} \ No newline at end of file +} diff --git a/src/data/useTaskData.js b/src/data/useTaskData.js index 7ff51ad..24b4849 100644 --- a/src/data/useTaskData.js +++ b/src/data/useTaskData.js @@ -11,7 +11,7 @@ const useTaskStore = create((set) => ({ text, dueDate, completed: false, - createdAt: new Date().toISOString() + createdAt: new Date().toISOString(), }, ], })), @@ -25,10 +25,10 @@ const useTaskStore = create((set) => ({ task.id === id ? { ...task, completed: !task.completed } : task ), })), - completeAllTasks: () => - set((state) => ({ - tasks: state.tasks.map((task) => ({ ...task, completed: true })), - })), + completeAllTasks: () => + set((state) => ({ + tasks: state.tasks.map((task) => ({ ...task, completed: true })), + })), })); export default useTaskStore; diff --git a/src/index.css b/src/index.css index 9161031..2ababe5 100644 --- a/src/index.css +++ b/src/index.css @@ -1,4 +1,10 @@ -:root { - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; -} +/* +@tailwind base; +@tailwind components; +@tailwind utilities; */ + +/* @tailwind base; +@tailwind components; +@tailwind utilities; */ + @import "tailwindcss"; \ No newline at end of file diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..df778f9 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,9 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ['./index.html', './src/**/*.{js,jsx,ts,tsx}'], + theme: { + extend: {}, + }, + darkMode: 'class', // optional: enables dark mode support + plugins: [], +}; \ No newline at end of file From 1a869da1ceb860886da0a71dcb58447eb7982087 Mon Sep 17 00:00:00 2001 From: "DANUSHKA\\danus" Date: Fri, 13 Jun 2025 12:38:37 +0200 Subject: [PATCH 10/15] resposive layout added --- src/App.jsx | 62 +++++++++++++++++------------------- src/Components/TaskForm.jsx | 16 ++++++---- src/Components/TaskItem.jsx | 38 +++++++++++----------- src/Components/TaskList.jsx | 33 ++++++++++++------- src/Components/TaskStats.jsx | 6 ++-- 5 files changed, 85 insertions(+), 70 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 5d312f8..4e6ba43 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -32,37 +32,35 @@ useEffect(() => { return (
    - -

    Task Manager

    -
    - - - - -
    -
    +
    + +

    Task Manager

    + + + + +
    +
    ); } diff --git a/src/Components/TaskForm.jsx b/src/Components/TaskForm.jsx index 80d26a0..17008ad 100644 --- a/src/Components/TaskForm.jsx +++ b/src/Components/TaskForm.jsx @@ -1,9 +1,10 @@ export default function TaskForm({ onSubmit, input, setInput,dueDate, setDueDate }) { return ( - + setInput(e.target.value)} @@ -11,12 +12,13 @@ export default function TaskForm({ onSubmit, input, setInput,dueDate, setDueDate /> setDueDate(e.target.value)} - aria-label="Due date" - className="border rounded px-3 py-2" + value={dueDate} + onChange={(e) => setDueDate(e.target.value)} + aria-label="Due date" + className="border rounded px-3 py-2 w-full sm:w-48" /> - diff --git a/src/Components/TaskItem.jsx b/src/Components/TaskItem.jsx index 205f6ae..641cfe5 100644 --- a/src/Components/TaskItem.jsx +++ b/src/Components/TaskItem.jsx @@ -1,36 +1,38 @@ -import { format } from 'date-fns'; - +import { format } from "date-fns"; export default function TaskItem({ task, onToggle, onRemove }) { return ( - -
  • +
  • onToggle(task.id)} - onKeyDown={(e) => e.key === 'Enter' && onToggle(task.id)} + onKeyDown={(e) => e.key === "Enter" && onToggle(task.id)} > - {task.text}{task.dueDate && ( - - {format(new Date(task.dueDate), 'dd/MM/yyyy')} - +
    {task.text}
    + {task.dueDate && ( +
    + Due: {format(new Date(task.dueDate), "dd/MM/yyyy")} +
    + )} + {task.createdAt && ( +
    + Added: {format(new Date(task.createdAt), "dd MMM yyyy, p")} +
    )} -{task.createdAt && ( - - Added: {format(new Date(task.createdAt), 'dd MMM yyyy, p')} - -)} -
  • ); -} \ No newline at end of file +} diff --git a/src/Components/TaskList.jsx b/src/Components/TaskList.jsx index 13cd6fa..7b2f499 100644 --- a/src/Components/TaskList.jsx +++ b/src/Components/TaskList.jsx @@ -1,9 +1,16 @@ import TaskItem from "./TaskItem"; -export default function TaskList({ tasks, onToggle, onRemove, onCompleteAll , setDarkMode, darkMode}) { +export default function TaskList({ + tasks, + onToggle, + onRemove, + onCompleteAll, + setDarkMode, + darkMode, +}) { if (tasks.length === 0) { return ( -
    +
    No tasks yet. Start by adding one!
    ); @@ -12,22 +19,25 @@ export default function TaskList({ tasks, onToggle, onRemove, onCompleteAll , se const allCompleted = tasks.every((task) => task.completed); return ( - -
    - This will change color in dark mode! - -
    - +
    + {/* Dark mode toggle button */} +
    +
    - -
    + {/* Complete All button */} +
    + {/* Task List */}
      {tasks.map((task) => ( !t.completed).length; return ( -
      - Total: {total} | Uncompleted: {uncompleted} +
      + Total: {total} + | + Uncompleted: {uncompleted}
      ); } \ No newline at end of file From 176e64fceb8b797b74bc2ebe7c492d6658ba663a Mon Sep 17 00:00:00 2001 From: "DANUSHKA\\danus" Date: Fri, 13 Jun 2025 12:57:14 +0200 Subject: [PATCH 11/15] overdue styles added --- src/Components/TaskForm.jsx | 1 + src/Components/TaskItem.jsx | 34 ++++++++++++++++++++-------------- src/data/useTaskData.js | 12 ++++++++++-- tailwind.config.js | 2 +- 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/Components/TaskForm.jsx b/src/Components/TaskForm.jsx index 17008ad..ad74561 100644 --- a/src/Components/TaskForm.jsx +++ b/src/Components/TaskForm.jsx @@ -14,6 +14,7 @@ export default function TaskForm({ onSubmit, input, setInput,dueDate, setDueDate type="date" value={dueDate} onChange={(e) => setDueDate(e.target.value)} + placeholder="Due date" aria-label="Due date" className="border rounded px-3 py-2 w-full sm:w-48" /> diff --git a/src/Components/TaskItem.jsx b/src/Components/TaskItem.jsx index 641cfe5..aa0cbb3 100644 --- a/src/Components/TaskItem.jsx +++ b/src/Components/TaskItem.jsx @@ -1,34 +1,40 @@ -import { format } from "date-fns"; +import { format, isBefore, startOfDay } from 'date-fns'; export default function TaskItem({ task, onToggle, onRemove }) { - return ( -
    • + const today = startOfDay(new Date()); + const isOverdue = task.dueDate && isBefore(new Date(task.dueDate), today) && !task.completed; + + return ( +
    • onToggle(task.id)} - onKeyDown={(e) => e.key === "Enter" && onToggle(task.id)} + onKeyDown={(e) => e.key === 'Enter' && onToggle(task.id)} > -
      {task.text}
      + {task.text} {task.dueDate && ( -
      - Due: {format(new Date(task.dueDate), "dd/MM/yyyy")} -
      + + (Due: {format(new Date(task.dueDate), 'dd/MM/yyyy')}) + )} {task.createdAt && ( -
      - Added: {format(new Date(task.createdAt), "dd MMM yyyy, p")} -
      + + Added: {format(new Date(task.createdAt), 'dd MMM yyyy, p')} + )}
      + -

      Task Manager

      - - - - -
    • -
    +
    + +

    + Task Manager +

    + + +{/* Filter controls */} +
    + {/* Status filter */} + + + {/* Created after date filter */} + setFilterDate(e.target.value)} + className="border rounded px-3 py-1 dark:bg-gray-700 dark:text-white" + aria-label="Show tasks created after date" + /> + + {/* Clear filters button */} + +
    + + + + + +
    + ); } From b31801710eecaab67913582a2575506b984c66ac Mon Sep 17 00:00:00 2001 From: "DANUSHKA\\danus" Date: Fri, 13 Jun 2025 13:11:39 +0200 Subject: [PATCH 13/15] category type added --- src/App.jsx | 16 +++++++++++----- src/Components/TaskForm.jsx | 15 ++++++++++++++- src/Components/TaskItem.jsx | 30 +++++++++++++++++++----------- src/data/useTaskData.js | 3 ++- 4 files changed, 46 insertions(+), 18 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index abea96a..11e3cd9 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -15,6 +15,9 @@ export default function App() { const [filterStatus, setFilterStatus] = useState("all"); // all, completed, uncompleted const [filterDate, setFilterDate] = useState(""); // yyyy-MM-dd + // new category state + const [category, setCategory] = useState("General"); + useEffect(() => { const root = window.document.documentElement; if (darkMode) { @@ -43,8 +46,10 @@ export default function App() { const handleSubmit = (e) => { e.preventDefault(); if (!input.trim()) return; - addTask(input.trim(), dueDate); // pass dueDate to addTask + addTask(input.trim(), dueDate, category); // pass dueDate to addTask setInput(""); + setCategory("General"); // reset to default after adding + setDueDate(""); }; return ( @@ -65,9 +70,11 @@ export default function App() { setInput={setInput} dueDate={dueDate} setDueDate={setDueDate} + category={category} + setCategory={setCategory} /> -{/* Filter controls */} + {/* Filter controls */}
    {/* Status filter */} setCategory(e.target.value)} + className="border rounded px-3 py-2" + aria-label="Task category" + > + + + + + + + +
    + + {/* Select project */} +
    + + +
    +
    setDueDate(e.target.value)} - placeholder="Due date" + placeholder="Due date" aria-label="Due date" className="border rounded px-3 py-2 w-full sm:w-48" - /> + /> - -
    ); -} \ No newline at end of file +} diff --git a/src/data/useTaskData.js b/src/data/useTaskData.js index ec5d9a7..42caebe 100644 --- a/src/data/useTaskData.js +++ b/src/data/useTaskData.js @@ -3,39 +3,96 @@ import { persist } from "zustand/middleware"; const useTaskStore = create( persist( - (set) => ({ - tasks: [], - addTask: (text, dueDate, category = "General") => - set((state) => ({ - tasks: [ - ...state.tasks, - { - id: Date.now(), - text, - dueDate, - category, - completed: false, - createdAt: new Date().toISOString(), - }, - ], - })), - removeTask: (id) => - set((state) => ({ - tasks: state.tasks.filter((task) => task.id !== id), - })), - toggleTask: (id) => - set((state) => ({ - tasks: state.tasks.map((task) => - task.id === id ? { ...task, completed: !task.completed } : task - ), - })), - completeAllTasks: () => - set((state) => ({ - tasks: state.tasks.map((task) => ({ ...task, completed: true })), - })), -}), - { - name: "task-storage", // key in localStorage + (set, get) => ({ + tasks: [], + projects: [], + + // Add a new task with optional category and projectId + addTask: (text, dueDate, category = "General", projectId = null) => + set((state) => ({ + tasks: [ + ...state.tasks, + { + id: Date.now(), + text, + dueDate, + category, + projectId, + completed: false, + createdAt: new Date().toISOString(), + }, + ], + })), + + // Remove task + removeTask: (id) => + set((state) => ({ + tasks: state.tasks.filter((task) => task.id !== id), + })), + + // Toggle task complete + toggleTask: (id) => + set((state) => { + const updatedTasks = state.tasks.map((task) => + task.id === id ? { ...task, completed: !task.completed } : task + ); + + // Optionally mark project as complete if all its tasks are complete + const affectedTask = state.tasks.find((t) => t.id === id); + const projectId = affectedTask?.projectId; + let updatedProjects = state.projects; + + if (projectId) { + const tasksInProject = updatedTasks.filter( + (t) => t.projectId === projectId + ); + const allDone = tasksInProject.every((t) => t.completed); + + updatedProjects = state.projects.map((p) => + p.id === projectId ? { ...p, completed: allDone } : p + ); + } + + return { + tasks: updatedTasks, + projects: updatedProjects, + }; + }), + + // Complete all tasks + completeAllTasks: () => + set((state) => { + const updatedTasks = state.tasks.map((task) => ({ + ...task, + completed: true, + })); + + // Update project completion status + const updatedProjects = state.projects.map((project) => { + const projectTasks = updatedTasks.filter( + (task) => task.projectId === project.id + ); + const allCompleted = projectTasks.every((t) => t.completed); + return { ...project, completed: allCompleted }; + }); + + return { + tasks: updatedTasks, + projects: updatedProjects, + }; + }), + + // Add a new project + addProject: (name) => + set((state) => ({ + projects: [ + ...state.projects, + { id: Date.now(), name, completed: false }, + ], + })), + }), + { + name: "task-storage", // persisted key in localStorage } ) ); From d7d0c8bdaed6c007393fee1dcac20315ad528e49 Mon Sep 17 00:00:00 2001 From: "DANUSHKA\\danus" Date: Mon, 16 Jun 2025 08:22:23 +0200 Subject: [PATCH 15/15] tasklist modified --- src/Components/TaskList.jsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Components/TaskList.jsx b/src/Components/TaskList.jsx index 7b2f499..ee63164 100644 --- a/src/Components/TaskList.jsx +++ b/src/Components/TaskList.jsx @@ -7,6 +7,7 @@ export default function TaskList({ onCompleteAll, setDarkMode, darkMode, + projects, }) { if (tasks.length === 0) { return ( @@ -18,6 +19,14 @@ export default function TaskList({ const allCompleted = tasks.every((task) => task.completed); + // Group tasks by projectId + const groupedTasks = tasks.reduce((groups, task) => { + const key = task.projectId || "none"; + if (!groups[key]) groups[key] = []; + groups[key].push(task); + return groups; + }, {}); + return (
    {/* Dark mode toggle button */}