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 */}
-
- {/* checkbox 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 (
-
- )
-}
-
-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;
+