From d015dadac95fa6afbb35670354468d50c0c93891 Mon Sep 17 00:00:00 2001 From: jinnyleeis <101055312+jinnyleeis@users.noreply.github.com> Date: Wed, 20 Mar 2024 23:08:04 +0900 Subject: [PATCH 01/15] npm install --- public/index.html | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/public/index.html b/public/index.html index aa069f2..f881397 100644 --- a/public/index.html +++ b/public/index.html @@ -1,21 +1,21 @@ - - - - - - - - - - - React App - - - -
- - + From b8bb3ab19265ff9ac580a1c5ab64ef64723107d4 Mon Sep 17 00:00:00 2001 From: jinnyleeis <101055312+jinnyleeis@users.noreply.github.com> Date: Wed, 20 Mar 2024 23:44:49 +0900 Subject: [PATCH 02/15] =?UTF-8?q?=20=EC=84=B9=EC=85=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80,=20=EC=95=84=EC=9D=B4=ED=85=9C=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 124 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + public/index.html | 10 ---- src/App.js | 56 +++++++++++++++++++-- src/Section.js | 59 ++++++++++++++++++++++ src/TodoItem.js | 20 ++++++++ src/index.js | 14 +++--- 7 files changed, 264 insertions(+), 20 deletions(-) create mode 100644 src/Section.js create mode 100644 src/TodoItem.js diff --git a/package-lock.json b/package-lock.json index e523af2..115138b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1", + "styled-components": "^6.1.8", "web-vitals": "^2.1.4" } }, @@ -2283,6 +2284,24 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", + "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", + "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -4534,6 +4553,11 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==" }, + "node_modules/@types/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-n4sx2bqL0mW1tvDf/loQ+aMX7GQD3lc3fkCMC55VFNDu/vBOabO+LTIeXKM14xK0ppk5TUGcWRjiSpIlUpghKw==" + }, "node_modules/@types/testing-library__jest-dom": { "version": "5.14.9", "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz", @@ -6037,6 +6061,14 @@ "node": ">= 6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -6469,6 +6501,14 @@ "postcss": "^8.4" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "engines": { + "node": ">=4" + } + }, "node_modules/css-declaration-sorter": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", @@ -6659,6 +6699,16 @@ "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/css-tree": { "version": "1.0.0-alpha.37", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", @@ -15998,6 +16048,11 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -16530,6 +16585,70 @@ "webpack": "^5.0.0" } }, + "node_modules/styled-components": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.8.tgz", + "integrity": "sha512-PQ6Dn+QxlWyEGCKDS71NGsXoVLKfE1c3vApkvDYS5KAK+V8fNWGhbSUEo9Gg2iaID2tjLXegEW3bZDUGpofRWw==", + "dependencies": { + "@emotion/is-prop-valid": "1.2.1", + "@emotion/unitless": "0.8.0", + "@types/stylis": "4.2.0", + "css-to-react-native": "3.2.0", + "csstype": "3.1.2", + "postcss": "8.4.31", + "shallowequal": "1.1.0", + "stylis": "4.3.1", + "tslib": "2.5.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" + } + }, + "node_modules/styled-components/node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + }, + "node_modules/styled-components/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/styled-components/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, "node_modules/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -16545,6 +16664,11 @@ "postcss": "^8.2.15" } }, + "node_modules/stylis": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.1.tgz", + "integrity": "sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ==" + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", diff --git a/package.json b/package.json index 6c9c71f..eefbbf2 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1", + "styled-components": "^6.1.8", "web-vitals": "^2.1.4" }, "scripts": { diff --git a/public/index.html b/public/index.html index f881397..16566ec 100644 --- a/public/index.html +++ b/public/index.html @@ -29,15 +29,5 @@
- diff --git a/src/App.js b/src/App.js index 3b90819..bb4af63 100644 --- a/src/App.js +++ b/src/App.js @@ -1,8 +1,58 @@ +import React, { useState } from 'react'; +import styled from 'styled-components'; +import Section from './Section'; + +const AppContainer = styled.div` + display: flex; + flex-direction: column; + align-items: center; + width: 80%; + margin: 30px auto; +`; + +const Input = styled.input` + margin: 10px; + padding: 10px; + border-radius: 5px; + border: 1px solid #ccc; +`; + +const Button = styled.button` + padding: 10px 20px; + margin: 5px; + border-radius: 5px; + background-color: #007bff; + color: white; + border: none; + cursor: pointer; + + &:hover { + background-color: #0056b3; + } +`; + function App() { + const [sections, setSections] = useState([]); + const [sectionName, setSectionName] = useState(''); + + const addSection = () => { + setSections([...sections, { id: sections.length + 1, name: sectionName, todos: [] }]); + setSectionName(''); + }; + return ( -
-

CEOS 19기 프론트엔드 파이팅!( ¨̮ )و✧🔥

-
+ + setSectionName(e.target.value)} + /> + + {sections.map((section) => ( +
+ ))} + ); } diff --git a/src/Section.js b/src/Section.js new file mode 100644 index 0000000..00c5ccd --- /dev/null +++ b/src/Section.js @@ -0,0 +1,59 @@ +import React, { useState } from 'react'; +import styled from 'styled-components'; +import TodoItem from './TodoItem'; + +const SectionContainer = styled.div` + margin: 20px; + padding: 10px; + border: 1px solid #ccc; + border-radius: 5px; +`; + +const Input = styled.input` + margin: 10px; + padding: 10px; + border-radius: 5px; + border: 1px solid #ccc; +`; + +const Button = styled.button` + padding: 10px 20px; + margin: 5px; + border-radius: 5px; + background-color: #007bff; + color: white; + border: none; + cursor: pointer; + + &:hover { + background-color: #0056b3; + } +`; + +function Section({ section }) { + const [todos, setTodos] = useState(section.todos); + const [todoText, setTodoText] = useState(''); + + const addTodo = () => { + setTodos([...todos, { id: todos.length + 1, text: todoText }]); + setTodoText(''); + }; + + return ( + +

{section.name}

+ setTodoText(e.target.value)} + /> + + {todos.map((todo) => ( + + ))} +
+ ); +} + +export default Section; diff --git a/src/TodoItem.js b/src/TodoItem.js new file mode 100644 index 0000000..e9151eb --- /dev/null +++ b/src/TodoItem.js @@ -0,0 +1,20 @@ +import React from 'react'; +import styled from 'styled-components'; + +const ItemContainer = styled.div` + display: flex; + justify-content: space-between; + margin: 10px; + padding: 10px; + border-bottom: 1px solid #eee; +`; + +function TodoItem({ todo }) { + return ( + + {todo.text} + + ); +} + +export default TodoItem; diff --git a/src/index.js b/src/index.js index 8db5acb..b53893d 100644 --- a/src/index.js +++ b/src/index.js @@ -1,10 +1,10 @@ -import React from "react"; -import ReactDOM from "react-dom/client"; -import App from "./App"; +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; -const root = ReactDOM.createRoot(document.getElementById("root")); +const root = ReactDOM.createRoot(document.getElementById('root')); root.render( - - - + + + ); From 6a2d340f2f5fc51c39c76072533a5e6b6ef6f83d Mon Sep 17 00:00:00 2001 From: jinnyleeis <101055312+jinnyleeis@users.noreply.github.com> Date: Thu, 21 Mar 2024 00:03:04 +0900 Subject: [PATCH 03/15] =?UTF-8?q?=20=EC=95=84=EC=9D=B4=ED=85=9C=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 87 +++++++++++++++++++++++++------------------------ src/Section.js | 58 ++++++++++++++++++--------------- src/TodoItem.js | 16 ++++++++- 3 files changed, 92 insertions(+), 69 deletions(-) diff --git a/src/App.js b/src/App.js index bb4af63..34383e2 100644 --- a/src/App.js +++ b/src/App.js @@ -3,57 +3,60 @@ import styled from 'styled-components'; import Section from './Section'; const AppContainer = styled.div` - display: flex; - flex-direction: column; - align-items: center; - width: 80%; - margin: 30px auto; + display: flex; + flex-direction: column; + align-items: center; + width: 80%; + margin: 30px auto; `; const Input = styled.input` - margin: 10px; - padding: 10px; - border-radius: 5px; - border: 1px solid #ccc; + margin: 10px; + padding: 10px; + border-radius: 5px; + border: 1px solid #ccc; `; const Button = styled.button` - padding: 10px 20px; - margin: 5px; - border-radius: 5px; - background-color: #007bff; - color: white; - border: none; - cursor: pointer; - - &:hover { - background-color: #0056b3; - } + padding: 10px 20px; + margin: 5px; + border-radius: 5px; + background-color: #007bff; + color: white; + border: none; + cursor: pointer; + + &:hover { + background-color: #0056b3; + } `; function App() { - const [sections, setSections] = useState([]); - const [sectionName, setSectionName] = useState(''); - - const addSection = () => { - setSections([...sections, { id: sections.length + 1, name: sectionName, todos: [] }]); - setSectionName(''); - }; - - return ( - - setSectionName(e.target.value)} - /> - - {sections.map((section) => ( -
- ))} - - ); + const [sections, setSections] = useState([]); + const [sectionName, setSectionName] = useState(''); + + const addSection = () => { + setSections([ + ...sections, + { id: sections.length + 1, name: sectionName, todos: [] }, + ]); + setSectionName(''); + }; + + return ( + + setSectionName(e.target.value)} + /> + + {sections.map((section) => ( +
+ ))} + + ); } export default App; diff --git a/src/Section.js b/src/Section.js index 00c5ccd..db8562f 100644 --- a/src/Section.js +++ b/src/Section.js @@ -31,29 +31,35 @@ const Button = styled.button` `; function Section({ section }) { - const [todos, setTodos] = useState(section.todos); - const [todoText, setTodoText] = useState(''); - - const addTodo = () => { - setTodos([...todos, { id: todos.length + 1, text: todoText }]); - setTodoText(''); - }; - - return ( - -

{section.name}

- setTodoText(e.target.value)} - /> - - {todos.map((todo) => ( - - ))} -
- ); -} - -export default Section; + const [todos, setTodos] = useState(section.todos); + const [inputText, setInputText] = useState(''); + + const addTodo = () => { + if (!inputText) return; + const newTodo = { id: Date.now(), text: inputText }; + setTodos([...todos, newTodo]); + setInputText(''); + }; + + const deleteTodo = (todoId) => { + setTodos(todos.filter(todo => todo.id !== todoId)); + }; + + return ( + +

{section.name}

+ setInputText(e.target.value)} + /> + + {todos.map(todo => ( + + ))} +
+ ); + } + + export default Section; \ No newline at end of file diff --git a/src/TodoItem.js b/src/TodoItem.js index e9151eb..a1f702b 100644 --- a/src/TodoItem.js +++ b/src/TodoItem.js @@ -9,10 +9,24 @@ const ItemContainer = styled.div` border-bottom: 1px solid #eee; `; -function TodoItem({ todo }) { +const Button = styled.button` + padding: 5px 10px; + background-color: #007bff; + color: white; + border: none; + border-radius: 5px; + cursor: pointer; + + &:hover { + background-color: #0056b3; + } +`; + +function TodoItem({ todo, onDelete }) { return ( {todo.text} + ); } From edd4ba8e40c0a9143713890170ad3dc28fe27d53 Mon Sep 17 00:00:00 2001 From: jinnyleeis <101055312+jinnyleeis@users.noreply.github.com> Date: Thu, 21 Mar 2024 00:32:09 +0900 Subject: [PATCH 04/15] =?UTF-8?q?=20=EC=84=B9=EC=85=98=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 122 +++++++++++++++++++++++++++++++------------------ src/Section.js | 79 +++++++++++++++++++------------- 2 files changed, 124 insertions(+), 77 deletions(-) diff --git a/src/App.js b/src/App.js index 34383e2..021e583 100644 --- a/src/App.js +++ b/src/App.js @@ -3,60 +3,92 @@ import styled from 'styled-components'; import Section from './Section'; const AppContainer = styled.div` - display: flex; - flex-direction: column; - align-items: center; - width: 80%; - margin: 30px auto; + display: flex; + flex-direction: column; + align-items: center; + width: 80%; + margin: 30px auto; `; const Input = styled.input` - margin: 10px; - padding: 10px; - border-radius: 5px; - border: 1px solid #ccc; + margin: 10px; + padding: 10px; + border-radius: 5px; + border: 1px solid #ccc; `; const Button = styled.button` - padding: 10px 20px; - margin: 5px; - border-radius: 5px; - background-color: #007bff; - color: white; - border: none; - cursor: pointer; - - &:hover { - background-color: #0056b3; - } + padding: 10px 20px; + margin: 5px; + border-radius: 5px; + background-color: #007bff; + color: white; + border: none; + cursor: pointer; + + &:hover { + background-color: #0056b3; + } `; function App() { - const [sections, setSections] = useState([]); - const [sectionName, setSectionName] = useState(''); - - const addSection = () => { - setSections([ - ...sections, - { id: sections.length + 1, name: sectionName, todos: [] }, - ]); - setSectionName(''); - }; - - return ( - - setSectionName(e.target.value)} - /> - - {sections.map((section) => ( -
- ))} - - ); + const [sections, setSections] = useState([]); + const [sectionName, setSectionName] = useState(''); + + const addSection = () => { + const newSection = { + id: Date.now(), // 현재 시간을 기준으로 고유한 ID 생성 + name: sectionName, + todos: [] + }; + setSections([...sections, newSection]); + setSectionName(''); // 입력 필드 초기화 + }; + + const deleteSection = (sectionId) => { + setSections(sections.filter(section => section.id !== sectionId)); + }; + + const addTodo = (sectionId, todoText) => { + setSections(sections.map(section => { + if (section.id === sectionId) { + const newTodo = { id: Date.now(), text: todoText }; + return { ...section, todos: [...section.todos, newTodo] }; + } + return section; + })); + }; + + const deleteTodo = (sectionId, todoId) => { + setSections(sections.map(section => { + if (section.id === sectionId) { + const filteredTodos = section.todos.filter(todo => todo.id !== todoId); + return { ...section, todos: filteredTodos }; + } + return section; + })); + }; + + return ( + + setSectionName(e.target.value)} + /> + + {sections.map((section) => ( +
deleteSection(section.id)} + addTodo={addTodo} + deleteTodo={deleteTodo} + /> + ))} + + ); } export default App; diff --git a/src/Section.js b/src/Section.js index db8562f..e66bca6 100644 --- a/src/Section.js +++ b/src/Section.js @@ -3,14 +3,23 @@ import styled from 'styled-components'; import TodoItem from './TodoItem'; const SectionContainer = styled.div` - margin: 20px; + margin: 20px 0; padding: 10px; border: 1px solid #ccc; border-radius: 5px; + display:flex; + flex-direction: column; + justify-content: flex-start; + align-content: space-around; + +`; + +const Title = styled.h3` + margin-bottom: 10px; `; const Input = styled.input` - margin: 10px; + margin-right: 10px; padding: 10px; border-radius: 5px; border: 1px solid #ccc; @@ -18,7 +27,7 @@ const Input = styled.input` const Button = styled.button` padding: 10px 20px; - margin: 5px; + margin:5px; border-radius: 5px; background-color: #007bff; color: white; @@ -30,36 +39,42 @@ const Button = styled.button` } `; -function Section({ section }) { - const [todos, setTodos] = useState(section.todos); - const [inputText, setInputText] = useState(''); - - const addTodo = () => { - if (!inputText) return; - const newTodo = { id: Date.now(), text: inputText }; - setTodos([...todos, newTodo]); - setInputText(''); - }; - - const deleteTodo = (todoId) => { - setTodos(todos.filter(todo => todo.id !== todoId)); - }; - - return ( - -

{section.name}

+const DeleteSectionButton = styled(Button)` + background-color: #007bff; + &:hover { + background-color:#0056b3; + } +`; + +function Section({ section, onDeleteSection, addTodo, deleteTodo }) { + const [todoText, setTodoText] = useState(''); + + const handleAddTodo = () => { + addTodo(section.id, todoText); + setTodoText(''); + }; + + return ( + +
+ {section.name} + 섹션 삭제 +
+ +
setInputText(e.target.value)} + value={todoText} + onChange={(e) => setTodoText(e.target.value)} /> - - {todos.map(todo => ( - - ))} - - ); - } - - export default Section; \ No newline at end of file + +
+ {section.todos.map(todo => ( + deleteTodo(section.id, todo.id)} /> + ))} +
+ ); +} + +export default Section; From 3adfcdb36340f220a010dffb7e16dcce5aa3d74e Mon Sep 17 00:00:00 2001 From: jinnyleeis <101055312+jinnyleeis@users.noreply.github.com> Date: Thu, 21 Mar 2024 14:59:13 +0900 Subject: [PATCH 05/15] =?UTF-8?q?=20=EC=84=B9=EC=85=98=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EB=B2=84=ED=8A=BC=20=EC=84=B9=EC=85=98=EB=AA=85=20?= =?UTF-8?q?=EC=98=86=EC=9C=BC=EB=A1=9C=20=EC=9C=84=EC=B9=98=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Section.js | 117 +++++++++++++++++++++++++++---------------------- 1 file changed, 64 insertions(+), 53 deletions(-) diff --git a/src/Section.js b/src/Section.js index e66bca6..495125e 100644 --- a/src/Section.js +++ b/src/Section.js @@ -3,78 +3,89 @@ import styled from 'styled-components'; import TodoItem from './TodoItem'; const SectionContainer = styled.div` - margin: 20px 0; - padding: 10px; - border: 1px solid #ccc; - border-radius: 5px; - display:flex; - flex-direction: column; + margin: 20px 0; + padding: 10px; + border: 1px solid #ccc; + border-radius: 5px; + display: flex; + flex-direction: column; justify-content: flex-start; align-content: space-around; - `; -const Title = styled.h3` - margin-bottom: 10px; +const TitleBar = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 10px; `; +const Title = styled.h3``; + const Input = styled.input` - margin-right: 10px; - padding: 10px; - border-radius: 5px; - border: 1px solid #ccc; + margin-right: 10px; + padding: 10px; + border-radius: 5px; + border: 1px solid #ccc; `; const Button = styled.button` - padding: 10px 20px; - margin:5px; - border-radius: 5px; - background-color: #007bff; - color: white; - border: none; - cursor: pointer; + padding: 10px 20px; + margin: 5px; + border-radius: 5px; + background-color: #007bff; + color: white; + border: none; + cursor: pointer; - &:hover { - background-color: #0056b3; - } + &:hover { + background-color: #0056b3; + } `; const DeleteSectionButton = styled(Button)` - background-color: #007bff; - &:hover { - background-color:#0056b3; - } + background-color: #007bff; + + &:hover { + background-color: #0056b3; + } `; function Section({ section, onDeleteSection, addTodo, deleteTodo }) { - const [todoText, setTodoText] = useState(''); + const [todoText, setTodoText] = useState(''); - const handleAddTodo = () => { - addTodo(section.id, todoText); - setTodoText(''); - }; + const handleAddTodo = () => { + if (todoText.trim() === '') return; // Prevent adding empty todos + addTodo(section.id, todoText); + setTodoText(''); + }; - return ( - -
- {section.name} - 섹션 삭제 -
- -
- setTodoText(e.target.value)} - /> - -
- {section.todos.map(todo => ( - deleteTodo(section.id, todo.id)} /> - ))} -
- ); + return ( + + + {section.name} + + 섹션 삭제 + + +
+ setTodoText(e.target.value)} + /> + +
+ {section.todos.map((todo) => ( + deleteTodo(section.id, todo.id)} + /> + ))} +
+ ); } export default Section; From f6bff54cec19f854b988c49aff902da3525d43a2 Mon Sep 17 00:00:00 2001 From: jinnyleeis <101055312+jinnyleeis@users.noreply.github.com> Date: Thu, 21 Mar 2024 18:50:27 +0900 Subject: [PATCH 06/15] =?UTF-8?q?=20=EB=A1=9C=EC=BB=AC=20=EC=8A=A4?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=EC=A7=80=EC=97=90=20=EC=84=B9=EC=85=98/?= =?UTF-8?q?=ED=95=A0=EC=9D=BC=20=EC=A0=80=EC=9E=A5/=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 55 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/src/App.js b/src/App.js index 021e583..b2f3d7e 100644 --- a/src/App.js +++ b/src/App.js @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import styled from 'styled-components'; import Section from './Section'; @@ -35,38 +35,49 @@ function App() { const [sections, setSections] = useState([]); const [sectionName, setSectionName] = useState(''); + // 로컬 스토리지에서 섹션 데이터 불러오기 + useEffect(() => { + const storedSections = JSON.parse(localStorage.getItem('sections')) || []; + setSections(storedSections); + }, []); + + // 섹션 또는 할 일이 변경될 때마다 로컬 스토리지 업데이트 + useEffect(() => { + localStorage.setItem('sections', JSON.stringify(sections)); + }, [sections]); + const addSection = () => { const newSection = { - id: Date.now(), // 현재 시간을 기준으로 고유한 ID 생성 + id: Date.now(), name: sectionName, todos: [] }; - setSections([...sections, newSection]); - setSectionName(''); // 입력 필드 초기화 + setSections(prevSections => [...prevSections, newSection]); + setSectionName(''); }; const deleteSection = (sectionId) => { - setSections(sections.filter(section => section.id !== sectionId)); + setSections(prevSections => prevSections.filter(section => section.id !== sectionId)); }; const addTodo = (sectionId, todoText) => { - setSections(sections.map(section => { - if (section.id === sectionId) { - const newTodo = { id: Date.now(), text: todoText }; - return { ...section, todos: [...section.todos, newTodo] }; - } - return section; - })); + setSections(prevSections => + prevSections.map(section => + section.id === sectionId + ? { ...section, todos: [...section.todos, { id: Date.now(), text: todoText }] } + : section + ) + ); }; const deleteTodo = (sectionId, todoId) => { - setSections(sections.map(section => { - if (section.id === sectionId) { - const filteredTodos = section.todos.filter(todo => todo.id !== todoId); - return { ...section, todos: filteredTodos }; - } - return section; - })); + setSections(prevSections => + prevSections.map(section => + section.id === sectionId + ? { ...section, todos: section.todos.filter(todo => todo.id !== todoId) } + : section + ) + ); }; return ( @@ -75,10 +86,10 @@ function App() { type="text" placeholder="새 섹션 이름" value={sectionName} - onChange={(e) => setSectionName(e.target.value)} + onChange={e => setSectionName(e.target.value)} /> - {sections.map((section) => ( + {sections.map(section => (
Date: Thu, 21 Mar 2024 19:02:16 +0900 Subject: [PATCH 07/15] =?UTF-8?q?=20=EA=B8=80=EB=A1=9C=EB=B2=8C=20?= =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=20=ED=8F=B0=ED=8A=B8=20=EC=86=8D?= =?UTF-8?q?=EC=84=B1=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 6 +++++- src/GlobalStyle.js | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 src/GlobalStyle.js diff --git a/src/App.js b/src/App.js index b2f3d7e..a76674d 100644 --- a/src/App.js +++ b/src/App.js @@ -1,6 +1,9 @@ import React, { useState, useEffect } from 'react'; -import styled from 'styled-components'; +import {styled} from 'styled-components'; import Section from './Section'; +import GlobalStyle from './GlobalStyle'; + + const AppContainer = styled.div` display: flex; @@ -82,6 +85,7 @@ function App() { return ( + Date: Fri, 22 Mar 2024 00:16:07 +0900 Subject: [PATCH 08/15] =?UTF-8?q?=20=EB=82=A8=EC=9D=80=20=ED=95=A0?= =?UTF-8?q?=EC=9D=BC=20=EA=B0=9C=EC=88=98,=20=EC=98=A4=EB=8A=98=20?= =?UTF-8?q?=EB=82=A0=EC=A7=9C=20=EB=9D=84=EC=9A=B0=EA=B8=B0=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 214 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 121 insertions(+), 93 deletions(-) diff --git a/src/App.js b/src/App.js index a76674d..3b30b53 100644 --- a/src/App.js +++ b/src/App.js @@ -1,109 +1,137 @@ import React, { useState, useEffect } from 'react'; -import {styled} from 'styled-components'; +import styled from 'styled-components'; import Section from './Section'; import GlobalStyle from './GlobalStyle'; - - const AppContainer = styled.div` - display: flex; - flex-direction: column; - align-items: center; - width: 80%; - margin: 30px auto; + display: flex; + flex-direction: column; + align-items: center; + width: 80%; + margin: 30px auto; `; const Input = styled.input` - margin: 10px; - padding: 10px; - border-radius: 5px; - border: 1px solid #ccc; + margin: 10px; + padding: 10px; + border-radius: 5px; + border: 1px solid #ccc; `; const Button = styled.button` - padding: 10px 20px; - margin: 5px; - border-radius: 5px; - background-color: #007bff; - color: white; - border: none; - cursor: pointer; - - &:hover { - background-color: #0056b3; - } + padding: 10px 20px; + margin: 5px; + border-radius: 5px; + background-color: #007bff; + color: white; + border: none; + cursor: pointer; + + &:hover { + background-color: #0056b3; + } +`; + +const SummaryText = styled.h2` + margin-top: 20px; `; function App() { - const [sections, setSections] = useState([]); - const [sectionName, setSectionName] = useState(''); - - // 로컬 스토리지에서 섹션 데이터 불러오기 - useEffect(() => { - const storedSections = JSON.parse(localStorage.getItem('sections')) || []; - setSections(storedSections); - }, []); - - // 섹션 또는 할 일이 변경될 때마다 로컬 스토리지 업데이트 - useEffect(() => { - localStorage.setItem('sections', JSON.stringify(sections)); - }, [sections]); - - const addSection = () => { - const newSection = { - id: Date.now(), - name: sectionName, - todos: [] - }; - setSections(prevSections => [...prevSections, newSection]); - setSectionName(''); - }; - - const deleteSection = (sectionId) => { - setSections(prevSections => prevSections.filter(section => section.id !== sectionId)); - }; - - const addTodo = (sectionId, todoText) => { - setSections(prevSections => - prevSections.map(section => - section.id === sectionId - ? { ...section, todos: [...section.todos, { id: Date.now(), text: todoText }] } - : section - ) - ); - }; - - const deleteTodo = (sectionId, todoId) => { - setSections(prevSections => - prevSections.map(section => - section.id === sectionId - ? { ...section, todos: section.todos.filter(todo => todo.id !== todoId) } - : section - ) - ); - }; - - return ( - - - setSectionName(e.target.value)} - /> - - {sections.map(section => ( -
deleteSection(section.id)} - addTodo={addTodo} - deleteTodo={deleteTodo} - /> - ))} - - ); + const [sections, setSections] = useState([]); + const [sectionName, setSectionName] = useState(''); + + // 로컬 스토리지에서 섹션 데이터 불러오기 + useEffect(() => { + const storedSections = JSON.parse(localStorage.getItem('sections')) || []; + setSections(storedSections); + }, []); + + // 섹션 또는 할 일이 변경될 때마다 로컬 스토리지 업데이트 + useEffect(() => { + localStorage.setItem('sections', JSON.stringify(sections)); + }, [sections]); + + const addSection = () => { + if (!sectionName.trim()) return; + const newSection = { + id: Date.now(), + name: sectionName, + todos: [], + }; + setSections((prevSections) => [...prevSections, newSection]); + setSectionName(''); + }; + + const deleteSection = (sectionId) => { + setSections((prevSections) => + prevSections.filter((section) => section.id !== sectionId) + ); + }; + + const addTodo = (sectionId, todoText) => { + setSections((prevSections) => + prevSections.map((section) => + section.id === sectionId + ? { + ...section, + todos: [...section.todos, { id: Date.now(), text: todoText }], + } + : section + ) + ); + }; + + const deleteTodo = (sectionId, todoId) => { + setSections((prevSections) => + prevSections.map((section) => + section.id === sectionId + ? { + ...section, + todos: section.todos.filter((todo) => todo.id !== todoId), + } + : section + ) + ); + }; + + // 남은 할 일의 총 개수 계산 + const remainingTodos = sections.reduce( + (acc, section) => acc + section.todos.length, + 0 + ); + + // 오늘의 날짜 표시 + const today = new Date().toLocaleDateString('ko-KR', { + year: 'numeric', + month: 'long', + day: 'numeric', + weekday: 'long', + }); + + return ( + + + + {today} - 남은 할 일: {remainingTodos}개 + + setSectionName(e.target.value)} + /> + + {sections.map((section) => ( +
deleteSection(section.id)} + addTodo={addTodo} + deleteTodo={deleteTodo} + /> + ))} + + ); } -export default App; \ No newline at end of file +export default App; From 43869c38fac44b59e1a933c44ea61d1d25de1f33 Mon Sep 17 00:00:00 2001 From: jinnyleeis <101055312+jinnyleeis@users.noreply.github.com> Date: Fri, 22 Mar 2024 19:03:51 +0900 Subject: [PATCH 09/15] =?UTF-8?q?=20=EC=99=84=EB=A3=8C=20->=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=EB=B2=84=ED=8A=BC=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD,=20=EC=99=84=EB=A3=8C=20=EB=B2=84=ED=8A=BC?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20&=20=ED=99=94=EB=A9=B4=EC=97=90=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C=EB=90=9C=20=ED=95=A0=20=EC=9D=BC=20=ED=91=9C?= =?UTF-8?q?=EC=8B=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 50 +++++++++++++++++++++++++------- src/Section.js | 77 +++++++++++++++++++++++++------------------------ src/TodoItem.js | 28 +++++++++++------- 3 files changed, 96 insertions(+), 59 deletions(-) diff --git a/src/App.js b/src/App.js index 3b30b53..c382912 100644 --- a/src/App.js +++ b/src/App.js @@ -36,17 +36,19 @@ const SummaryText = styled.h2` margin-top: 20px; `; +const CountText = styled.h4` + margin-top: 2px; +`; + function App() { const [sections, setSections] = useState([]); const [sectionName, setSectionName] = useState(''); - // 로컬 스토리지에서 섹션 데이터 불러오기 useEffect(() => { const storedSections = JSON.parse(localStorage.getItem('sections')) || []; setSections(storedSections); }, []); - // 섹션 또는 할 일이 변경될 때마다 로컬 스토리지 업데이트 useEffect(() => { localStorage.setItem('sections', JSON.stringify(sections)); }, [sections]); @@ -74,7 +76,10 @@ function App() { section.id === sectionId ? { ...section, - todos: [...section.todos, { id: Date.now(), text: todoText }], + todos: [ + ...section.todos, + { id: Date.now(), text: todoText, completed: false }, + ], } : section ) @@ -94,13 +99,34 @@ function App() { ); }; - // 남은 할 일의 총 개수 계산 - const remainingTodos = sections.reduce( - (acc, section) => acc + section.todos.length, - 0 + // toggleTodoCompleted 함수만 추가된 부분을 보여줍니다. + + const toggleTodoCompleted = (sectionId, todoId) => { + setSections((prevSections) => + prevSections.map((section) => + section.id === sectionId + ? { + ...section, + todos: section.todos.map((todo) => + todo.id === todoId + ? { ...todo, completed: !todo.completed } + : todo + ), + } + : section + ) + ); + }; + + // 남은 할 일과 완료된 할 일의 총 개수 계산 + const [remainingTodos, completedTodos] = sections.reduce( + ([remaining, completed], section) => [ + remaining + section.todos.filter((todo) => !todo.completed).length, + completed + section.todos.filter((todo) => todo.completed).length, + ], + [0, 0] ); - // 오늘의 날짜 표시 const today = new Date().toLocaleDateString('ko-KR', { year: 'numeric', month: 'long', @@ -111,9 +137,10 @@ function App() { return ( - - {today} - 남은 할 일: {remainingTodos}개 - + {today} + + 남은 할 일: {remainingTodos}개, 완료된 할 일: {completedTodos}개 + deleteSection(section.id)} addTodo={addTodo} deleteTodo={deleteTodo} + onToggleCompleted={toggleTodoCompleted} /> ))} diff --git a/src/Section.js b/src/Section.js index 495125e..a5a9003 100644 --- a/src/Section.js +++ b/src/Section.js @@ -51,41 +51,42 @@ const DeleteSectionButton = styled(Button)` } `; -function Section({ section, onDeleteSection, addTodo, deleteTodo }) { - const [todoText, setTodoText] = useState(''); - - const handleAddTodo = () => { - if (todoText.trim() === '') return; // Prevent adding empty todos - addTodo(section.id, todoText); - setTodoText(''); - }; - - return ( - - - {section.name} - - 섹션 삭제 - - -
- setTodoText(e.target.value)} - /> - -
- {section.todos.map((todo) => ( - deleteTodo(section.id, todo.id)} - /> - ))} -
- ); -} - -export default Section; +function Section({ section, onDeleteSection, addTodo, deleteTodo, onToggleCompleted }) { + const [todoText, setTodoText] = useState(''); + + const handleAddTodo = () => { + if (todoText.trim() === '') return; + addTodo(section.id, todoText); + setTodoText(''); + }; + + return ( + + + {section.name} + onDeleteSection(section.id)}> + 섹션 삭제 + + +
+ setTodoText(e.target.value)} + /> + +
+ {section.todos.map((todo) => ( + deleteTodo(section.id, todo.id)} + onToggleCompleted={() => onToggleCompleted(section.id, todo.id)} + /> + ))} +
+ ); + } + + export default Section; \ No newline at end of file diff --git a/src/TodoItem.js b/src/TodoItem.js index a1f702b..f8d73bf 100644 --- a/src/TodoItem.js +++ b/src/TodoItem.js @@ -16,19 +16,27 @@ const Button = styled.button` border: none; border-radius: 5px; cursor: pointer; + &:hover { background-color: #0056b3; } `; -function TodoItem({ todo, onDelete }) { - return ( - - {todo.text} - - - ); -} - -export default TodoItem; +//TodoItem 컴포넌트에 완료 상태를 토글하는 기능 추가 +function TodoItem({ todo, onDelete, onToggleCompleted }) { + return ( + + + {todo.text} + + + + + ); + } + + export default TodoItem; + \ No newline at end of file From 3ac6e99dc75ec475f389195c2ade94357331e41a Mon Sep 17 00:00:00 2001 From: jinnyleeis <101055312+jinnyleeis@users.noreply.github.com> Date: Fri, 22 Mar 2024 20:08:53 +0900 Subject: [PATCH 10/15] =?UTF-8?q?