File name
Commit message
Commit date
File name
Commit message
Commit date
09-03
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}
/>
);
}