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 ( +
+ logo +
+ ); +}; 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 ( +
+
+

+ 할 일 추가 +

+ + {/* 제목 */} +
+ + +
+ + {/* 내용 */} +
+ +