Skip to content
Merged
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
615 changes: 193 additions & 422 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"preview": "vite preview"
},
"dependencies": {
"@tailwindcss/vite": "^4.2.2",
"react": "^19.2.4",
"react-dom": "^19.2.4"
},
Expand All @@ -23,6 +24,7 @@
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.5.2",
"globals": "^17.4.0",
"tailwindcss": "^4.2.2",
"typescript": "~5.9.3",
"typescript-eslint": "^8.56.1",
"vite": "^8.0.0"
Expand Down
15 changes: 0 additions & 15 deletions src/App.css

This file was deleted.

16 changes: 8 additions & 8 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import TodoHeader from './components/TodoHeader';
import TodoList from './components/TodoList';
import './App.css'
import TodoHeader from './components/TodoHeader'
import TodoList from './components/TodoList'

function App() {

return (
<div className="todo-wrapper">
<TodoHeader />
<TodoList />
<div className="flex min-h-screen w-full items-start bg-[#F5F5F5]">
<div className="mx-auto flex w-full max-w-[640px] flex-col items-start gap-[22px]">
<TodoHeader />
<TodoList />
</div>
</div>
)
}

export default App
export default App
34 changes: 27 additions & 7 deletions src/components/TodoCard.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,30 @@
import "./css/TodoCard.css"
import EmptyCircleIcon from "../icons/EmptyCircleIcon"
import CheckIcon from "../icons/CheckIcon"

export default function TodoCard({ todo }: { todo: string }) {

return (
<div className="todo-card">
<p className="todo-card-text">{todo}</p>
function Item({todo, isCompleted }: {todo: string;isCompleted: boolean;}) {
return(
<div>
{isCompleted ? (
<div className= "flex items-center gap-[12px] text-[#1F2937] opacity-50">
<CheckIcon />
<del>{todo}</del>
</div>
) : (
<div className= "flex items-center gap-[12px] text-[#1F2937]">
<EmptyCircleIcon />
<span>{todo}</span>
</div>
)
)}
</div>
)
}

export default function TodoCard({todo, isCompleted }: {todo: string;isCompleted: boolean;}) {
return (
<li className="flex w-full items-center gap-[10px] p-4 rounded-[12px] bg-white shadow-sm">
<p className="text-[14px] leading-[21px] text-[#1F2937]">
<Item todo={todo} isCompleted={isCompleted} />
</p>
Comment on lines +25 to +27
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p 태그로 Item 컴포넌트를 감싸고 있는데 Item 컴포넌트가 내부에서 div로 감싸져있어 html 구조가 어색해지는 부분이 있습니다 텍스트만 p로 감싸거나, 바깥 구조를 div로 정리하는 쪽이 더 자연스러울 것 같아요.

</li>
)
}
28 changes: 15 additions & 13 deletions src/components/TodoHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import "./css/TodoHeader.css";

export default function TodoHeader() {
const emoji = "✅";
const title = "오늘의 할 일";

return (
<div className="todo-header-wrapper">
<div className="todo-header-row">
<span className="todo-header-text">{emoji}</span>
<span className="todo-header-text">{title}</span>
</div>
const emoji = "✅";
const title = "오늘의 할 일";

return (
<div className="flex flex-col items-start gap-[10px] self-stretch py-[10px] mt-[80px]">
<div className="flex h-[36px] items-center gap-[8px] self-stretch">
<span className="text-[24px] font-bold leading-[36px] text-[#1F2937]">
{emoji}
</span>
<span className="text-[24px] font-bold leading-[36px] text-[#1F2937]">
{title}
</span>
Comment on lines +11 to +13
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

헤더 타이틀이 지금은 span으로 들어가 있는데, 화면의 실제 제목 역할이라면 h1이나 h2같은 태그로 표현하면 구조 의미가 더 분명해질 것 같습니다.

</div>
);
}
</div>
);
}
22 changes: 15 additions & 7 deletions src/components/TodoList.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import TodoCard from "./TodoCard";
import "./css/TodoList.css"
import { todoData } from "../data/TodoData";

export default function TodoList() {
const todoItems = todoData.map( item =>
<TodoCard key={item.id} todo = {item.todo} isCompleted = {item.isCompleted} />
);

return (
<div className="todo-list">
<TodoCard todo="리액트 공식문서 읽기" />
<TodoCard todo="알고리즘 문제 풀기" />
<TodoCard todo="운동 30분 하기" />
<TodoCard todo="프로젝트 회의 준비" />
</div>
<ul className="flex flex-col items-start gap-[16px] self-stretch">
{todoData.length === 0 ? (
<li className="mx-auto flex w-full max-w-[640px] h-[233px] flex-col items-center justify-center gap-[12px] rounded-[12px] bg-white shadow-sm">
<span className="text-[48px] leading-[72px] text-center">📋</span>
<p className="text-[14px] text-[#6B7280]">아직 할 일이 없어요</p>
</li>
) : (
todoItems
)}
</ul>
)
}
19 changes: 0 additions & 19 deletions src/components/css/TodoCard.css

This file was deleted.

26 changes: 0 additions & 26 deletions src/components/css/TodoHeader.css

This file was deleted.

8 changes: 0 additions & 8 deletions src/components/css/TodoList.css

This file was deleted.

28 changes: 28 additions & 0 deletions src/data/TodoData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
type Todo = {
id: number;
todo: string;
isCompleted: boolean;
};

export const todoData: Todo[] = [
{
id: 0,
todo: '리액트 공식문서 읽기',
isCompleted: true
},
{
id: 1,
todo: '알고리즘 문제 풀기',
isCompleted: true
},
{
id:2,
todo: '운동 30분 하기',
isCompleted: false
},
{
id: 3,
todo: '프로젝트 회의 준비',
isCompleted: false
}
];
28 changes: 28 additions & 0 deletions src/icons/CheckIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
export default function CheckCircle() {
return (
<div className="flex h-[24px] w-[24px] items-center justify-center rounded-full bg-[#3B82F6] border-2 border-[#3B82F6]">
<svg
width="14"
height="10"
viewBox="0 0 14 10"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0)">
<path
d="M1 5L5 9L13 1"
stroke="white"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</g>
<defs>
<clipPath id="clip0">
<rect width="14" height="10" fill="white" />
</clipPath>
</defs>
</svg>
</div>
);
}
38 changes: 38 additions & 0 deletions src/icons/EmptyCircleIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
type EmptyCircleIconProps = {
checked?: boolean;
};

export default function EmptyCircleIcon({
checked = false,
}: EmptyCircleIconProps) {
return checked ? (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<circle cx="12" cy="12" r="11" fill="#22C55E" stroke="#22C55E" strokeWidth="2" />
<path
d="M8 12.5L10.5 15L16 9.5"
stroke="white"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
) : (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<circle cx="12" cy="12" r="11" stroke="#E5E7EB" strokeWidth="2" />
</svg>
);
}
Loading