refactor(FE-347): Tanstack Table 도입 - #7
Conversation
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (1)
통합 개요TanStack React-Table 라이브러리를 추가하고 새로운 AdminDataTable 컴포넌트를 도입하여 여러 관리자 페이지에서 페이지네이션, 정렬, 검색 기능을 일관되게 지원하도록 리팩토링했습니다. API 계층을 업데이트하여 구조화된 페이지 응답을 반환하고, 상태 관리 훅을 확장하여 페이지네이션 및 정렬 상태를 명시적으로 처리합니다. 변경 사항
시퀀스 다이어그램sequenceDiagram
participant UI as UI 컴포넌트
participant Hook as 상태 관리 훅
participant API as API 계층
participant Server as 서버
UI->>Hook: onSortingChange / onPaginationChange 호출
activate Hook
Hook->>Hook: 정렬/페이지네이션 상태 업데이트
Hook->>Hook: serializeSortingState로 정렬 상태를 문자열로 변환
Hook->>API: fetchData(page, size, sort, keyword)
deactivate Hook
activate API
API->>API: URL 쿼리 파라미터 구성 (page, size, sort, keyword)
API->>Server: GET /admin/resource?page=0&size=50&sort=id,asc&keyword=...
deactivate API
activate Server
Server->>Server: 페이지네이션/정렬 적용하여 데이터 조회
Server-->>API: AdminPageResponse { content, page, size, totalElements, totalPages, hasNext }
deactivate Server
activate API
API-->>Hook: 페이지 응답 반환
deactivate API
activate Hook
Hook->>Hook: 페이지 데이터 및 페이지네이션 상태 업데이트
Hook-->>UI: 업데이트된 상태 반환
deactivate Hook
activate UI
UI->>UI: AdminDataTable에 columns, data, sorting, pagination 전달
UI->>UI: 테이블 렌더링 (헤더, 바디, 페이지네이션 컨트롤)
UI-->>UI: 사용자에게 표시
deactivate UI
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (10)
src/api/coupon/get-coupon-codes.ts (1)
4-28: 쿼리 파라미터 빌드 로직 공통화 고려
getCouponCodes,getCoupons,getIssuedCoupons등에서page,size,sort,keyword파라미터 빌드 로직이 동일하게 반복됩니다. 공통 유틸리티 함수로 추출하면 유지보수가 편해집니다.♻️ 공통 유틸리티 예시
// src/api/common/query-params.ts interface BasePageQuery { page?: number; size?: number; sort?: string; keyword?: string; } export function buildPageParams(query: BasePageQuery): URLSearchParams { const params = new URLSearchParams(); params.set("page", String(query.page ?? 0)); params.set("size", String(query.size ?? 50)); if (query.sort) params.set("sort", query.sort); if (query.keyword?.trim()) params.set("keyword", query.keyword.trim()); return params; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/api/coupon/get-coupon-codes.ts` around lines 4 - 28, Multiple API functions (getCouponCodes, getCoupons, getIssuedCoupons) repeat identical page/size/sort/keyword URLSearchParams building; extract that into a shared utility (e.g., buildPageParams or buildPageQuery) that accepts a BasePageQuery-like object and returns a URLSearchParams, then replace the inline params construction in getCouponCodes with a call to that utility and use its toString() when composing the request URL; ensure the utility applies the same defaults (page -> 0, size -> 50) and trims/guards keyword and only sets sort/keyword when present so existing behavior of requestApi(`/admin/coupons/code?${params.toString()}`, { method: "GET" }) remains unchanged.src/page/point/hooks/usePointPageState.ts (1)
155-165:void fetchLedger(...)패턴과eslint-disable주석마운트 시에만 fetch하려는 의도가 명확하며,
void로 floating promise를 명시적으로 무시하고 있습니다. 다만 이 패턴이 여러 hook에서 반복될 경우, 커스텀useMount유틸로 추출하는 것도 고려해볼 수 있습니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/page/point/hooks/usePointPageState.ts` around lines 155 - 165, The useEffect currently calls a floating promise via void fetchLedger(...) and disables eslint for deps; extract this mount-only behavior into a reusable hook (e.g., create a useMount utility) and call fetchLedger from that hook instead of the anonymous useEffect to avoid repeating the void pattern and eslint-disable. Update usePointPageState to import and use useMount(() => { fetchLedger(ledgerPagination, ledgerMemberKeyword, ledgerTransactionType, ledgerSorting, ledgerFrom, ledgerTo) }) (or wrap fetchLedger call in an async IIFE inside useMount) so the mount-only invocation is explicit and you can remove the eslint-disable and the void usage. Ensure the referenced symbols are fetchLedger and usePointPageState (and the new useMount helper) so reviewers can locate changes.src/api/coupon/types.ts (1)
43-68: 페이지 응답 타입에 제네릭 베이스 타입 도입 고려
AdminCouponPageResponse,AdminCouponCodePageResponse,AdminIssuedCouponPageResponse모두 동일한 페이징 필드(page,size,totalElements,totalPages,hasNext)를 반복하고 있습니다. PR 전체적으로 이벤트, 멤버, 결제, 포인트 등에서도 동일한 패턴이 사용될 것으로 보이므로, 제네릭 베이스 타입을 정의하면 중복을 줄일 수 있습니다.♻️ 제네릭 페이지 응답 타입 제안
+// 공통 페이지 응답 타입 (예: src/api/common/types.ts) +export interface AdminPageResponse<T> { + content: T[] + page: number + size: number + totalElements: number + totalPages: number + hasNext: boolean +} + -export interface AdminCouponPageResponse { - content: AdminCoupon[] - page: number - size: number - totalElements: number - totalPages: number - hasNext: boolean -} +export type AdminCouponPageResponse = AdminPageResponse<AdminCoupon> - -export interface AdminCouponCodePageResponse { - content: AdminCouponCode[] - page: number - size: number - totalElements: number - totalPages: number - hasNext: boolean -} +export type AdminCouponCodePageResponse = AdminPageResponse<AdminCouponCode> - -export interface AdminIssuedCouponPageResponse { - content: AdminIssuedCoupon[] - page: number - size: number - totalElements: number - totalPages: number - hasNext: boolean -} +export type AdminIssuedCouponPageResponse = AdminPageResponse<AdminIssuedCoupon>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/api/coupon/types.ts` around lines 43 - 68, Multiple page response interfaces (AdminCouponPageResponse, AdminCouponCodePageResponse, AdminIssuedCouponPageResponse) duplicate the same paging fields; introduce a generic base like PageResponse<T> that defines content: T[], page, size, totalElements, totalPages, hasNext, then replace each specific interface with type aliases or extends (e.g., type AdminCouponPageResponse = PageResponse<AdminCoupon>) using the existing domain types AdminCoupon, AdminCouponCode, AdminIssuedCoupon; update any other similar page types (events, members, payments, points) to use PageResponse<T> to remove duplication.src/page/payment/hooks/usePaymentPageState.ts (1)
282-301: Pagination 변경 핸들러의 중복 로직을 유틸로 추출하면 좋겠습니다.
handlePaymentPaginationChange와handleTransactionPaginationChange는 boundary check 로직(pageIndex < 0,pageIndex >= totalPages)이 거의 동일하며, 이 패턴이 coupon 쪽에도 3번 반복됩니다. 공통 헬퍼 함수를 만들어 재사용하면 유지보수가 편해질 것입니다.♻️ 헬퍼 예시
function createPaginationChangeHandler( currentPagination: PaginationState, totalPages: number | undefined, setPagination: (p: PaginationState) => void, onFetch: (p: PaginationState) => void, ) { return (updater: Updater<PaginationState>): void => { const next = functionalUpdate(updater, currentPagination) if (next.pageIndex < 0) return if (totalPages !== undefined && next.pageIndex >= totalPages) return setPagination(next) onFetch(next) } }Also applies to: 394-415
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/page/payment/hooks/usePaymentPageState.ts` around lines 282 - 301, Extract the duplicated boundary-check + update + fetch logic into a reusable helper (e.g., createPaginationChangeHandler) and replace handlePaymentPaginationChange and handleTransactionPaginationChange (and the three coupon handlers) to call that helper; the helper should accept currentPagination (paymentPagination), totalPages (paymentData?.totalPages), setPagination (setPaymentPagination) and onFetch (a wrapper for fetchPayments with paymentYearSemester/paymentStatus/paymentSorting/paymentMemberKeyword bound) and perform the functionalUpdate, pageIndex < 0 and pageIndex >= totalPages checks, then call setPagination and onFetch with the next pagination.src/components/admin/table/AdminDataTable.tsx (1)
73-76:text-right문자열 포함 여부로 정렬 버튼 위치를 결정하는 것은 다소 취약합니다.
columnMeta?.headerClassName?.includes("text-right")에 의존하면, className 변경 시 정렬 버튼 정렬이 깨질 수 있습니다.AdminColumnMeta에align: "left" | "right"같은 명시적 필드를 추가하는 것을 고려해 보세요.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/admin/table/AdminDataTable.tsx` around lines 73 - 76, The current layout logic in AdminDataTable.tsx relies on checking columnMeta?.headerClassName?.includes("text-right") to position the sort button, which is brittle; update the AdminColumnMeta type to add an explicit align?: "left" | "right" (or required align) field, update any places that construct columnMeta to set align, and change the rendering logic in AdminDataTable (the code around className={cn(... columnMeta?.headerClassName?.includes("text-right") && "ml-auto flex", ...)}) to use columnMeta.align === "right" instead of inspecting headerClassName; keep headerClassName usage for styling but use the new align field for layout decisions so changes to classes won’t affect alignment behavior.src/page/coupon/hooks/useCouponPageState.ts (1)
437-501: 세 탭의 sorting/pagination 핸들러가 거의 동일합니다.
handleCouponSortingChange,handleCouponCodeSortingChange,handleIssuedSortingChange그리고 pagination 핸들러 3개가 정렬 기본값과 상태 setter만 다르고 로직이 완전히 동일합니다. 헬퍼 팩토리 함수를 만들면 약 60줄을 줄일 수 있습니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/page/coupon/hooks/useCouponPageState.ts` around lines 437 - 501, Many handlers (handleCouponSortingChange, handleCouponCodeSortingChange, handleIssuedSortingChange and their pagination counterparts) duplicate the same logic; create a small factory that, given the relevant default sorting constant (e.g. DEFAULT_COUPON_SORTING, DEFAULT_COUPON_CODE_SORTING, DEFAULT_ISSUED_SORTING), the current pagination/page state (couponPagination/couponPage, couponCodePagination/couponCodePage, issuedPagination/issuedCouponPage) and the setters (setCouponSorting/setCouponPagination, setCouponCodeSorting/setCouponCodePagination, setIssuedSorting/setIssuedPagination), returns the pair of handlers (sortingChange, paginationChange) that call normalizeSingleSorting and functionalUpdate, validate pageIndex bounds and reset pageIndex to 0 on sorting change; replace the six inline functions with instances produced by this factory so behavior remains identical while removing duplication.src/page/member-management/hooks/useMemberManagementPageState.ts (1)
377-383:handleSortingChange에서 pagination 리셋이 useEffect와 안전하게 동작하는지 확인 필요.
setSorting과setRecordPagination이 같은 이벤트 핸들러 내에서 호출되므로 React 18+ batching 덕분에 단일 렌더에서 처리됩니다. 다만, 이 파일은 useEffect 기반 fetch 패턴을 사용하는 반면usePaymentPageState는 핸들러 내에서 직접 fetch를 호출합니다. 동일 PR 내에서 두 가지 패턴이 혼재하는 점은 향후 혼란의 소지가 있습니다.src/page/point/sections/PointLedgerSection.tsx (1)
78-78:transactionType렌더링이 이진 분기에 의존합니다.현재
"EARN"외의 모든 값을"차감"으로 표시합니다. 향후 새로운 트랜잭션 유형이 추가되면 의도치 않게"차감"으로 표시될 수 있습니다. 명시적인 매핑이나 exhaustive check를 고려해 볼 수 있습니다.♻️ 명시적 매핑 예시
- cell: ({ row }) => (row.original.transactionType === "EARN" ? "적립" : "차감"), + cell: ({ row }) => { + const labels: Record<string, string> = { EARN: "적립", SPEND: "차감" } + return labels[row.original.transactionType] ?? row.original.transactionType + },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/page/point/sections/PointLedgerSection.tsx` at line 78, The current cell renderer for row.original.transactionType uses a binary check (row.original.transactionType === "EARN") and renders everything else as "차감"; change it to an explicit mapping or exhaustive switch so new transaction types don’t silently render as "차감". Update the cell function (the column definition using cell: ({ row }) => ...) to look up transactionType in a map (e.g., { EARN: "적립", REDEEM: "차감", ... }) or use a switch with a clear default like "알 수 없음" or the raw transactionType, ensuring all known types are listed and unknown types get a non-misleading fallback.src/page/coupon/sections/CouponCodeTabSection.tsx (1)
26-26:formatDateTime유틸리티가 여러 파일에 중복 정의되어 있습니다.
src/page/coupon/hooks/useCouponPageState.ts와src/page/member-management/hooks/useMemberManagementPageState.ts에 동일한 구현의formatDateTime이 존재합니다. 공용 유틸리티로 추출하면 DRY 원칙을 준수하고 일관성을 유지할 수 있습니다. 이 파일에 국한된 문제는 아니므로, 추후 별도 작업으로 진행해도 됩니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/page/coupon/sections/CouponCodeTabSection.tsx` at line 26, The duplicate formatDateTime implementation appears in the hooks useCouponPageState and useMemberManagementPageState; extract this helper into a shared utility (e.g., export function formatDateTime in a new utils/date.ts or utils/formatters.ts), replace the local implementations by importing the shared formatDateTime, and update both hooks to use the exported function so the logic is centralized and DRY.src/page/coupon/sections/CouponTabSection.tsx (1)
47-104:couponNameDrafts가useMemo의존성에 포함되어 입력 시마다 컬럼이 재생성됩니다.
couponNameDrafts객체는 사용자가 이름 수정 Input에 타이핑할 때마다 변경되므로, 매 키스트로크마다 전체columns배열이 재생성되고 TanStack Table이 컬럼을 다시 처리합니다. 어드민 페이지 규모에서는 문제가 없을 수 있지만,meta를 활용하여couponNameDrafts를 테이블 메타로 전달하면 컬럼 정의를 안정적으로 유지할 수 있습니다.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/components/admin/table/AdminDataTable.tsx`:
- Around line 124-127: The current display uses a misleading fallback
"totalElements ?? rows.length" in AdminDataTable which shows only the current
page row count when totalElements is undefined; change this by either making
totalElements a required prop in the AdminDataTable props (update the props
type/interface and all callers to pass totalElements) or remove the fallback and
conditionally render the count only when totalElements is provided (e.g., show
nothing or a placeholder when totalElements is undefined). Update the span
rendering logic that references totalElements, and adjust any parent components
that instantiate AdminDataTable to supply the totalElements value if you choose
the required-prop approach.
In `@src/page/coupon/hooks/useCouponPageState.ts`:
- Around line 196-232: Both fetchCouponOptions and fetchMembers lack try/catch
blocks so an exception will reject the Promise.all call and prevent the other
result from applying; wrap the body of fetchCouponOptions and fetchMembers in
try/catch, handle errors individually by calling showError (for
fetchCouponOptions you can pass resolveCouponErrorMessage(error) or a fallback
message and ensure early return) and for fetchMembers show a user-friendly
error, and keep existing success paths that call setCoupons,
setCouponNameDrafts, and setMembers (references: getCoupons,
setCouponNameDrafts, setCoupons, getAdminMembers, setMembers) so each function
handles its own failures without propagating uncaught exceptions.
- Around line 234-275: The fetchCouponPage function currently lacks error
handling so if getCoupons throws the loading flag remains true; wrap the body of
fetchCouponPage in try/catch/finally, call setIsCouponLoading(true) before the
try, in catch call showError(resolveCouponErrorMessage(err?.message)) and
setCouponPage(null) (or similar error fallback), and in finally always call
setIsCouponLoading(false); apply the same try/catch/finally pattern to
fetchCouponCodePage and fetchIssuedCouponPage to mirror the error handling used
in usePaymentPageState and ensure the UI loading state is cleared on network or
thrown errors.
In `@src/page/event/sections/EventToolbarSection.tsx`:
- Around line 39-43: The onKeyDown handler currently calls onApplySearch on
Enter even during IME composition or when a search is already loading; update
the Enter branch in the onKeyDown handler to first check
event.nativeEvent.isComposing (or event.isComposing) and the component's
isLoading flag and return early if either is true, then only call onApplySearch
when not composing and not isLoading—refer to the onKeyDown handler and
onApplySearch symbols to locate where to add these guards.
In `@src/page/member-management/hooks/useMemberManagementPageState.ts`:
- Around line 385-397: handlePaginationChange blocks all navigation when
recordPage.totalPages is 0 because nextPagination.pageIndex >=
recordPage.totalPages is always true; change the guard in handlePaginationChange
so it only enforces the upper bound when totalPages > 0 (for example, require
recordPage.totalPages > 0 && nextPagination.pageIndex >= recordPage.totalPages),
ensuring you still keep the existing check for nextPagination.pageIndex < 0 and
then call setRecordPagination(nextPagination).
---
Nitpick comments:
In `@src/api/coupon/get-coupon-codes.ts`:
- Around line 4-28: Multiple API functions (getCouponCodes, getCoupons,
getIssuedCoupons) repeat identical page/size/sort/keyword URLSearchParams
building; extract that into a shared utility (e.g., buildPageParams or
buildPageQuery) that accepts a BasePageQuery-like object and returns a
URLSearchParams, then replace the inline params construction in getCouponCodes
with a call to that utility and use its toString() when composing the request
URL; ensure the utility applies the same defaults (page -> 0, size -> 50) and
trims/guards keyword and only sets sort/keyword when present so existing
behavior of requestApi(`/admin/coupons/code?${params.toString()}`, { method:
"GET" }) remains unchanged.
In `@src/api/coupon/types.ts`:
- Around line 43-68: Multiple page response interfaces (AdminCouponPageResponse,
AdminCouponCodePageResponse, AdminIssuedCouponPageResponse) duplicate the same
paging fields; introduce a generic base like PageResponse<T> that defines
content: T[], page, size, totalElements, totalPages, hasNext, then replace each
specific interface with type aliases or extends (e.g., type
AdminCouponPageResponse = PageResponse<AdminCoupon>) using the existing domain
types AdminCoupon, AdminCouponCode, AdminIssuedCoupon; update any other similar
page types (events, members, payments, points) to use PageResponse<T> to remove
duplication.
In `@src/components/admin/table/AdminDataTable.tsx`:
- Around line 73-76: The current layout logic in AdminDataTable.tsx relies on
checking columnMeta?.headerClassName?.includes("text-right") to position the
sort button, which is brittle; update the AdminColumnMeta type to add an
explicit align?: "left" | "right" (or required align) field, update any places
that construct columnMeta to set align, and change the rendering logic in
AdminDataTable (the code around className={cn(...
columnMeta?.headerClassName?.includes("text-right") && "ml-auto flex", ...)}) to
use columnMeta.align === "right" instead of inspecting headerClassName; keep
headerClassName usage for styling but use the new align field for layout
decisions so changes to classes won’t affect alignment behavior.
In `@src/page/coupon/hooks/useCouponPageState.ts`:
- Around line 437-501: Many handlers (handleCouponSortingChange,
handleCouponCodeSortingChange, handleIssuedSortingChange and their pagination
counterparts) duplicate the same logic; create a small factory that, given the
relevant default sorting constant (e.g. DEFAULT_COUPON_SORTING,
DEFAULT_COUPON_CODE_SORTING, DEFAULT_ISSUED_SORTING), the current
pagination/page state (couponPagination/couponPage,
couponCodePagination/couponCodePage, issuedPagination/issuedCouponPage) and the
setters (setCouponSorting/setCouponPagination,
setCouponCodeSorting/setCouponCodePagination,
setIssuedSorting/setIssuedPagination), returns the pair of handlers
(sortingChange, paginationChange) that call normalizeSingleSorting and
functionalUpdate, validate pageIndex bounds and reset pageIndex to 0 on sorting
change; replace the six inline functions with instances produced by this factory
so behavior remains identical while removing duplication.
In `@src/page/coupon/sections/CouponCodeTabSection.tsx`:
- Line 26: The duplicate formatDateTime implementation appears in the hooks
useCouponPageState and useMemberManagementPageState; extract this helper into a
shared utility (e.g., export function formatDateTime in a new utils/date.ts or
utils/formatters.ts), replace the local implementations by importing the shared
formatDateTime, and update both hooks to use the exported function so the logic
is centralized and DRY.
In `@src/page/payment/hooks/usePaymentPageState.ts`:
- Around line 282-301: Extract the duplicated boundary-check + update + fetch
logic into a reusable helper (e.g., createPaginationChangeHandler) and replace
handlePaymentPaginationChange and handleTransactionPaginationChange (and the
three coupon handlers) to call that helper; the helper should accept
currentPagination (paymentPagination), totalPages (paymentData?.totalPages),
setPagination (setPaymentPagination) and onFetch (a wrapper for fetchPayments
with paymentYearSemester/paymentStatus/paymentSorting/paymentMemberKeyword
bound) and perform the functionalUpdate, pageIndex < 0 and pageIndex >=
totalPages checks, then call setPagination and onFetch with the next pagination.
In `@src/page/point/hooks/usePointPageState.ts`:
- Around line 155-165: The useEffect currently calls a floating promise via void
fetchLedger(...) and disables eslint for deps; extract this mount-only behavior
into a reusable hook (e.g., create a useMount utility) and call fetchLedger from
that hook instead of the anonymous useEffect to avoid repeating the void pattern
and eslint-disable. Update usePointPageState to import and use useMount(() => {
fetchLedger(ledgerPagination, ledgerMemberKeyword, ledgerTransactionType,
ledgerSorting, ledgerFrom, ledgerTo) }) (or wrap fetchLedger call in an async
IIFE inside useMount) so the mount-only invocation is explicit and you can
remove the eslint-disable and the void usage. Ensure the referenced symbols are
fetchLedger and usePointPageState (and the new useMount helper) so reviewers can
locate changes.
In `@src/page/point/sections/PointLedgerSection.tsx`:
- Line 78: The current cell renderer for row.original.transactionType uses a
binary check (row.original.transactionType === "EARN") and renders everything
else as "차감"; change it to an explicit mapping or exhaustive switch so new
transaction types don’t silently render as "차감". Update the cell function (the
column definition using cell: ({ row }) => ...) to look up transactionType in a
map (e.g., { EARN: "적립", REDEEM: "차감", ... }) or use a switch with a clear
default like "알 수 없음" or the raw transactionType, ensuring all known types are
listed and unknown types get a non-misleading fallback.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge base: Disabled due to data retention organization setting
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (30)
package.jsonsrc/api/activity/get-activities.tssrc/api/activity/types.tssrc/api/coupon/get-coupon-codes.tssrc/api/coupon/get-coupons.tssrc/api/coupon/get-issued-coupons.tssrc/api/coupon/types.tssrc/components/admin/index.tssrc/components/admin/table/AdminDataTable.tsxsrc/components/admin/table/sorting.tssrc/components/admin/table/types.tssrc/page/coupon.tsxsrc/page/coupon/hooks/useCouponPageState.tssrc/page/coupon/sections/CouponCodeTabSection.tsxsrc/page/coupon/sections/CouponIssuedTabSection.tsxsrc/page/coupon/sections/CouponPageHeader.tsxsrc/page/coupon/sections/CouponTabSection.tsxsrc/page/event.tsxsrc/page/event/hooks/useEventPageState.tssrc/page/event/sections/EventTableSection.tsxsrc/page/event/sections/EventToolbarSection.tsxsrc/page/member-management.tsxsrc/page/member-management/hooks/useMemberManagementPageState.tssrc/page/member-management/sections/MemberRecordsSection.tsxsrc/page/payment/hooks/usePaymentPageState.tssrc/page/payment/sections/PaymentManagementSection.tsxsrc/page/payment/sections/TransactionManagementSection.tsxsrc/page/point.tsxsrc/page/point/hooks/usePointPageState.tssrc/page/point/sections/PointLedgerSection.tsx
There was a problem hiding this comment.
Pull request overview
This pull request refactors the admin interface to use TanStack Table (v8.21.3) library, replacing custom table implementations across all admin pages. The refactoring introduces a unified AdminDataTable component and standardizes pagination and sorting patterns throughout the application.
Changes:
- Introduced
AdminDataTablecomponent with TanStack Table integration for consistent table behavior - Refactored all admin pages (Point, Payment, Member Management, Event, Coupon) to use the new table component
- Updated API functions to support server-side pagination, sorting, and keyword search
- Changed search behavior from instant filtering to button/Enter-triggered search with applied keyword display
Reviewed changes
Copilot reviewed 30 out of 31 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/components/admin/table/AdminDataTable.tsx | New unified table component using TanStack Table with built-in pagination and sorting |
| src/components/admin/table/sorting.ts | Utility functions for converting TanStack sorting state to backend API format |
| src/components/admin/table/types.ts | Type definitions for AdminDataTable props and column metadata |
| src/components/admin/index.ts | Added exports for new table components and utilities |
| src/page/point/sections/PointLedgerSection.tsx | Migrated to AdminDataTable with column definitions using useMemo |
| src/page/point/hooks/usePointPageState.ts | Refactored to use TanStack pagination/sorting state instead of custom implementation |
| src/page/point.tsx | Updated props to pass sorting and pagination state |
| src/page/payment/sections/TransactionManagementSection.tsx | Migrated to AdminDataTable pattern |
| src/page/payment/sections/PaymentManagementSection.tsx | Migrated to AdminDataTable with action column support |
| src/page/payment/hooks/usePaymentPageState.ts | Refactored state management for both payment and transaction tables |
| src/page/member-management/sections/MemberRecordsSection.tsx | Migrated to AdminDataTable with row selection and custom styling |
| src/page/member-management/hooks/useMemberManagementPageState.ts | Added sort preset conversion to sync dropdown with table sorting |
| src/page/member-management.tsx | Updated props for new table implementation |
| src/page/event/sections/EventToolbarSection.tsx | Added search button and applied keyword display |
| src/page/event/sections/EventTableSection.tsx | Migrated to AdminDataTable with action buttons in columns |
| src/page/event/hooks/useEventPageState.ts | Refactored to handle server-side pagination and keyword search |
| src/page/event.tsx | Updated to pass pagination and sorting state |
| src/page/coupon/sections/CouponPageHeader.tsx | Added search button and applied keyword display |
| src/page/coupon/sections/CouponTabSection.tsx | Migrated coupon table to AdminDataTable |
| src/page/coupon/sections/CouponIssuedTabSection.tsx | Migrated issued coupon table to AdminDataTable |
| src/page/coupon/sections/CouponCodeTabSection.tsx | Migrated coupon code table to AdminDataTable |
| src/page/coupon/hooks/useCouponPageState.ts | Refactored to manage separate pagination/sorting for three tabs |
| src/page/coupon.tsx | Updated to remove loading/empty state cards (now handled by AdminDataTable) |
| src/api/coupon/types.ts | Added paginated response types for coupons, codes, and issued coupons |
| src/api/coupon/get-issued-coupons.ts | Changed to accept query parameters for pagination, sorting, and filtering |
| src/api/coupon/get-coupons.ts | Changed to accept query parameters for pagination, sorting, and keyword search |
| src/api/coupon/get-coupon-codes.ts | Changed to accept query parameters for pagination, sorting, and keyword search |
| src/api/activity/types.ts | Added AdminActivityPageResponse type for paginated activities |
| src/api/activity/get-activities.ts | Changed to accept query parameters for pagination, sorting, and keyword search |
| package.json | Added @tanstack/react-table dependency (v8.21.3) |
| package-lock.json | Lock file update for new dependency |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary by CodeRabbit
릴리스 노트
New Features
Refactor