diff --git a/README.md b/README.md index a2f13c0b..4e61fad7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ -<<<<<<< HEAD # πŸ–₯️ Mission-FE-Zero100 FE Zero100 λ―Έμ…˜μ„ μœ„ν•œ λ ˆν¬μ§€ν† λ¦¬μž…λ‹ˆλ‹€. diff --git a/package-lock.json b/package-lock.json index b77564a5..ad96620a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "0.0.0", "dependencies": { "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "react-router-dom": "^7.4.1" }, "devDependencies": { "@eslint/js": "^9.21.0", @@ -1339,6 +1340,12 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", @@ -1589,6 +1596,15 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2432,6 +2448,46 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.4.1.tgz", + "integrity": "sha512-Vmizn9ZNzxfh3cumddqv3kLOKvc7AskUT0dC1prTabhiEi0U4A33LmkDOJ79tXaeSqCqMBXBU/ySX88W85+EUg==", + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.6.0", + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0", + "turbo-stream": "2.4.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.4.1.tgz", + "integrity": "sha512-L3/4tig0Lvs6m6THK0HRV4eHUdpx0dlJasgCxXKnavwhh4tKYgpuZk75HRYNoRKDyDWi9QgzGXsQ1oQSBlWpAA==", + "license": "MIT", + "dependencies": { + "react-router": "7.4.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -2505,6 +2561,12 @@ "semver": "bin/semver.js" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -2564,6 +2626,12 @@ "node": ">=8" } }, + "node_modules/turbo-stream": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz", + "integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==", + "license": "ISC" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index 4fffc473..c050a39c 100644 --- a/package.json +++ b/package.json @@ -8,13 +8,11 @@ "build": "vite build", "lint": "eslint .", "preview": "vite preview" - - - }, "dependencies": { "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "react-router-dom": "^7.4.1" }, "devDependencies": { "@eslint/js": "^9.21.0", diff --git a/src/App.jsx b/src/App.jsx index 9b836f3c..4af9a464 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,23 +1,44 @@ -import "./index.css"; -import Text from "./component/Text"; -import Input from "./component/Input"; -import Button from "./component/Button"; -import Checkbox from "./component/Checkbox"; +import styled from "styled-components"; +import Header from "./component/Header"; +import AddTodo from "./component/AddTodo"; +import Category from "./component/Category"; +import TodoList from "./component/TodoList"; +import { useState } from "react"; + +const Container = styled.div` + max-width: 800px; + margin: 0 auto; + padding: 20px; + background-color: #f9f9f9; + + @media (max-width: 768px) and (orientation: portrait) { + max-width: 90%; + padding: 1.5rem; + } + + @media (max-width: 480px) and (orientation: portrait) { + padding: 1rem; + } +`; function App() { - return ( -
- {/* text part */} - + const [todos, setTodos] = useState([]); + const [activeCategory, setActiveCategory] = useState("All"); + + const handleAddTodo = (text) => { + if (text.trim() === "") return; + const newTodo = { id: Date.now(), text, completed: false }; + setTodos([...todos, newTodo]); + }; - {/* input part */} - - {/* button part */} -
+ return ( + +
+ + + + ); -} +}; export default App; diff --git a/src/Blocks.jsx b/src/Blocks.jsx deleted file mode 100644 index 9095b707..00000000 --- a/src/Blocks.jsx +++ /dev/null @@ -1,58 +0,0 @@ -import styled from "styled-components"; - -const Wrapper = styled.div` -padding: 1rem; -display: flex; -flex-direction: row; -align-items: flex-start; -justify-content: flex-start; -background-color: lightgrey; -`; - -const Block = styled.div` -padding: ${(props)=> props.padding}; -border: 1px solid black; -border-radius: 1rem; -background-color: ${(props) => props.backgroundColor}; -color: white; -font-size: 2rem; -font-weight: bold; -text-align: center; -`; - -const blockItems= [ - { - label: "1", - padding: "1rem", - backgroundColor: "red", - }, - { - label: "2", - padding: "3rem", - backgroundColor: "green", - }, - { - label:"3", - padding: "2rem", - backgroundColor: "blue", - }, -]; - -function Blocks(props){ - return( - - {blockItems.map((blockItem) => { - return ( - - {blockItem.label} - - ); - })} - - ); -} - -export default Blocks; \ No newline at end of file diff --git a/src/component/AddTodo.jsx b/src/component/AddTodo.jsx new file mode 100644 index 00000000..e855bb5a --- /dev/null +++ b/src/component/AddTodo.jsx @@ -0,0 +1,59 @@ +import React, { useState } from 'react'; +import styled from "styled-components"; + +const AddTodoContainer = styled.div` + display: flex; + flex-direction: column; + align-items: center; + text-align: center; +`; + +const StyledText = styled.h2` + text-align: center; + display: flex; +`; + +const StyledInput = styled.input` + width: 500px; + padding: 14px; + border-radius: 0px; + margin-bottom: 8px; +`; + +const StyledButton = styled.button` + background-color: black; + color: white; + width: 532px; + padding: 9px; + border: none; + border-radius: 0px; + margin-bottom: 8px; +`; + +const AddTodo = ({ onAddTodo }) => { + const [inputValue, setInputValue] = useState(""); + + const handleInputChange = (e) => { + setInputValue(e.target.value); + }; + + const handleAddClick = () => { + onAddTodo(inputValue); + setInputValue(""); + }; + + return( + + What needs to be done? + + Add + + ) +} + +export default AddTodo; \ No newline at end of file diff --git a/src/component/Button.jsx b/src/component/Button.jsx index 551f49c4..e69de29b 100644 --- a/src/component/Button.jsx +++ b/src/component/Button.jsx @@ -1,13 +0,0 @@ -import React from 'react' - -const Button = ({label, onClick}) => { - return ( -
- - - -
- ) -} - -export default Button; diff --git a/src/component/Category.jsx b/src/component/Category.jsx new file mode 100644 index 00000000..291e5d6b --- /dev/null +++ b/src/component/Category.jsx @@ -0,0 +1,35 @@ +import React, { useState } from 'react'; +import styled from "styled-components"; + +const CategoryContainer = styled.div` + display: flex; + justify-content: center; + gap: 8px; +`; + +const StyledButton = styled.button` + width: 130px; + padding: 7px; + background-color: white; + border: 2px solid ${(props) => (props.active ? "black" : "#ccc")}; + border-radius: 0px; + font-size: 14px; +`; + +const Category = ({ activeCategory, setActiveCategory }) => { + return ( + + {["All", "Active", "Completed"].map((label) => ( + setActiveCategory(label)} + > + {label} + + ))} + + ); +}; + +export default Category; \ No newline at end of file diff --git a/src/component/Checkbox.jsx b/src/component/Checkbox.jsx index e674f743..e69de29b 100644 --- a/src/component/Checkbox.jsx +++ b/src/component/Checkbox.jsx @@ -1,37 +0,0 @@ -import React from 'react' - -const Checkbox = () => { - return ( -
-

3 tasks remaining

-
    -
  • - - Eat -
    - - -
    -
  • -
  • - - Sleep -
    - - -
    -
  • -
  • - - Repeat -
    - - -
    -
  • -
-
- ) -} - -export default Checkbox diff --git a/src/component/Header.jsx b/src/component/Header.jsx new file mode 100644 index 00000000..46bf9d3c --- /dev/null +++ b/src/component/Header.jsx @@ -0,0 +1,21 @@ +import React from 'react'; +import styled from "styled-components"; + +const HeaderContainer = styled.text` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; +`; + +const Header = () => { + return ( + +

TodoMatic

+
+ ); + }; + + export default Header + \ No newline at end of file diff --git a/src/component/Input.jsx b/src/component/Input.jsx index 0592b378..e69de29b 100644 --- a/src/component/Input.jsx +++ b/src/component/Input.jsx @@ -1,12 +0,0 @@ -import React from 'react' - -const Input = () => { - return ( -
- - -
- ) -} - -export default Input diff --git a/src/component/Text.jsx b/src/component/Text.jsx index 6fe9ec78..e69de29b 100644 --- a/src/component/Text.jsx +++ b/src/component/Text.jsx @@ -1,12 +0,0 @@ -import React from 'react' - -const Text = () => { - return ( -
-

TodoMatic

-

What needs to be done?

-
- ) -} - -export default Text diff --git a/src/component/TodoList.jsx b/src/component/TodoList.jsx new file mode 100644 index 00000000..3d2a20d3 --- /dev/null +++ b/src/component/TodoList.jsx @@ -0,0 +1,121 @@ +import styled from "styled-components"; +import React, { useState } from 'react'; + +const TodoListContainer = styled.div` + display: flex; + flex-direction: column; + align-items: center; + text-align: center; +`; + +const StyledText = styled.h2` + align-self: flex-start; + margin-left: 200px; +`; + +const StyledCheckbox = styled.input.attrs({ type: 'checkbox' })` + appearance: none; + width: 40px; + height: 40px; + margin-right: 10px; + border: 2px solid black; + background-color: white; + position: relative; + margin-right: 10px; + + &:checked::after { + content: ''; + position: absolute; + top: 5px; + left: 12px; + width: 8px; + height: 16px; + border: solid black; + border-width: 0 4px 4px 0; + transform: rotate(45deg); + }; +`; + +const StyledButton = styled.button` + padding: 7px 80px; + font-size: 14px; + border: none; + border-radius: 0px; + margin: 5px; +`; + +const EditButton = styled(StyledButton)` + background-color: white; + border: 1px solid black; +`; + +const DeleteButton = styled(StyledButton)` + background-color: #d9534f; + color: white; +`; + +const TaskItem = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + margin-bottom: 20px; +`; + +const TaskTop = styled.div` + display: flex; + align-items: center; +`; + +const TaskActions = styled.div` + display: flex; + margin-top: 5px; +`; + + +const TodoList = ({ todos, setTodos, activeCategory }) => { + const handleDelete = (id) => { + setTodos(todos.filter(todo => todo.id !== id)); + }; + + const handleToggleComplete = (id) => { + setTodos( + todos.map(todo => + todo.id === id ? { ...todo, completed: !todo.completed } : todo + ) + ); + }; + + const filteredTodos = todos.filter(todo => { + if (activeCategory === "Active") return !todo.completed; + if (activeCategory === "Completed") return todo.completed; + return true; + }); + + return ( + + {todos.filter(todo => !todo.completed).length} tasks remaining +
+ {filteredTodos.map(todo => ( + + + handleToggleComplete(todo.id)} + /> + + {todo.text} + + + + Edit + handleDelete(todo.id)}>Delete + + + ))} +
+
+ ); +}; + +export default TodoList; +