'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 { FoxArrowUpRightIcon } from '@fox/core/icons'; 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 })); } 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: '아이디', width: 140 }, { key: 'studentName', header: '학생명', width: 120, emphasis: true }, { key: 'schoolName', header: '학교명', 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) => ( } label={`${row.studentName} 포인트 상세내역`} onAction={() => setDetailAccount(row)} /> ), }, ]; return ( <> title="누적포인트 관리 목록" breadcrumb={[ { label: '홈', href: '/' }, { label: '포인트관리' }, { label: '누적포인트 관리' }, ]} caption="누적포인트 관리 목록" columns={columns} rows={items} rowKey={(row) => row.id} totalCount={totalCount} emptyMessage="검색 결과가 없습니다. 다른 검색어로 다시 시도해 주세요." sorts={ ({ value: String(size), label: `${size}개씩 보기`, }))} value={String(query.pageSize)} onValueChange={(value) => go({ pageSize: Number(value) as PointAccountPageSize }) } /> } filter={
{ if (event.key === 'Enter') { go({ schoolName: schoolNameDraft }); } }} />
go({ grade: parseSelected(value) })} />
go({ classNo: parseSelected(value) })} />
go({ studentNo: parseSelected(value) })} />
} searchFields={POINT_ACCOUNT_SEARCH_FIELD_OPTIONS.map((option) => ({ value: option.value, label: option.label, }))} searchField={query.searchField} keyword={query.keyword} searchPlaceholder="검색어를 입력하세요." toolbarActions={ { setSchoolNameDraft(''); router.replace(POINT_ACCOUNTS_PATH); }} /> } page={currentPage} totalPages={totalPages} buildHref={(patch) => buildPointAccountHref(query, { page: Number(patch.page ?? 1) }) } onQueryChange={(patch) => { if (patch.keyword !== undefined || patch.searchField !== undefined) { go({ schoolName: schoolNameDraft, keyword: String(patch.keyword ?? ''), searchField: (patch.searchField ?? query.searchField) as PointAccountSearchField, }); } }} /> {detailAccount && ( setDetailAccount(null)} /> )} ); }