Skip to content

Commit

Permalink
ページ管理機能の型を修正し、検索パラメータのロジックを整理しました。また、新しいページリストコンポーネントとユーザー情報コンポーネントを追…
Browse files Browse the repository at this point in the history
…加しました。 (#645)
  • Loading branch information
ttizze authored Feb 28, 2025
2 parents b918d55 + 890b677 commit 4b1f31b
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 170 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { PageCard } from "@/app/[locale]/components/page-card";
import { fetchPaginatedPublicPagesWithInfo } from "@/app/[locale]/db/queries.server";
import { fetchUserByHandle } from "@/app/db/queries.server";
import { getCurrentUser } from "@/auth";
import { getGuestId } from "@/lib/get-guest-id";
import { notFound } from "next/navigation";
import { PaginationControls } from "./pagination-controls";
interface PageListServerProps {
handle: string;
page: number;
locale: string;
}

export async function PageListServer({
handle,
page,
locale,
}: PageListServerProps) {
const currentUser = await getCurrentUser();
const guestId = !currentUser ? await getGuestId() : undefined;
const isOwner = currentUser?.handle === handle;

const pageOwner = await fetchUserByHandle(handle);
if (!pageOwner) {
return notFound();
}
const { pagesWithInfo, totalPages, currentPage } =
await fetchPaginatedPublicPagesWithInfo({
page: page,
pageSize: 9,
currentUserId: currentUser?.id,
currentGuestId: guestId,
pageOwnerId: pageOwner.id,
onlyUserOwn: true,
locale,
});

if (pagesWithInfo.length === 0) {
return (
<p className="text-center text-gray-500 mt-10">
{isOwner ? "You haven't created any pages yet." : "No pages yet."}
</p>
);
}

return (
<>
<div className="grid gap-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
{pagesWithInfo.map((pageWithInfo) => (
<PageCard
key={pageWithInfo.id}
pageCard={pageWithInfo}
pageLink={`/user/${handle}/page/${pageWithInfo.slug}`}
userLink={`/user/${handle}`}
showOwnerActions={isOwner}
/>
))}
</div>

<div className="mt-8 flex justify-center">
<PaginationControls currentPage={currentPage} totalPages={totalPages} />
</div>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { fetchUserByHandle } from "@/app/db/queries.server";
import { getCurrentUser } from "@/auth";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Link } from "@/i18n/routing";
import Linkify from "linkify-react";
import { Settings } from "lucide-react";
import {
fetchFollowerList,
fetchFollowingList,
getFollowCounts,
} from "../db/queries.server";
import { FollowButton } from "./follow-button";
import { FollowStats } from "./follow-stats";

export async function UserInfo({
handle,
}: {
handle: string;
}) {
const pageOwner = await fetchUserByHandle(handle);
if (!pageOwner) {
const error = new Error("Unauthorized");
error.message = "Unauthorized";
throw error;
}

const currentUser = await getCurrentUser();

const isOwner = currentUser?.handle === handle;

const followCounts = await getFollowCounts(pageOwner.id);
const followerList = await fetchFollowerList(pageOwner.id);
const followingList = await fetchFollowingList(pageOwner.id);

return (
<Card className="mb-8">
<CardHeader className="pb-4">
<div className="flex w-full flex-col md:flex-row">
<div>
<Link href={`${pageOwner.image}`}>
<Avatar className="w-20 h-20 md:w-24 md:h-24">
<AvatarImage src={pageOwner.image} alt={pageOwner.name} />
<AvatarFallback>
{pageOwner.name.charAt(0).toUpperCase()}
</AvatarFallback>
</Avatar>
</Link>
</div>
<div className="mt-2 md:mt-0 md:ml-4 flex items-center justify-between w-full">
<div>
<CardTitle className="text-xl md:text-2xl font-bold">
{pageOwner.name}
</CardTitle>
<div>
<CardDescription className="text-sm text-gray-500">
@{pageOwner.handle}
</CardDescription>
<FollowStats
followingCount={followCounts.following}
followersCount={followCounts.followers}
followingList={followingList.map((item) => ({
handle: item.following.handle,
name: item.following.name,
image: item.following.image,
}))}
followerList={followerList.map((item) => ({
handle: item.follower.handle,
name: item.follower.name,
image: item.follower.image,
}))}
/>
</div>
</div>

{isOwner ? (
<Link href={`/user/${pageOwner.handle}/edit`}>
<Button
variant="secondary"
className="flex items-center rounded-full"
>
<Settings className="w-4 h-4" />
<span className="ml-2 text-sm">Edit Profile</span>
</Button>
</Link>
) : (
<FollowButton targetUserId={pageOwner.id} />
)}
</div>
</div>
</CardHeader>

<CardContent className="mt-4">
<Linkify options={{ className: "underline" }}>
{pageOwner.profile}
</Linkify>
</CardContent>
</Card>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { PageManagementTabClient } from "./client";
interface PageManagementTabProps {
currentUserId: string;
locale: string;
page: string;
page: number;
query: string;
handle: string;
}
Expand All @@ -18,13 +18,7 @@ export async function PageManagementTab({
handle,
}: PageManagementTabProps) {
const { pagesWithTitle, totalPages, currentPage } =
await fetchPaginatedOwnPages(
currentUserId,
locale,
Number(page),
10,
query,
);
await fetchPaginatedOwnPages(currentUserId, locale, page, 10, query);
const pagesWithTitleAndViewData = await Promise.all(
pagesWithTitle.map(async (pageData) => {
const path = `/user/${handle}/page/${pageData.slug}`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { getCurrentUser } from "@/auth";
import { Skeleton } from "@/components/ui/skeleton";
import dynamic from "next/dynamic";
import { redirect } from "next/navigation";

import { createLoader, parseAsInteger, parseAsString } from "nuqs/server";
import type { SearchParams } from "nuqs/server";
const PageManagementTab = dynamic(
() =>
import("./components/page-management-tab/server").then(
Expand All @@ -22,22 +23,26 @@ const PageManagementTab = dynamic(
),
},
);

const searchParamsSchema = {
page: parseAsInteger.withDefault(1),
query: parseAsString.withDefault(""),
};
const loadSearchParams = createLoader(searchParamsSchema);

export default async function PageManagementPage({
params,
searchParams,
}: {
params: Promise<{ locale: string }>;
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
searchParams: Promise<SearchParams>;
}) {
const currentUser = await getCurrentUser();
if (!currentUser || !currentUser.id) {
return redirect("/auth/login");
}
const { locale } = await params;
const { page = "1", query = "" } = await searchParams;
if (typeof page !== "string" || typeof query !== "string") {
throw new Error("Invalid page or query");
}
const { page, query } = await loadSearchParams(searchParams);

return (
<div className="mx-auto max-w-4xl py-10">
Expand Down
Loading

0 comments on commit 4b1f31b

Please sign in to comment.