Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Mission-FE-Zero100
Submodule Mission-FE-Zero100 added at 839460
179 changes: 150 additions & 29 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,23 @@ body {
font-family: Arial, sans-serif; /* 폰트 변경 */
display: flex;
justify-content: center;
align-items: center;
align-items: flex-start; /* 상단 정렬 */
min-height: 100vh;
margin: 0;
background-color: #fff;
padding-top: 50px; /* 상단 여백 추가 */
}

.app-container {
max-width: 600px;
width: 100%;
padding: 20px;
/* 그림자에 대한 언급은 없으므로 제거하거나 유지 */
/* box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); */
background-color: #fff; /* 배경색 확실히 지정 */
}

/* 기존 요소들 스타일 */
h1 {
font-size: 2.5rem;
font-weight: bold;
Expand All @@ -29,7 +34,7 @@ h2 {
text-align: left;
}

/* 입력창 */
/* 입력창 (AddTodo) */
.input-container {
margin-bottom: 10px;
}
Expand All @@ -40,7 +45,8 @@ h2 {
font-size: 1rem;
border: 2px solid #333; /* 입력창만 굵은 테두리 */
border-radius: 0;
box-sizing: border-box;
box-sizing: border-box; /* 패딩과 테두리가 width에 포함되도록 */
outline: none; /* 포커스 시 기본 외곽선 제거 */
}

/* Add 버튼 */
Expand All @@ -54,9 +60,11 @@ h2 {
font-size: 1rem;
background-color: #000;
color: #fff;
border: none;
border: none; /* Add 버튼은 테두리 없음 */
border-radius: 0;
cursor: pointer;
outline: none;
transition: background-color 0.2s ease; /* 호버 효과 부드럽게 */
}

.add-button-container button:hover {
Expand All @@ -80,9 +88,18 @@ h2 {
border: 1px solid #333; /* 얇은 테두리 */
border-radius: 0;
cursor: pointer;
outline: none;
transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}

.filter-buttons button:hover {
/* 활성 필터 버튼 스타일 */
.filter-buttons button.active {
background-color: #000; /* 활성 버튼은 배경 검정 */
color: #fff; /* 글자 흰색 */
border-color: #000;
}

.filter-buttons button:not(.active):hover { /* 활성 상태가 아닐 때만 호버 스타일 적용 */
background-color: #f0f0f0;
}

Expand All @@ -94,38 +111,53 @@ h2 {
text-align: left;
}

/* 작업 목록 */
/* 작업 목록 컨테이너 */
.task-list {
margin-top: 10px;
/* margin-top: 10px; */
}

/* 각 할 일 항목 (기본 & 편집 공통 스타일) */
.task-item {
margin-bottom: 20px;
margin-bottom: 20px; /* 항목 하단 간격 */
border-bottom: 1px solid #eee; /* 항목 구분선 */
padding-bottom: 20px; /* 구분선과 내용 간 간격 */
}

.task-row {
.task-item:last-child {
border-bottom: none; /* 마지막 항목은 구분선 없음 */
padding-bottom: 0;
}


/* 기본 모드 (.task-item 안에 있을 때) */
.task-item .task-row {
display: flex;
align-items: center;
margin-bottom: 5px;
margin-bottom: 5px; /* 체크박스/이름과 버튼 간 간격 */
}

.task-row input[type="checkbox"] {
/* 체크박스 기본 스타일 */
.task-item .task-row input[type="checkbox"] {
width: 20px;
height: 20px;
margin-right: 10px;
border: 2px solid #333;
border-radius: 0;
appearance: none;
appearance: none; /* 기본 브라우저 스타일 제거 */
cursor: pointer;
flex-shrink: 0; /* 체크박스가 줄어들지 않도록 */
position: relative; /* 체크마크 위치 기준 */
outline: none;
}

.task-row input[type="checkbox"]:checked {
/* 체크박스 체크된 상태 스타일 */
.task-item .task-row input[type="checkbox"]:checked {
background-color: #000;
border-color: #000;
position: relative;
}

.task-row input[type="checkbox"]:checked::after {
/* 체크박스 체크마크 */
.task-item .task-row input[type="checkbox"]:checked::after {
content: "✔";
color: #fff;
font-size: 14px;
Expand All @@ -135,36 +167,125 @@ h2 {
transform: translate(-50%, -50%);
}

.task-row .task-name {
/* 할 일 이름 */
.task-item .task-row .task-name {
font-size: 1rem;
flex-grow: 1; /* 남은 공간 차지 */
word-break: break-word; /* 긴 단어 줄바꿈 */
}

/* 완료된 할 일 이름 스타일 */
.task-item .task-row .task-name.completed {
text-decoration: line-through;
color: #888; /* 회색으로 변경 */
}

.task-actions {

/* Edit/Delete 버튼 컨테이너 (기본 모드 & 편집 모드 공통) */
.task-item .task-actions {
display: flex;
gap: 10px;
gap: 10px; /* 버튼 간 간격 */
}

.task-actions button {
flex: 1;
/* Edit/Delete 버튼 기본 스타일 */
.task-item .task-actions button {
flex: 1; /* 필터 버튼과 동일한 폭 */
padding: 5px 10px;
font-size: 0.9rem;
background-color: #fff;
color: #000;
border: 1px solid #333;
border: 1px solid #333; /* 얇은 테두리 */
border-radius: 0;
cursor: pointer;
outline: none;
transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}

.task-actions button:hover {
.task-item .task-actions button:hover {
background-color: #f0f0f0;
}

.task-actions .button-delete {
background-color: #ff0000;
color: #fff;

/* Delete 버튼 스타일 */
.task-item .task-actions button:last-child {
background-color: #ff0000; /* 빨간색 배경 */
color: #fff; /* 흰색 글씨 */
border-color: #ff0000; /* 배경과 동일한 색 테두리 */
}

.task-item .task-actions button:last-child:hover {
background-color: #e60000; /* 호버 시 약간 어두운 빨간색 */
border-color: #e60000;
}


/* --- 👇 편집 모드 (Editing Mode) 스타일 --- */

/* 편집 모드일 때 할 일 항목 컨테이너 레이아웃 */
.task-item.editing {
display: flex; /* Flexbox 사용 */
flex-direction: column; /* 자식 요소를 세로로 쌓음 */
align-items: flex-start; /* 왼쪽 정렬 */
}

/* 편집 모드일 때 기본 .task-row (체크박스 + 이름) 숨기기 */
.task-item.editing .task-row {
display: none;
}

/* 편집 모드 라벨 스타일 ("New name for...") */
.task-item.editing .edit-label {
font-size: 1rem; /* task-name과 동일한 크기 */
margin-bottom: 5px; /* 라벨과 입력창 간 간격 */
font-weight: normal; /* 볼드 해제 (task-name은 볼드 아니었으므로) */
}

/* 편집 모드 입력창 스타일 */
.task-item.editing input.edit-input {
width: 100%; /* 부모 컨테이너 폭에 맞춤 */
padding: 8px;
font-size: 1rem;
border: 2px solid #333;
border-radius: 0;
box-sizing: border-box;
margin-bottom: 10px; /* 입력창과 버튼 간 간격 */
outline: none; /* 포커스 시 기본 외곽선 제거 */
}

/* 편집 모드 버튼 컨테이너 스타일 (Cancel/Save) */
.task-item.editing .task-actions {
display: flex; /* 버튼들은 Flex로 */
gap: 10px; /* 버튼 간 간격 */
width: 100%; /* 부모(task-item.editing) 너비에 맞춤 */
}

/* Cancel 버튼 스타일 */
.task-item.editing .task-actions .cancel-button { /* 클래스 선택자 사용 */
flex: 1; /* 동일 폭 */
padding: 5px 10px;
font-size: 0.9rem;
background-color: #fff; /* 흰색 배경 */
color: #000; /* 검은색 글씨 */
border: 1px solid #333; /* 얇은 테두리 */
border-radius: 0;
}
.task-item.editing .task-actions .cancel-button:hover {
background-color: #f0f0f0;
}

/* Save 버튼 스타일 */
.task-item.editing .task-actions .save-button { /* 클래스 선택자 사용 */
flex: 1; /* 동일 폭 */
padding: 5px 10px;
font-size: 0.9rem;
/* 👇 여기! Save 버튼 배경을 검정색으로 변경 */
background-color: #000; /* 검은색 배경 */
color: #fff; /* 글자색 흰색 유지 */
border: none; /* 테두리 없음 유지 */
/* 👆 여기까지 수정 */
border-radius: 0;
}
/* Save 버튼 호버 스타일 */
.task-item.editing .task-actions .save-button:hover {
background-color: #333; /* 호버 시 약간 밝은 검정색 */
}

.task-actions .button-delete:hover {
background-color: #e60000;
}
84 changes: 31 additions & 53 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,66 +1,44 @@
// src/App.jsx - 라우팅 설정 담당
import React from 'react';
import { Routes, Route } from 'react-router-dom';

import Header from "@/component/Header";
import AddTodo from "@/component/AddTodo";
import Category from "@/component/Category";
import TodoList from "@/component/TodoList";
// 라우팅할 페이지 컴포넌트들을 임포트합니다.
// 아직 HomePage, LoginPage, SignupPage 파일이 없다면 이전에 제공된 코드로 src/pages 폴더에 생성해야 합니다.
import HomePage from './pages/HomePage';
import LoginPage from './pages/LoginPage';
import SignupPage from './pages/SignupPage';
// 이름을 변경하고 이동시킨 TodoPage 컴포넌트를 임포트합니다.
import TodoPage from './pages/TodoPage';

import "./App.css";
import './App.css'; // App 전체에 적용될 스타일이 있다면 유지

function App() {
const [tasks, setTasks] = useState([
{ name: "Eat", completed: true },
{ name: "Sleep", completed: false },
{ name: "Repeat", completed: false },
]);
const [inputValue, setInputValue] = useState("");
const [filter, setFilter] = useState("all");
return (
// 앱 전체 레이아웃 또는 전역 스타일을 위한 컨테이너
<div className="App">
{/* App 전체에 적용될 헤더, 네비게이션 바 등은 Routes 바깥에 둘 수 있습니다. */}
<h1 style={{ textAlign: 'center', marginTop: '20px' }}>Zero100 과제 - 5주차</h1>
<h2 style={{ textAlign: 'center' }}>로그인/회원가입 구현하기 - ui 구현</h2>

const addTask = () => {
if (inputValue) {
setTasks([...tasks, { name: inputValue, completed: false }]);
setInputValue("");
}
};
{/* Routes 컴포넌트로 라우트를 정의 */}
<Routes>
{/* 루트 경로 ('/')는 HomePage 컴포넌트와 연결 */}
<Route path="/" element={<HomePage />} />

const toggleTask = (index) => {
const updatedTasks = [...tasks];
updatedTasks[index].completed = !updatedTasks[index].completed;
setTasks(updatedTasks);
};
{/* '/login' 경로는 LoginPage 컴포넌트와 연결 */}
<Route path="/login" element={<LoginPage />} />

const deleteTask = (index) => {
const updatedTasks = tasks.filter((_, i) => i !== index);
setTasks(updatedTasks);
};
{/* '/signup' 경로는 SignupPage 컴포넌트와 연결 */}
<Route path="/signup" element={<SignupPage />} />

const editTask = (index, newName) => {
const updatedTasks = [...tasks];
updatedTasks[index].name = newName;
setTasks(updatedTasks);
};
{/* '/todo' 경로는 이제 TodoPage (기존 App) 컴포넌트와 연결 */}
<Route path="/todo" element={<TodoPage />} />

const filteredTasks = tasks.filter((task) => {
if (filter === "all") return true;
if (filter === "active") return !task.completed;
if (filter === "completed") return task.completed;
return true;
});
{/* 정의되지 않은 경로에 대한 처리 */}
{/* <Route path="*" element={<div>404 Not Found</div>} /> */}
</Routes>

return (
<div className="app-container">
<Header />
<AddTodo
inputValue={inputValue}
setInputValue={setInputValue}
addTask={addTask}
/>
<Category setFilter={setFilter} />
<TodoList
filteredTasks={filteredTasks}
toggleTask={toggleTask}
editTask={editTask}
deleteTask={deleteTask}
/>
{/* App 전체에 적용될 푸터 등은 Routes 바깥에 둘 수 있습니다. */}
</div>
);
}
Expand Down
1 change: 1 addition & 0 deletions src/Mission-FE-Zero100
Submodule Mission-FE-Zero100 added at 839460
4 changes: 3 additions & 1 deletion src/component/AddTodo.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ function AddTodo({ inputValue, setInputValue, addTask }) {
onChange={(e) => setInputValue(e.target.value)}
/>
</div>
<ButtonComponent type="add" label="Add" onClick={addTask} />
<div className="add-button-container">
<ButtonComponent label="Add" onClick={addTask} />
</div>
</div>
);
}
Expand Down
Loading