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
File name
Commit message
Commit date
import type { Metadata } from 'next';
import { verifySession } from '@/lib/auth/dal';
import {
fetchCodeDetails,
fetchCodeGroups,
} from '@/lib/data/repositories/common-code-repository';
import type { CommonCodeGroup } from '@/lib/domain/common-code';
import {
COMMON_CODE_DETAIL_PAGE_SIZE,
COMMON_CODE_PAGE_SIZE,
parseCommonCodeQuery,
type CommonCodeQuery,
} from '@/lib/domain/common-code-query';
import { CodeManagement } from './_components/code-management';
export const metadata: Metadata = {
title: '코드관리',
};
// cookies()로 이미 동적이지만, 정적 프리렌더로 데이터가 빌드 산출물에 박히는 경로를
// 원천 차단하기 위해 명시적으로 강제한다.
export const dynamic = 'force-dynamic';
interface PageProps {
searchParams: Promise<Record<string, string | string[] | undefined>>;
}
/** 부분일치 검색 — 백엔드가 완전일치만 지원해 여기서 거른다(Repository 주석 참조). */
function filterGroups(
groups: CommonCodeGroup[],
query: CommonCodeQuery
): CommonCodeGroup[] {
const keyword = query.keyword.trim().toLowerCase();
if (!keyword) {
return groups;
}
return groups.filter((group) => {
const target = query.searchField === 'comCd' ? group.comCd : group.name;
return target.toLowerCase().includes(keyword);
});
}
/** 1-based 페이지를 잘라낸다. 범위를 벗어난 페이지는 마지막 페이지로 맞춘다. */
function paginate<T>(rows: T[], page: number, pageSize: number) {
const totalPages = Math.max(1, Math.ceil(rows.length / pageSize));
const currentPage = Math.min(Math.max(page, 1), totalPages);
const offset = (currentPage - 1) * pageSize;
return {
rows: rows.slice(offset, offset + pageSize),
currentPage,
totalPages,
totalCount: rows.length,
};
}
/**
* 코드관리 — 기획 SYS_COD_001, 시안 관리자페이지(QsFVFFUKGH68xAPVkF3SJ9) 5402:13399
*
* 좌측 공통코드를 고르면 우측 상세코드 목록이 그 그룹으로 갱신된다. 선택·검색·두 목록의 페이지가
* 모두 URL에 실려, 이 서버 컴포넌트가 매 요청 그 조건대로 데이터를 만들어 넘긴다.
*
* **검색·페이징을 서버에서 처리한다** — 백엔드가 두 목록 모두 전체를 반환하고 검색도 완전일치라
* (Repository 주석 참조), 부분일치 필터와 페이지 자르기를 여기서 한다. 전체를 받으므로 총건수는
* 정확하다(다른 목록 화면의 하한값 보정이 여기에는 필요 없다).
*
* **선택된 공통코드가 목록에 없으면 첫 행으로 대체한다** — 검색으로 걸러졌거나 삭제된 코드가
* URL에 남아 있으면 우측이 영영 비어 보이기 때문이다.
*/
export default async function Page({ searchParams }: PageProps) {
await verifySession();
const query = parseCommonCodeQuery(await searchParams);
const groups = filterGroups(await fetchCodeGroups(), query);
const groupPage = paginate(groups, query.page, COMMON_CODE_PAGE_SIZE);
const selectedComCd =
groups.find((group) => group.comCd === query.comCd)?.comCd ??
groupPage.rows[0]?.comCd ??
null;
const details = selectedComCd ? await fetchCodeDetails(selectedComCd) : [];
const detailPage = paginate(
details,
query.detailPage,
COMMON_CODE_DETAIL_PAGE_SIZE
);
return (
<CodeManagement
query={query}
groups={groupPage.rows}
groupTotalCount={groupPage.totalCount}
groupPage={groupPage.currentPage}
groupTotalPages={groupPage.totalPages}
selectedComCd={selectedComCd}
details={detailPage.rows}
detailTotalCount={detailPage.totalCount}
detailPage={detailPage.currentPage}
detailTotalPages={detailPage.totalPages}
// 새 상세코드의 기본 정렬번호 — 시안 등록 팝업이 기존 개수 다음 번호를 채워 둔다.
nextSortSeq={details.length + 1}
/>
);
}