'use client'; import { useRouter } from 'next/navigation'; import { useState } from 'react'; import { FoxButton } from '@fox/core/components/fox-button'; import { FoxInput } from '@fox/core/components/fox-input'; import { FoxListContainer, type FoxListColumn, } from '@fox/core/components/fox-list-container'; import { FoxSelect } from '@fox/core/components/fox-select'; import { FoxSelectText } from '@fox/core/components/fox-select-text'; import { FoxIconButton } from '@fox/core/components/fox-icon-button'; import { formatOptionalValue, formatPoint, type PointAccount, } from '@/lib/domain/point-account'; import { CLASS_OPTIONS, FILTER_ALL, GRADE_OPTIONS, NUMBER_OPTIONS, POINT_ACCOUNTS_PATH, POINT_ACCOUNT_PAGE_SIZE_OPTIONS, POINT_ACCOUNT_SEARCH_FIELD_OPTIONS, buildPointAccountHref, type PointAccountPageSize, type PointAccountQuery, type PointAccountSearchField, } from '@/lib/domain/point-account-query'; import { PointDetailModal } from './point-detail-modal'; import styles from './point-account-filter.module.scss'; interface PointAccountListProps { items: PointAccount[]; query: PointAccountQuery; currentPage: number; totalPages: number; totalCount: number; } function numberOptions(values: readonly number[], suffix: string) { return [ { value: FILTER_ALL, label: `${suffix} 전체` }, ...values.map((value) => ({ value: String(value), label: `${value}${suffix}`, })), ]; } export function PointAccountList({ items, query, currentPage, totalPages, totalCount, }: PointAccountListProps) { const router = useRouter(); const [detailAccount, setDetailAccount] = useState(null); const [schoolNameDraft, setSchoolNameDraft] = useState(query.schoolName); function go(patch: Partial) { router.replace(buildPointAccountHref(query, { ...patch, page: 1 })); } // 검색 초기화 — 주소를 되돌리는 것만으론 아직 적용 안 한 입력값이 남는다. 목록 컨테이너를 // 다시 마운트해 검색 상자의 초안까지 비운다. const [resetCount, setResetCount] = useState(0); function resetSearch() { setSchoolNameDraft(''); setResetCount((count) => count + 1); router.replace(POINT_ACCOUNTS_PATH); } function selectedNumber(value: number | null) { return value === null ? FILTER_ALL : String(value); } function parseSelected(value: string): number | null { return value === FILTER_ALL ? null : Number(value); } const columns: FoxListColumn[] = [ { key: 'no', header: '번호', width: 80, render: (_row, index) => (currentPage - 1) * query.pageSize + index + 1, }, { key: 'loginId', header: '아이디', align: 'start', width: 140 }, { key: 'studentName', header: '학생명', align: 'start', width: 120, emphasis: true }, { key: 'schoolName', header: '학교명', align: 'start', width: 160 }, { key: 'grade', header: '학년', width: 80, align: 'center' }, { key: 'classNo', header: '반', width: 80, align: 'center' }, { key: 'studentNo', header: '번호', width: 80, align: 'center' }, { key: 'totalPoint', header: '총 포인트', width: 120, align: 'end', render: (row) => formatPoint(row.totalPoint), }, { key: 'deductedPoint', header: '차감 포인트', width: 120, align: 'end', render: (row) => formatPoint(row.deductedPoint), }, { key: 'usedPoint', header: '사용 포인트', width: 120, align: 'end', render: (row) => formatPoint(row.usedPoint), }, { key: 'currentPoint', header: '현재 포인트', width: 120, align: 'end', render: (row) => formatPoint(row.currentPoint), }, { key: 'updatedAt', header: '수정일시', width: 160, render: (row) => formatOptionalValue(row.updatedAt), }, { key: 'siteName', header: '구분(사이트)', width: 160 }, { key: 'actions', header: '관리', width: 100, render: (row) => (