File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
import type { Metadata } from 'next';
import Link from 'next/link';
import { Alert } from '@/components/ui/alert';
import { Pagination } from '@/components/ui/pagination';
import { verifySession } from '@/lib/auth/dal';
import { fetchDecorationItems } from '@/lib/data/repositories/decoration-item-repository';
import {
buildDecorationItemHref,
parseDecorationItemQuery,
} from '@/lib/domain/decoration-item-query';
import { DecorationItemListToolbar } from './_components/decoration-item-list-toolbar';
import { DecorationItemSearchBar } from './_components/decoration-item-search-bar';
import { DecorationItemTable } from './_components/decoration-item-table';
import { DecorationItemTypeTabs } from './_components/decoration-item-type-tabs';
export const metadata: Metadata = {
title: '꾸미기 아이템',
};
interface PageProps {
searchParams: Promise<Record<string, string | string[] | undefined>>;
}
/**
* 꾸미기 아이템 목록(시안 ADM_ITM_101).
*
* **셋트아이템은 별도 화면이 없다** — 유형 탭(개별/셋트)만 바꿔 같은 목록·같은 폼을 재사용
* 한다(시안 두 곳에 명시된 사양). `query.itemType`이 곧 그 탭 상태이고, 다른 검색·페이지
* 상태와 마찬가지로 URL(searchParams)이 단일 진실원천이다(`useState`로 들지 않는다).
*
* `verifySession()`을 호출만 하고 반환값을 쓰지 않는 이유는 인증 확인 자체가 목적이기
* 때문이다 — admins의 "본인 계정 삭제 방지" 같은 사용자 조건부 로직이 이 화면에는 없다.
*/
export default async function Page({ searchParams }: PageProps) {
await verifySession();
const query = parseDecorationItemQuery(await searchParams);
const { items, totalCount, typeTotalCount } =
await fetchDecorationItems(query);
const totalPages = Math.max(1, Math.ceil(totalCount / query.pageSize));
// 요청 페이지가 범위를 벗어나면(예: 삭제로 마지막 페이지가 사라짐, 또는 유형 탭 전환으로
// 전체 건수가 줄어듦) 마지막 페이지로 맞춘다 — 표의 순번 계산도 이 값을 기준으로 해야 헤더의
// "현재페이지"와 어긋나지 않는다.
const currentPage = Math.min(query.page, totalPages);
return (
<section className="flex flex-1 flex-col gap-6 p-8">
<h1 className="text-headline-sm text-foreground">꾸미기 아이템 목록</h1>
<DecorationItemTypeTabs query={query} />
<DecorationItemSearchBar query={query} />
<DecorationItemListToolbar query={query} typeTotalCount={typeTotalCount} />
<p className="text-body-md text-foreground-muted">
총 {totalCount}개 | 현재페이지 {currentPage}/{totalPages}
</p>
{items.length === 0 ? (
<Alert
title="등록된 아이템이 없습니다."
actions={
<Link
href={buildDecorationItemHref(query, { keyword: '', page: 1 })}
className="bg-foreground px-4 py-2 text-background"
>
확인
</Link>
}
>
다른 검색어로 다시 시도해 주세요.
</Alert>
) : (
<>
<DecorationItemTable
items={items}
page={currentPage}
pageSize={query.pageSize}
typeTotalCount={typeTotalCount}
/>
<Pagination
currentPage={currentPage}
totalPages={totalPages}
buildHref={(page: number) => buildDecorationItemHref(query, { page })}
/>
</>
)}
</section>
);
}