🤖

【NextJs14】NextJs14 と 便利なライブラリ【#37Clerk Organization Todos】

2024/03/01に公開

【#37Clerk Organization Todos】

YouTube: https://youtu.be/K98ZCAWwaCw

https://youtu.be/K98ZCAWwaCw

今回は各OrganizaitonごとにTodoを作成し、
表示する部分について実装します。

actions/todo-create.ts
"use server";
import * as z from "zod";
import { CreateTodoSchema } from "@/types/schema";
import { db } from "@/lib/db";
import { revalidatePath } from "next/cache";
import { auth } from "@clerk/nextjs";

export const createTodo = async (values: z.infer<typeof CreateTodoSchema>) => {
  const { userId, orgId } = auth();

  if (!userId || !orgId) {
    return {
      error: "Unauthorized",
    };
  }

  const validatedFields = CreateTodoSchema.safeParse(values);

  if (!validatedFields.success) {
    return {
      error: "Invalid fields",
    };
  }

  const { title } = validatedFields.data;

  const todo = await db.todo.create({
    data: {
      title,
      clerkId: userId,
      orgId,
    },
  });

  console.log(todo);

  revalidatePath("/account");

  return { success: "New todo created!" };
};

ここまで出来ましたら、
新しく「service」というフォルダを作成して
「getTodos」を移動します。

service/todo-get.ts
import { db } from "@/lib/db";

export const getTodos = async () => {
  const todos = await db.todo.findMany({
    orderBy: {
      createdAt: "asc",
    },
  });
  return todos;
};

export const getTodosByOrgId = async (orgId: string | null | undefined) => {
  if (!orgId) {
    return [];
  }

  const todos = await db.todo.findMany({
    where: {
      orgId,
    },
    orderBy: {
      createdAt: "asc",
    },
  });

  return todos;
};

最後に上記の「getTodosByOrgId」を呼び出します。

app/(main)/account/page.tsx
import { auth } from "@clerk/nextjs";
import { formatDistanceToNow } from "date-fns";

import { Separator } from "@/components/ui/separator";

import { TodoForm } from "./_components/todo-form";
import { TodoActions } from "./_components/todo-actions";
import { OrgNavbar } from "./_components/org-navbar";
import { getTodosByOrgId } from "@/service/todo-get";

const AccountPage = async () => {
  const { orgId } = auth();
  const todos = await getTodosByOrgId(orgId);

  return (
    <div className="p-6 w-full h-full">
      <div className="w-full h-full flex flex-col gap-y-3">
        <OrgNavbar />
        <div className="flex flex-col md:flex-row items-center justify-between gap-2">
          <h2 className="text-center md:text-left text-3xl font-bold">Todos</h2>
          <TodoForm />
        </div>
        <Separator />
        <ul className="w-full space-y-3">
          {todos.map((todo, idx) => (
            <li
              key={todo.id}
              className="flex flex-col md:flex-row items-center justify-between gap-x-4 gap-y-2"
            >
              <div className="flex flex-col md:flex-row flex-1 items-center justify-between gap-x-3">
                <p className="text-xl font-semibold">
                  <span className="pr-2">{idx + 1}</span>
                  <span className={todo.isCompleted ? "line-through" : ""}>
                    {todo.title}
                  </span>
                </p>
                <p>
                  {formatDistanceToNow(todo.createdAt, { addSuffix: true })}
                </p>
              </div>
              <TodoActions todoId={todo.id} isCompleted={todo.isCompleted} />
            </li>
          ))}
        </ul>
      </div>
    </div>
  );
};

export default AccountPage;

Discussion