File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
export const POINT_ACCOUNTS_PATH = '/points/accumulated';
export type PointAccountSearchField = 'loginId' | 'studentName';
export const POINT_ACCOUNT_SEARCH_FIELD_OPTIONS: ReadonlyArray<{
value: PointAccountSearchField;
label: string;
}> = [
{ value: 'loginId', label: '아이디' },
{ value: 'studentName', label: '이름' },
];
/** 필터 셀렉트의 "전체" 값 — URL에서는 생략되고 화면에서는 이 값이 선택된다. */
export const FILTER_ALL = 'all';
export const GRADE_OPTIONS = [1, 2, 3] as const;
export const CLASS_OPTIONS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] as const;
export const NUMBER_OPTIONS = Array.from({ length: 30 }, (_, index) => index + 1);
/** 상세내역 팝업 안의 페이지 크기(기획 PTS10300001). */
export const POINT_HISTORY_PAGE_SIZE = 5;
export const POINT_ACCOUNT_PAGE_SIZE_OPTIONS = [10, 30, 50] as const;
export type PointAccountPageSize =
(typeof POINT_ACCOUNT_PAGE_SIZE_OPTIONS)[number];
export const DEFAULT_POINT_ACCOUNT_SEARCH_FIELD: PointAccountSearchField =
'loginId';
export const DEFAULT_POINT_ACCOUNT_PAGE_SIZE: PointAccountPageSize = 10;
const DEFAULT_PAGE = 1;
const MAX_KEYWORD_LENGTH = 100;
const MAX_SCHOOL_NAME_LENGTH = 100;
export type PointAccountQuery = {
schoolName: string;
grade: number | null;
classNo: number | null;
studentNo: number | null;
searchField: PointAccountSearchField;
keyword: string;
page: number;
pageSize: PointAccountPageSize;
};
type RawSearchParams = Record<string, string | string[] | undefined>;
function readParam(params: RawSearchParams, key: string): string | undefined {
const value = params[key];
return Array.isArray(value) ? value[0] : value;
}
function readNumberIn(
params: RawSearchParams,
key: string,
allowed: readonly number[]
): number | null {
const value = Number(readParam(params, key));
return Number.isInteger(value) && allowed.includes(value) ? value : null;
}
function isPointAccountSearchField(
value: string | undefined
): value is PointAccountSearchField {
return (
value !== undefined &&
POINT_ACCOUNT_SEARCH_FIELD_OPTIONS.some((option) => option.value === value)
);
}
function isPointAccountPageSize(value: number): value is PointAccountPageSize {
return (POINT_ACCOUNT_PAGE_SIZE_OPTIONS as readonly number[]).includes(value);
}
export function parsePointAccountQuery(
searchParams: RawSearchParams
): PointAccountQuery {
const searchFieldRaw = readParam(searchParams, 'searchField');
const keywordRaw = readParam(searchParams, 'keyword');
const schoolNameRaw = readParam(searchParams, 'schoolName');
const pageRaw = Number(readParam(searchParams, 'page'));
const pageSizeRaw = Number(readParam(searchParams, 'pageSize'));
return {
schoolName: (schoolNameRaw ?? '').trim().slice(0, MAX_SCHOOL_NAME_LENGTH),
grade: readNumberIn(searchParams, 'grade', GRADE_OPTIONS),
classNo: readNumberIn(searchParams, 'classNo', CLASS_OPTIONS),
studentNo: readNumberIn(searchParams, 'studentNo', NUMBER_OPTIONS),
searchField: isPointAccountSearchField(searchFieldRaw)
? searchFieldRaw
: DEFAULT_POINT_ACCOUNT_SEARCH_FIELD,
keyword: (keywordRaw ?? '').trim().slice(0, MAX_KEYWORD_LENGTH),
page: Number.isInteger(pageRaw) && pageRaw > 0 ? pageRaw : DEFAULT_PAGE,
pageSize: isPointAccountPageSize(pageSizeRaw)
? pageSizeRaw
: DEFAULT_POINT_ACCOUNT_PAGE_SIZE,
};
}
export function buildPointAccountHref(
query: PointAccountQuery,
overrides: Partial<PointAccountQuery> = {}
): string {
const merged = { ...query, ...overrides };
const params = new URLSearchParams();
if (merged.schoolName) {
params.set('schoolName', merged.schoolName);
}
if (merged.grade !== null) {
params.set('grade', String(merged.grade));
}
if (merged.classNo !== null) {
params.set('classNo', String(merged.classNo));
}
if (merged.studentNo !== null) {
params.set('studentNo', String(merged.studentNo));
}
if (merged.searchField !== DEFAULT_POINT_ACCOUNT_SEARCH_FIELD) {
params.set('searchField', merged.searchField);
}
if (merged.keyword) {
params.set('keyword', merged.keyword);
}
if (merged.pageSize !== DEFAULT_POINT_ACCOUNT_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
? `${POINT_ACCOUNTS_PATH}?${queryString}`
: POINT_ACCOUNTS_PATH;
}