| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- 'use client';
- // D6 §5.1 — 종목행(StockRow) 열 구성 빌더 + StockDataTable 편의 래퍼.
- // 한 줄 완결: 종목명+코드 | 현재가 | 등락률(색+부호) | 거래대금 | 시가총액 | 시장.
- // 정렬: 종목명·현재가·등락률·거래대금·시가총액 헤더 클릭 → SSR 재조회(URL sort/order). 기본 시가총액 desc.
- // 거래대금/시총 등 우선순위 낮은 열은 mobile drop (data-priority='low').
- import Link from 'next/link';
- import { useRouter } from 'next/navigation';
- import { useCallback, useTransition } from 'react';
- import DataTable, { DataTableColumn } from '@/components/table/DataTable';
- import PriceChange from '@/components/table/PriceChange';
- import { StockListRow, StockSortKey, StockSortOrder } from '@/types/stock';
- import { formatKrwCompact, formatNumber, tradingStatusLabel } from '@/lib/utils/stock';
- // 활성 정렬 컬럼만 방향, 나머지 정렬 가능 컬럼은 null(비활성)
- function colSort(key: StockSortKey, activeSort: StockSortKey, order: StockSortOrder): StockSortOrder|null {
- return key === activeSort ? order : null;
- }
- export function buildStockColumns(sort: StockSortKey, order: StockSortOrder): DataTableColumn<StockListRow>[] {
- return [
- {
- key: 'name',
- header: '종목명',
- align: 'left',
- priority: 'high',
- sort: colSort('name', sort, order),
- cellClassName: 'data-table__cell--name',
- cell: (row) => {
- const status = tradingStatusLabel(row.tradingStatus);
- return (
- <Link href={`/stock/${row.code}`} className='stock-cell'>
- <span className='stock-cell__name'>{row.name}</span>
- <span className='stock-cell__code'>{row.code}</span>
- {status && (
- <span className={`stock-badge stock-badge--${row.tradingStatus.toLowerCase()}`}>{status}</span>
- )}
- </Link>
- );
- }
- },
- {
- key: 'price',
- header: <>현재가 <small>(전일종가)</small></>,
- align: 'right',
- priority: 'high',
- sort: colSort('price', sort, order),
- cell: (row) => formatNumber(row.closePrice)
- },
- {
- key: 'change',
- header: '등락률',
- align: 'right',
- priority: 'high',
- sort: colSort('change', sort, order),
- cell: (row) => <PriceChange rate={row.changeRate} />
- },
- {
- key: 'value',
- header: '거래대금',
- align: 'right',
- priority: 'low',
- sort: colSort('value', sort, order),
- cell: (row) => formatKrwCompact(row.tradingValue)
- },
- {
- key: 'cap',
- header: '시가총액',
- align: 'right',
- priority: 'low',
- sort: colSort('cap', sort, order),
- cell: (row) => formatKrwCompact(row.marketCap)
- },
- {
- key: 'market',
- header: '시장',
- align: 'center',
- priority: 'medium',
- cell: (row) => <span className={`stock-badge stock-badge--${row.market.toLowerCase()}`}>{row.market}</span>
- }
- ];
- }
- interface StockDataTableProps {
- rows: StockListRow[];
- sort: StockSortKey;
- order: StockSortOrder;
- // 정렬 URL 재구성 시 유지할 현재 필터
- market?: string;
- q?: string;
- caption?: string;
- emptyMessage?: string;
- }
- // 종목 목록 편의 래퍼 — 정렬 헤더 클릭 시 sort/order URL 로 SSR 재조회(필터 유지·페이지 1로 리셋).
- // useTransition 으로 재조회 중 현재 표 유지(스켈레톤 플래시 없이) + 흐림 표시.
- export default function StockDataTable({ rows, sort, order, market, q, caption, emptyMessage }: StockDataTableProps)
- {
- const router = useRouter();
- const [isPending, startTransition] = useTransition();
- const handleSort = useCallback((key: string) => {
- const k = key as StockSortKey;
- // 같은 컬럼 재클릭 → 방향 토글, 새 컬럼 → 기본 방향(종목명 asc, 숫자열 desc)
- const nextOrder: StockSortOrder = k === sort ? (order === 'desc' ? 'asc' : 'desc') : (k === 'name' ? 'asc' : 'desc');
- const params = new URLSearchParams();
- if (market) {
- params.set('market', market);
- }
- if (q) {
- params.set('q', q);
- }
- params.set('sort', k);
- params.set('order', nextOrder);
- startTransition(() => {
- router.push(`/stock?${params.toString()}`);
- });
- }, [router, sort, order, market, q]);
- return (
- <div className={isPending ? 'data-table--pending' : undefined}>
- <DataTable<StockListRow>
- caption={caption ?? '종목 목록 — 종목명, 현재가(전일 종가), 등락률, 거래대금, 시가총액, 시장'}
- columns={buildStockColumns(sort, order)}
- rows={rows}
- rowKey={(row) => row.code}
- onSort={handleSort}
- emptyMessage={emptyMessage}
- />
- </div>
- );
- }
|