File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
import { MERCHANT_DETAIL_VISIBILITY_OPTIONS } from '@/lib/domain/merchant-detail';
export const MERCHANT_DETAILS_PATH = '/system/merchant-details';
export function merchantDetailPath(frcsNo: string): string {
return `${MERCHANT_DETAILS_PATH}/${encodeURIComponent(frcsNo)}`;
}
export function merchantProductNewPath(frcsNo: string): string {
return `${merchantDetailPath(frcsNo)}/products/new`;
}
export function merchantProductPath(frcsNo: string, productSn: string): string {
return `${merchantDetailPath(frcsNo)}/products/${encodeURIComponent(productSn)}`;
}
export const FILTER_ALL = '';
export const MERCHANT_DETAIL_PAGE_SIZE_OPTIONS = [10, 30, 50] as const;
export type MerchantDetailPageSize =
(typeof MERCHANT_DETAIL_PAGE_SIZE_OPTIONS)[number];
export const DEFAULT_MERCHANT_DETAIL_PAGE_SIZE: MerchantDetailPageSize = 10;
const DEFAULT_PAGE = 1;
const MAX_KEYWORD_LENGTH = 100;
const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
/** 오늘 — 로컬 달력 기준 `YYYY-MM-DD`. 서버·브라우저가 같은 규칙으로 계산한다. */
export function todayIsoDate(now: Date = new Date()): string {
const offsetMs = now.getTimezoneOffset() * 60 * 1000;
return new Date(now.getTime() - offsetMs).toISOString().slice(0, 10);
}
/**
* 등록일 기간 규칙(사용자 확정) — 시작일은 오늘을 넘지 못하고, 종료일은 시작일보다 앞설 수 없다.
* 어기는 값은 경계로 당긴다. 화면의 피커 제한과 URL 파싱이 같은 함수를 쓴다.
*/
export function clampDateRange(
startDate: string,
endDate: string,
today: string = todayIsoDate()
): { startDate: string; endDate: string } {
const start = startDate && startDate > today ? today : startDate;
const end = endDate && start && endDate < start ? start : endDate;
return { startDate: start, endDate: end };
}
/** 기획 BO-MAL-001 ① — 업체명·카테고리·노출상태·등록일 네 조건을 AND로 결합한다. */
export type MerchantDetailQuery = {
keyword: string;
category: string;
visibility: string;
startDate: string;
endDate: string;
page: number;
pageSize: MerchantDetailPageSize;
};
type RawSearchParams = Record<string, string | string[] | undefined>;
function readParam(params: RawSearchParams, key: string): string {
const value = params[key];
return (Array.isArray(value) ? value[0] : value) ?? '';
}
function readDate(params: RawSearchParams, key: string): string {
const raw = readParam(params, key);
return ISO_DATE.test(raw) ? raw : '';
}
function isPageSize(value: number): value is MerchantDetailPageSize {
return (MERCHANT_DETAIL_PAGE_SIZE_OPTIONS as readonly number[]).includes(value);
}
export function parseMerchantDetailQuery(
searchParams: RawSearchParams
): MerchantDetailQuery {
const page = Number(readParam(searchParams, 'page'));
const pageSize = Number(readParam(searchParams, 'pageSize'));
const visibility = readParam(searchParams, 'visibility');
return {
keyword: readParam(searchParams, 'keyword').trim().slice(0, MAX_KEYWORD_LENGTH),
// 카테고리는 공통코드라 여기서 허용 목록을 알 수 없다 — 값 그대로 두고 서버가 거른다.
category: readParam(searchParams, 'category').trim().slice(0, MAX_KEYWORD_LENGTH),
visibility: MERCHANT_DETAIL_VISIBILITY_OPTIONS.some(
(option) => option.value === visibility
)
? visibility
: FILTER_ALL,
...clampDateRange(readDate(searchParams, 'startDate'), readDate(searchParams, 'endDate')),
page: Number.isInteger(page) && page > 0 ? page : DEFAULT_PAGE,
pageSize: isPageSize(pageSize) ? pageSize : DEFAULT_MERCHANT_DETAIL_PAGE_SIZE,
};
}
export function buildMerchantDetailHref(
query: MerchantDetailQuery,
overrides: Partial<MerchantDetailQuery> = {}
): string {
const merged = { ...query, ...overrides };
const params = new URLSearchParams();
if (merged.keyword) params.set('keyword', merged.keyword);
if (merged.category) params.set('category', merged.category);
if (merged.visibility) params.set('visibility', merged.visibility);
if (merged.startDate) params.set('startDate', merged.startDate);
if (merged.endDate) params.set('endDate', merged.endDate);
if (merged.pageSize !== DEFAULT_MERCHANT_DETAIL_PAGE_SIZE) {
params.set('pageSize', String(merged.pageSize));
}
if (merged.page !== DEFAULT_PAGE) params.set('page', String(merged.page));
const queryString = params.toString();
return queryString
? `${MERCHANT_DETAILS_PATH}?${queryString}`
: MERCHANT_DETAILS_PATH;
}