🐕
【NextJs14】NextJs14 と 便利なライブラリ【#38Clerk Organization List】
【#38Clerk Organization List】
YouTube: https://youtu.be/vvsuLXygTHA
今回はサイドバーを作成して、Organizationの一覧を表示します。
/app/(main)/account/page.tsx
import { auth } from "@clerk/nextjs";
import { formatDistanceToNow } from "date-fns";
import { getTodosByOrgId } from "@/service/todo-get";
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 { Sidebar } from "./_components/sidebar";
const AccountPage = async () => {
const { orgId } = auth();
const todos = await getTodosByOrgId(orgId);
return (
<div className="p-6 w-full h-full flex gap-x-3">
<Sidebar />
<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;
/app/(main)/account/_components/sidebar/index.tsx
import { List } from "./list";
export const Sidebar = () => {
return (
<div className="h-full w-[60px] flex flex-col px-3 py-1 gap-y-4 text-white border-r">
<List />
</div>
);
};
/app/(main)/account/_components/sidebar/list.tsx
"use client";
import { useOrganizationList } from "@clerk/nextjs";
import { Item } from "./item";
export const List = () => {
const { userMemberships } = useOrganizationList({
userMemberships: {
infinite: true,
},
});
if (!userMemberships) return null;
return (
<ul className="space-y-4">
{userMemberships.data?.map((org) => (
<Item
key={org.organization.id}
id={org.organization.id}
name={org.organization.name}
imageUrl={org.organization.imageUrl}
/>
))}
</ul>
);
};
こちらの「li」タグですが動画では「div」を設定していますので、
次回の動画で修正します。
/app/(main)/account/_components/sidebar/item.tsx
import Image from "next/image";
import { useOrganization, useOrganizationList } from "@clerk/nextjs";
import { cn } from "@/lib/utils";
import { OwnTooltipWrapper } from "@/components/own-tooltip-wrapper";
interface ItemProps {
id: string;
name: string;
imageUrl: string;
}
export const Item = ({ id, name, imageUrl }: ItemProps) => {
const { organization } = useOrganization();
const { setActive } = useOrganizationList();
const isActive = organization?.id === id;
const onClick = () => {
if (!setActive) return;
setActive({ organization: id });
};
return (
<li className="aspect-square relative">
<OwnTooltipWrapper label={name} side="right" sideOffset={10}>
<Image
src={imageUrl}
alt={name}
fill
onClick={onClick}
className={cn(
"rounded-md cursor-pointer opacity-75 hover:opacity-100 transition",
isActive && "opacity-100"
)}
/>
</OwnTooltipWrapper>
</li>
);
};
Discussion