(
+ (acc, todo) => {
+ if (todo.isCompleted) {
+ acc.completed.push(todo);
+ } else {
+ acc.incomplete.push(todo);
+ }
+ return acc;
+ },
+ {
+ completed: [],
+ incomplete: [],
+ }
+ );
+
+ return separatedTodos;
+}
+
+export default async function TodoList() {
+ const separatedTodos = await getAllTodoList();
+
+ return (
+ <>
+
+
+
+ 할 일이 없어요.
+
+ TODO를 새롭게 추가해주세요!
+ >
+ }
+ />
+
+
+
+
+ 아직 다 한 일이 없어요.
+
+ 해야 할 일을 체크해보세요!
+ >
+ }
+ />
+
+
+ >
+ );
+}
diff --git a/src/hooks/useCompleteTodo.tsx b/src/hooks/useCompleteTodo.tsx
new file mode 100644
index 00000000..9d04c54e
--- /dev/null
+++ b/src/hooks/useCompleteTodo.tsx
@@ -0,0 +1,53 @@
+import revalidateTodo from "@/actions/revalidate-todo.action";
+import { TodoDetailData } from "@/types";
+import { useEffect, useState } from "react";
+
+export default function useCompleteTodo(
+ id: number,
+ todoData: TodoDetailData | null
+) {
+ const [isLoading, setIsLoading] = useState(false);
+ const [error, setError] = useState(false);
+
+ const onClickComplete = async (taskId: number, data: TodoDetailData) => {
+ setIsLoading(true);
+
+ try {
+ const response = await fetch(
+ `${process.env.NEXT_PUBLIC_API_URL}/items/${taskId}`,
+ {
+ method: "PATCH",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(data),
+ }
+ );
+
+ if (!response.ok) {
+ alert(`완료하지 못했습니다`);
+ setError(true);
+ return;
+ }
+
+ revalidateTodo();
+ } catch (error) {
+ alert("완료하지 못했습니다");
+ setError(true);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ useEffect(() => {
+ if (!todoData) return;
+
+ onClickComplete(id, todoData);
+ }, [id, todoData]);
+
+ return {
+ isLoading,
+ error,
+ onClickComplete,
+ };
+}
diff --git a/src/types.ts b/src/types.ts
new file mode 100644
index 00000000..97413ff2
--- /dev/null
+++ b/src/types.ts
@@ -0,0 +1,33 @@
+import { StaticImageData } from "next/image";
+import { ReactNode } from "react";
+
+export interface TodoData {
+ id: number;
+ name: string;
+ isCompleted: boolean;
+}
+
+export interface TodoDetailData {
+ name: string;
+ isCompleted: boolean;
+ memo: string;
+ imageUrl: string;
+}
+
+export interface SeparatedTodos {
+ completed: TodoData[];
+ incomplete: TodoData[];
+}
+
+export interface TodoSectionProps {
+ img: StaticImageData;
+ list: TodoData[];
+ emptyImg: StaticImageData;
+ imgAlt: string;
+ emptyMsg: ReactNode;
+}
+
+export interface ActionState {
+ status: boolean;
+ error: string;
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 00000000..c1334095
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,27 @@
+{
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}