diff --git a/src/components/service/ButtonSection/index.module.css b/src/components/service/ButtonSection/index.module.css
new file mode 100644
index 0000000..b12d604
--- /dev/null
+++ b/src/components/service/ButtonSection/index.module.css
@@ -0,0 +1,4 @@
+.buttonSection {
+ display: flex;
+ justify-content: flex-end;
+}
diff --git a/src/components/service/ButtonSection/index.tsx b/src/components/service/ButtonSection/index.tsx
new file mode 100644
index 0000000..47a21bd
--- /dev/null
+++ b/src/components/service/ButtonSection/index.tsx
@@ -0,0 +1,13 @@
+import { Button } from "../../ui/Button";
+import styles from "./index.module.css";
+import { useNavigate } from "react-router-dom";
+
+export const ButtonSection = () => {
+ const navigate = useNavigate();
+
+ return (
+
+
+
+ );
+};
diff --git a/src/components/service/Header/index.module.css b/src/components/service/Header/index.module.css
new file mode 100644
index 0000000..95a916f
--- /dev/null
+++ b/src/components/service/Header/index.module.css
@@ -0,0 +1,16 @@
+.header {
+ display: flex;
+ align-items: center;
+ padding: 16px;
+ background: #fff;
+ box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
+ width: 100%;
+ position: fixed;
+ top: 0px;
+ z-index: 1000;
+}
+
+.logo {
+ width: 32px;
+ height: 32px;
+}
diff --git a/src/components/service/Header/index.tsx b/src/components/service/Header/index.tsx
new file mode 100644
index 0000000..23766bf
--- /dev/null
+++ b/src/components/service/Header/index.tsx
@@ -0,0 +1,9 @@
+import styles from "./index.module.css";
+
+export const Header = () => {
+ return (
+
+
+
+ );
+};
diff --git a/src/components/service/TodoSection/TodoCard.module.css b/src/components/service/TodoSection/TodoCard.module.css
new file mode 100644
index 0000000..1159f37
--- /dev/null
+++ b/src/components/service/TodoSection/TodoCard.module.css
@@ -0,0 +1,52 @@
+.card {
+ background: #f9fafb;
+ border: 1px solid #e5e7eb;
+ border-radius: 8px;
+ padding: 12px;
+}
+
+.cardHeader {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.cardTitle {
+ font-weight: 500;
+}
+
+.cardDate {
+ background-color: #e5e7eb;
+ color: #374151;
+ font-size: 12px;
+ font-weight: bold;
+ border-radius: 9999px;
+ padding: 4px 12px;
+}
+
+.cardDescription {
+ color: #6b7280;
+ font-size: 14px;
+ margin-top: 8px;
+}
+
+.avatarSection {
+ display: flex;
+ align-items: center;
+ margin-top: 20px;
+}
+
+.avatar {
+ width: 24px;
+ height: 24px;
+ border-radius: 9999px;
+ background-color: #ccc;
+ box-shadow: 0px 0px 0px 2px #fff;
+}
+
+.avatarText {
+ font-weight: 500;
+ font-size: 14px;
+ color: #6b7280;
+ margin-left: 8px;
+}
diff --git a/src/components/service/TodoSection/TodoCard.tsx b/src/components/service/TodoSection/TodoCard.tsx
new file mode 100644
index 0000000..14a8854
--- /dev/null
+++ b/src/components/service/TodoSection/TodoCard.tsx
@@ -0,0 +1,36 @@
+import styles from "./TodoCard.module.css";
+import { Todo } from "../../../types/todo";
+
+type Props = {
+ task: Todo;
+};
+
+export const TodoCard = ({ task }: Props) => {
+ const { title, content, startDate, assignees } = task;
+
+ const formattedDate = new Date(startDate).toLocaleDateString("ko-KR", {
+ year: "numeric",
+ month: "2-digit",
+ day: "2-digit",
+ });
+
+ return (
+
+
+
{title}
+
{formattedDate}
+
+
{content}
+
+ {assignees.map((assignee, i) => (
+
0 ? "-12px" : "0" }}
+ >
+ ))}
+
{assignees.join(",")}
+
+
+ );
+};
diff --git a/src/components/service/TodoSection/TodoColumn.module.css b/src/components/service/TodoSection/TodoColumn.module.css
new file mode 100644
index 0000000..c15c3d3
--- /dev/null
+++ b/src/components/service/TodoSection/TodoColumn.module.css
@@ -0,0 +1,17 @@
+.container {
+ flex: 1;
+ background: #fff;
+ border-radius: 8px;
+ padding: 16px;
+ box-shadow: "0px 1px 2px -1px rgba(0, 0, 0, 0.1), 0px 1px 3px 0px rgba(0, 0, 0, 0.1)";
+ margin-top: 16px;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+}
+
+.title {
+ font-size: 18px;
+ font-weight: bold;
+ margin-bottom: 16px;
+}
diff --git a/src/components/service/TodoSection/TodoColumn.tsx b/src/components/service/TodoSection/TodoColumn.tsx
new file mode 100644
index 0000000..1857c6b
--- /dev/null
+++ b/src/components/service/TodoSection/TodoColumn.tsx
@@ -0,0 +1,19 @@
+import styles from "./TodoColumn.module.css";
+import { TodoCard } from "./TodoCard";
+import { Todo } from "../../../types/todo";
+
+type Props = {
+ title: string;
+ tasks: Todo[];
+};
+
+export const TodoColumn = ({ title, tasks }: Props) => {
+ return (
+
+
{title}
+ {tasks.map((task) => (
+
+ ))}
+
+ );
+};
diff --git a/src/components/service/TodoSection/index.module.css b/src/components/service/TodoSection/index.module.css
new file mode 100644
index 0000000..c1d188b
--- /dev/null
+++ b/src/components/service/TodoSection/index.module.css
@@ -0,0 +1,4 @@
+.todoSection {
+ display: flex;
+ gap: 24px;
+}
diff --git a/src/components/service/TodoSection/index.tsx b/src/components/service/TodoSection/index.tsx
new file mode 100644
index 0000000..21db815
--- /dev/null
+++ b/src/components/service/TodoSection/index.tsx
@@ -0,0 +1,36 @@
+import { useEffect, useState } from "react";
+import styles from "./index.module.css";
+import { TodoColumn } from "./TodoColumn";
+import { Todo } from "../../../types/todo";
+
+const columns = [
+ { title: "시작하지 않음", status: "notStarted" },
+ { title: "진행 중", status: "inProgress" },
+ { title: "완료", status: "done" },
+];
+
+export const TodoSection = () => {
+ const [todos, setTodos] = useState([]);
+
+ useEffect(() => {
+ const fetchTodos = async () => {
+ const response = await fetch("/api/todos");
+ const data = await response.json();
+ setTodos(data);
+ };
+ fetchTodos();
+ }, []);
+
+ console.log(todos);
+ return (
+
+ {columns.map((column) => (
+ todo.status === column.status)}
+ />
+ ))}
+
+ );
+};
diff --git a/src/components/shared/Container/index.module.css b/src/components/shared/Container/index.module.css
new file mode 100644
index 0000000..04921ef
--- /dev/null
+++ b/src/components/shared/Container/index.module.css
@@ -0,0 +1,4 @@
+.container {
+ width: 90%;
+ margin: 0 auto;
+}
diff --git a/src/components/shared/Container/index.tsx b/src/components/shared/Container/index.tsx
new file mode 100644
index 0000000..b18fdda
--- /dev/null
+++ b/src/components/shared/Container/index.tsx
@@ -0,0 +1,9 @@
+import styles from "./index.module.css";
+
+type Props = {
+ children: React.ReactNode;
+};
+
+export const Container = ({ children }: Props) => {
+ return {children}
;
+};
diff --git a/src/components/ui/Button/index.module.css b/src/components/ui/Button/index.module.css
new file mode 100644
index 0000000..a995aef
--- /dev/null
+++ b/src/components/ui/Button/index.module.css
@@ -0,0 +1,11 @@
+.button {
+ margin-top: 80px;
+ padding: 8px 16px;
+ height: 40px;
+ border: none;
+ background: #000;
+ color: #fff;
+ cursor: pointer;
+ border-radius: 4px;
+ width: fit-content;
+}
diff --git a/src/components/ui/Button/index.tsx b/src/components/ui/Button/index.tsx
new file mode 100644
index 0000000..bf00142
--- /dev/null
+++ b/src/components/ui/Button/index.tsx
@@ -0,0 +1,14 @@
+import styles from "./index.module.css";
+
+type Props = {
+ children: React.ReactNode;
+ onClick?: () => void;
+};
+
+export const Button = ({ children, onClick }: Props) => {
+ return (
+
+ );
+};
diff --git a/src/mocks/handlers.ts b/src/mocks/handlers.ts
index 80c3371..04482e4 100644
--- a/src/mocks/handlers.ts
+++ b/src/mocks/handlers.ts
@@ -11,7 +11,7 @@ type Todo = {
endDate: string;
createdAt: string;
updatedAt: string;
- assignee: string;
+ assignees: string[];
};
let todos: Todo[] = [
@@ -24,7 +24,7 @@ let todos: Todo[] = [
endDate: "2023-06-05",
createdAt: "2023-06-01T09:00:00Z",
updatedAt: "2023-06-05T16:30:00Z",
- assignee: "김철수",
+ assignees: ["김철수", "이민호"],
},
{
id: "2",
@@ -35,7 +35,7 @@ let todos: Todo[] = [
endDate: "2023-06-15",
createdAt: "2023-06-06T10:15:00Z",
updatedAt: "2023-06-10T14:20:00Z",
- assignee: "박지영",
+ assignees: ["박지영", "이민호"],
},
{
id: "3",
@@ -46,7 +46,7 @@ let todos: Todo[] = [
endDate: "2023-06-20",
createdAt: "2023-06-10T11:30:00Z",
updatedAt: "2023-06-10T11:30:00Z",
- assignee: "이민호",
+ assignees: ["이민호"],
},
];
@@ -69,8 +69,8 @@ export const handlers = [
}
if (assignee) {
- filteredTodos = filteredTodos.filter(
- (todo) => todo.assignee === assignee
+ filteredTodos = filteredTodos.filter((todo) =>
+ todo.assignees.includes(assignee)
);
}
@@ -108,6 +108,8 @@ export const handlers = [
todos.push(todo);
+ console.log(todo);
+
return HttpResponse.json(todo, { status: 201 });
}),
diff --git a/src/pages/UI_image/Create.png b/src/pages/UI_image/Create.png
new file mode 100644
index 0000000..27cba8b
Binary files /dev/null and b/src/pages/UI_image/Create.png differ
diff --git a/src/pages/UI_image/Todos.png b/src/pages/UI_image/Todos.png
new file mode 100644
index 0000000..346c234
Binary files /dev/null and b/src/pages/UI_image/Todos.png differ
diff --git a/src/pages/create/index.tsx b/src/pages/create/index.tsx
index 897fd83..0910c45 100644
--- a/src/pages/create/index.tsx
+++ b/src/pages/create/index.tsx
@@ -1,3 +1,323 @@
+import { useState, ChangeEvent, FormEvent } from "react";
+import { useNavigate } from "react-router-dom";
+import { TodoFormData } from "../../types/todo";
+
export default function Create() {
- return Create
;
+ const navigate = useNavigate();
+ const [formData, setFormData] = useState({
+ title: "",
+ content: "",
+ status: "notStarted",
+ startDate: "",
+ endDate: "",
+ assignees: [],
+ });
+ const [assigneeInput, setAssigneeInput] = useState("");
+
+ const handleInputChange = (
+ e: ChangeEvent
+ ) => {
+ const { name, value } = e.target;
+ setFormData({
+ ...formData,
+ [name]: value,
+ });
+ };
+
+ const handleAssigneeInputChange = (e: ChangeEvent) => {
+ setAssigneeInput(e.target.value);
+ };
+
+ const handleAddAssignee = () => {
+ if (assigneeInput.trim()) {
+ setFormData({
+ ...formData,
+ assignees: [...formData.assignees, assigneeInput.trim()],
+ });
+ setAssigneeInput("");
+ }
+ };
+
+ const handleSubmit = async (e: FormEvent) => {
+ e.preventDefault();
+
+ try {
+ const response = await fetch("/api/todos", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(formData),
+ });
+
+ if (response.ok) {
+ navigate("/");
+ } else {
+ alert("할 일 저장에 실패했습니다.");
+ }
+ } catch (error) {
+ console.error("할 일 저장 중 오류 발생:", error);
+ alert("할 일 저장 중 오류가 발생했습니다.");
+ }
+ };
+
+ return (
+
+ );
}
diff --git a/src/pages/todos/index.tsx b/src/pages/todos/index.tsx
index 896b30c..6c8866f 100644
--- a/src/pages/todos/index.tsx
+++ b/src/pages/todos/index.tsx
@@ -1,5 +1,23 @@
-function Todos() {
- return Todos
;
-}
+import { ButtonSection } from "../../components/service/ButtonSection";
+import { Header } from "../../components/service/Header";
+import { TodoSection } from "../../components/service/TodoSection";
+import { Container } from "../../components/shared/Container";
-export default Todos;
+export default function App() {
+ return (
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/types/todo.ts b/src/types/todo.ts
new file mode 100644
index 0000000..16a4435
--- /dev/null
+++ b/src/types/todo.ts
@@ -0,0 +1,24 @@
+type TodoStatus = "notStarted" | "inProgress" | "done";
+
+type Todo = {
+ id: string;
+ title: string;
+ content: string;
+ status: TodoStatus;
+ startDate: string;
+ endDate: string;
+ createdAt: string;
+ updatedAt: string;
+ assignees: string[];
+};
+
+type TodoFormData = {
+ title: string;
+ content: string;
+ status: TodoStatus;
+ startDate: string;
+ endDate: string;
+ assignees: string[];
+};
+
+export type { Todo, TodoStatus, TodoFormData };