From c637a2b6eb2b6dfec6263ad1eecb1a9abdfb2044 Mon Sep 17 00:00:00 2001 From: VariaSlu Date: Wed, 21 May 2025 21:43:18 +0200 Subject: [PATCH 1/7] structure --- src/Store/useTodoStore.js | 0 src/components/EmptyState.jsx | 0 src/components/Header.jsx | 0 src/components/TodoForm.jsx | 0 src/components/TodoItem.jsx | 0 src/components/TodoList.jsx | 0 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/Store/useTodoStore.js create mode 100644 src/components/EmptyState.jsx create mode 100644 src/components/Header.jsx create mode 100644 src/components/TodoForm.jsx create mode 100644 src/components/TodoItem.jsx create mode 100644 src/components/TodoList.jsx diff --git a/src/Store/useTodoStore.js b/src/Store/useTodoStore.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/EmptyState.jsx b/src/components/EmptyState.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/Header.jsx b/src/components/Header.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/TodoForm.jsx b/src/components/TodoForm.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/TodoItem.jsx b/src/components/TodoItem.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/TodoList.jsx b/src/components/TodoList.jsx new file mode 100644 index 0000000..e69de29 From 754cc29ade8f61c81c9e2259f9ecd272dd315f68 Mon Sep 17 00:00:00 2001 From: VariaSlu Date: Wed, 21 May 2025 21:58:08 +0200 Subject: [PATCH 2/7] store --- src/Store/useTodoStore.js | 20 ++++++++++++++++++++ src/components/Header.jsx | 3 +++ 2 files changed, 23 insertions(+) diff --git a/src/Store/useTodoStore.js b/src/Store/useTodoStore.js index e69de29..8634b88 100644 --- a/src/Store/useTodoStore.js +++ b/src/Store/useTodoStore.js @@ -0,0 +1,20 @@ +import { create } from 'zustand'; +import { nanoid } from 'nanoid'; + +export const useTodoStore = create((set) => ({ + tasks: [], + addTask: (title) => + set((state) => ({ + tasks: [...state.tasks, { id: nanoid(), title, 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 + ), + })), +})); diff --git a/src/components/Header.jsx b/src/components/Header.jsx index e69de29..5d704c1 100644 --- a/src/components/Header.jsx +++ b/src/components/Header.jsx @@ -0,0 +1,3 @@ +const { tasks } = useTodoStore(); +const total = tasks.length; +const incomplete = tasks.filter(task => !task.completed).length; From 0560a09c385db9f4b005b69b60e9e857e9447841 Mon Sep 17 00:00:00 2001 From: VariaSlu Date: Thu, 22 May 2025 22:11:01 +0200 Subject: [PATCH 3/7] add initial tasks logic to store --- package.json | 3 +- src/App.jsx | 14 +++++++-- .../{useTodoStore.js => useTodoStore.jsx} | 8 +++-- src/components/EmptyState.jsx | 8 +++++ src/components/Header.jsx | 16 ++++++++-- src/components/TodoForm.jsx | 29 +++++++++++++++++++ src/components/TodoItem.jsx | 29 +++++++++++++++++++ src/components/TodoList.jsx | 18 ++++++++++++ 8 files changed, 117 insertions(+), 8 deletions(-) rename src/Store/{useTodoStore.js => useTodoStore.jsx} (69%) diff --git a/package.json b/package.json index caf6289..1f583a3 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ }, "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", diff --git a/src/App.jsx b/src/App.jsx index 5427540..531ca09 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,5 +1,15 @@ + +import { Header } from './components/Header'; +import { TodoForm } from './components/TodoForm'; +import { TodoList } from './components/TodoList'; + export const App = () => { return ( -

React Boilerplate

+ +
+
+ + +
) -} +} \ No newline at end of file diff --git a/src/Store/useTodoStore.js b/src/Store/useTodoStore.jsx similarity index 69% rename from src/Store/useTodoStore.js rename to src/Store/useTodoStore.jsx index 8634b88..479b2da 100644 --- a/src/Store/useTodoStore.js +++ b/src/Store/useTodoStore.jsx @@ -2,7 +2,10 @@ import { create } from 'zustand'; import { nanoid } from 'nanoid'; export const useTodoStore = create((set) => ({ - tasks: [], + tasks: [ + { id: nanoid(), title: 'Your first task is to find what feels effortless to you.', completed: false }, + { id: nanoid(), title: 'Your second task is to put maximum effort into it.', completed: false }, + ], addTask: (title) => set((state) => ({ tasks: [...state.tasks, { id: nanoid(), title, completed: false }], @@ -17,4 +20,5 @@ export const useTodoStore = create((set) => ({ task.id === id ? { ...task, completed: !task.completed } : task ), })), -})); +})) + diff --git a/src/components/EmptyState.jsx b/src/components/EmptyState.jsx index e69de29..d2d9e83 100644 --- a/src/components/EmptyState.jsx +++ b/src/components/EmptyState.jsx @@ -0,0 +1,8 @@ +import { React } from 'react'; + + +export const EmptyState = () => ( +
+

No tasks. Add one or two!

+
+) \ No newline at end of file diff --git a/src/components/Header.jsx b/src/components/Header.jsx index 5d704c1..9472e69 100644 --- a/src/components/Header.jsx +++ b/src/components/Header.jsx @@ -1,3 +1,13 @@ -const { tasks } = useTodoStore(); -const total = tasks.length; -const incomplete = tasks.filter(task => !task.completed).length; +import { useTodoStore } from '../Store/useTodoStore'; + +export const Header = () => { + const tasks = useTodoStore((state) => state.tasks); + const incomplete = tasks.filter((t) => !t.completed).length; + + return ( +
+

Now

+

{incomplete} tasks remaining

+
+ ); +} diff --git a/src/components/TodoForm.jsx b/src/components/TodoForm.jsx index e69de29..39bff78 100644 --- a/src/components/TodoForm.jsx +++ b/src/components/TodoForm.jsx @@ -0,0 +1,29 @@ +import React, { useState } from 'react'; +import { useTodoStore } from '../Store/useTodoStore'; + +export const TodoForm = () => { + const [title, setTitle] = useState(''); + const addTask = useTodoStore((state) => state.addTask); + + const handleSubmit = (e) => { + e.preventDefault(); + if (!title.trim()) return; + addTask(title); + setTitle(''); + }; + + return ( +
+ setTitle(e.target.value)} + /> + +
+ ); +} \ No newline at end of file diff --git a/src/components/TodoItem.jsx b/src/components/TodoItem.jsx index e69de29..3aef447 100644 --- a/src/components/TodoItem.jsx +++ b/src/components/TodoItem.jsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { useTodoStore } from '../Store/useTodoStore'; + +export const TodoItem = ({ task }) => { + const toggleTask = useTodoStore((state) => state.toggleTask); + const removeTask = useTodoStore((state) => state.removeTask); + + return ( +
  • +
    toggleTask(task.id)} + role="button" + tabIndex={0} + onKeyDown={(e) => e.key === 'Enter' && toggleTask(task.id)} + aria-label="Toggle task" + > + {task.title} +
    + +
  • + ); +} \ No newline at end of file diff --git a/src/components/TodoList.jsx b/src/components/TodoList.jsx index e69de29..a2d6c38 100644 --- a/src/components/TodoList.jsx +++ b/src/components/TodoList.jsx @@ -0,0 +1,18 @@ +import { React } from 'react'; +import { useTodoStore } from '../Store/useTodoStore'; +import { TodoItem } from './TodoItem'; +import { EmptyState } from './EmptyState'; + +export const TodoList = () => { + const tasks = useTodoStore((state) => state.tasks); + + if (tasks.length === 0) return ; + + return ( +
      + {tasks.map((task) => ( + + ))} +
    + ); +} \ No newline at end of file From 529dbe5bc5a99c3789b07c4bee622bc6f0eda1b1 Mon Sep 17 00:00:00 2001 From: VariaSlu Date: Thu, 22 May 2025 23:47:04 +0200 Subject: [PATCH 4/7] styling --- src/App.jsx | 3 ++- src/components/Header.jsx | 2 +- src/components/TodoForm.jsx | 6 ++--- src/components/TodoItem.jsx | 6 ++--- src/components/TodoList.jsx | 15 ++++++++++-- src/index.css | 48 +++++++++++++++++++++++++++++++++++++ 6 files changed, 70 insertions(+), 10 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 531ca09..a3ffff2 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -2,11 +2,12 @@ import { Header } from './components/Header'; import { TodoForm } from './components/TodoForm'; import { TodoList } from './components/TodoList'; +import './index.css'; export const App = () => { return ( -
    +
    diff --git a/src/components/Header.jsx b/src/components/Header.jsx index 9472e69..c79a5ca 100644 --- a/src/components/Header.jsx +++ b/src/components/Header.jsx @@ -5,7 +5,7 @@ export const Header = () => { const incomplete = tasks.filter((t) => !t.completed).length; return ( -
    +

    Now

    {incomplete} tasks remaining

    diff --git a/src/components/TodoForm.jsx b/src/components/TodoForm.jsx index 39bff78..c342e2a 100644 --- a/src/components/TodoForm.jsx +++ b/src/components/TodoForm.jsx @@ -13,15 +13,15 @@ export const TodoForm = () => { }; return ( -
    + setTitle(e.target.value)} /> -
    diff --git a/src/components/TodoItem.jsx b/src/components/TodoItem.jsx index 3aef447..d162dc2 100644 --- a/src/components/TodoItem.jsx +++ b/src/components/TodoItem.jsx @@ -6,9 +6,9 @@ export const TodoItem = ({ task }) => { const removeTask = useTodoStore((state) => state.removeTask); return ( -
  • +
  • toggleTask(task.id)} role="button" tabIndex={0} @@ -19,7 +19,7 @@ export const TodoItem = ({ task }) => {
    +
  • ))} diff --git a/src/index.css b/src/index.css index 3e3637a..5d8f087 100644 --- a/src/index.css +++ b/src/index.css @@ -48,4 +48,64 @@ ul { font-size: 2rem; } -input[type="checkbox"] {} \ No newline at end of file +.checkbox-wrapper { + display: flex; + align-items: center; + gap: 0.6rem; + cursor: pointer; +} + +.checkbox-wrapper input[type="checkbox"] { + display: none; +} + +.custom-checkbox { + width: 18px; + height: 18px; + border: 2px solid #7a6c5d; + border-radius: 4px; + background-color: transparent; + position: relative; + flex-shrink: 0; +} + +.checkbox-wrapper input[type="checkbox"]:checked+.custom-checkbox::after { + content: ""; + position: absolute; + top: 2px; + left: 5px; + width: 4px; + height: 8px; + border: solid #7a6c5d; + border-width: 0 2px 2px 0; + transform: rotate(45deg); +} + +.task-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.4rem 0.6rem; + border-bottom: 1px solid #eee; + position: relative; +} + +.delete-button { + background: transparent; + border: none; + font-size: 1rem; + cursor: pointer; + opacity: 0; + transition: opacity 0.2s ease; + color: #a84444; +} + +.task-item:hover .delete-button { + opacity: 1; +} + +.task-content { + display: flex; + align-items: center; + gap: 0.5rem; +} \ No newline at end of file From 1fc40422afd8b286b86d44e9b8cec7977409466a Mon Sep 17 00:00:00 2001 From: VariaSlu Date: Sun, 25 May 2025 21:11:07 +0200 Subject: [PATCH 6/7] delete button styling and devideer --- src/components/TodoList.jsx | 7 +++++-- src/index.css | 22 +++++----------------- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/components/TodoList.jsx b/src/components/TodoList.jsx index c501dd5..0ad8ce0 100644 --- a/src/components/TodoList.jsx +++ b/src/components/TodoList.jsx @@ -25,8 +25,11 @@ export const TodoList = () => { {task.title} - - + +
    ))} diff --git a/src/index.css b/src/index.css index 5d8f087..4f91a3a 100644 --- a/src/index.css +++ b/src/index.css @@ -23,7 +23,7 @@ h1 { } p { - color: grey; + color: rgb(96, 95, 95); } @@ -91,21 +91,9 @@ ul { } .delete-button { - background: transparent; - border: none; - font-size: 1rem; - cursor: pointer; - opacity: 0; - transition: opacity 0.2s ease; - color: #a84444; -} - -.task-item:hover .delete-button { - opacity: 1; -} + height: 32px; + border: 2px solid #7a6c5d; + border-radius: 4px; + background-color: transparent; -.task-content { - display: flex; - align-items: center; - gap: 0.5rem; } \ No newline at end of file From f57155c858f819988a8e6e49e655e2b8acfb90d0 Mon Sep 17 00:00:00 2001 From: VariaSlu Date: Sun, 25 May 2025 21:24:50 +0200 Subject: [PATCH 7/7] readmee update --- README.md | 3 ++- src/components/EmptyState.jsx | 2 +- src/components/TodoForm.jsx | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d1c68b5..4172b2c 100644 --- a/README.md +++ b/README.md @@ -1 +1,2 @@ -# Todo \ No newline at end of file +# Todo +https://nowapp.netlify.app/ \ No newline at end of file diff --git a/src/components/EmptyState.jsx b/src/components/EmptyState.jsx index d2d9e83..05c0db8 100644 --- a/src/components/EmptyState.jsx +++ b/src/components/EmptyState.jsx @@ -2,7 +2,7 @@ import { React } from 'react'; export const EmptyState = () => ( -
    +

    No tasks. Add one or two!

    ) \ No newline at end of file diff --git a/src/components/TodoForm.jsx b/src/components/TodoForm.jsx index c342e2a..fc3acfb 100644 --- a/src/components/TodoForm.jsx +++ b/src/components/TodoForm.jsx @@ -13,7 +13,7 @@ export const TodoForm = () => { }; return ( -
    +