diff --git a/src/app/[locale]/(protected)/accounting/sales/page.tsx b/src/app/[locale]/(protected)/accounting/sales/page.tsx index 3d313c0c..6ba000e9 100644 --- a/src/app/[locale]/(protected)/accounting/sales/page.tsx +++ b/src/app/[locale]/(protected)/accounting/sales/page.tsx @@ -30,12 +30,19 @@ export default function SalesPage() { getSales({ perPage: 100 }) .then(result => { - setData(result.data); - setPagination(result.pagination); + if (result.success) { + setData(result.data); + setPagination(result.pagination); + } }) .finally(() => setIsLoading(false)); }, [mode]); + // mode=new일 때 등록 화면 표시 + if (mode === 'new') { + return ; + } + if (isLoading) { return (
@@ -44,11 +51,6 @@ export default function SalesPage() { ); } - // mode=new일 때 등록 화면 표시 - if (mode === 'new') { - return ; - } - return ( - {clients.map((client) => ( + {clients.filter(c => c.id !== '').map((client) => ( {client.name} diff --git a/src/components/accounting/SalesManagement/index.tsx b/src/components/accounting/SalesManagement/index.tsx index a03a8371..8ab7f236 100644 --- a/src/components/accounting/SalesManagement/index.tsx +++ b/src/components/accounting/SalesManagement/index.tsx @@ -14,7 +14,7 @@ * - deleteConfirmMessage로 삭제 다이얼로그 처리 */ -import { useState, useMemo, useCallback } from 'react'; +import { useState, useMemo, useCallback, useEffect } from 'react'; import { useRouter } from 'next/navigation'; import { toast } from 'sonner'; import { @@ -62,7 +62,7 @@ import { ISSUANCE_FILTER_OPTIONS, ACCOUNT_SUBJECT_SELECTOR_OPTIONS, } from './types'; -import { deleteSale, toggleSaleIssuance } from './actions'; +import { getSales, deleteSale, toggleSaleIssuance } from './actions'; // ===== 테이블 컬럼 정의 ===== const tableColumns = [ @@ -95,7 +95,10 @@ export function SalesManagement({ initialData, initialPagination }: SalesManagem // ===== 외부 상태 (UniversalListPage 외부에서 관리) ===== const [startDate, setStartDate] = useState('2025-01-01'); const [endDate, setEndDate] = useState('2025-12-31'); - const [salesData, setSalesData] = useState(initialData); + const [salesData, setSalesData] = useState(initialData || []); + const [pagination, setPagination] = useState(initialPagination); + const [currentPage, setCurrentPage] = useState(initialPagination.currentPage); + const [isLoading, setIsLoading] = useState(false); const [searchQuery, setSearchQuery] = useState(''); // 통합 필터 상태 (filterConfig 사용) @@ -178,6 +181,45 @@ export function SalesManagement({ initialData, initialPagination }: SalesManagem }); }, []); + // ===== API 데이터 로드 ===== + const loadData = useCallback(async (page: number = 1) => { + setIsLoading(true); + try { + const result = await getSales({ + search: searchQuery || undefined, + startDate, + endDate, + perPage: 100, + page, + }); + + if (result.success) { + setSalesData(result.data); + setPagination(result.pagination); + setCurrentPage(result.pagination.currentPage); + } else { + toast.error(result.error || '데이터를 불러오는데 실패했습니다.'); + } + } catch { + toast.error('데이터를 불러오는데 실패했습니다.'); + } finally { + setIsLoading(false); + } + }, [searchQuery, startDate, endDate]); + + // initialData가 비어있으면 자동 로드 + useEffect(() => { + if ((!initialData || initialData.length === 0) && !isLoading) { + loadData(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + // ===== 페이지 변경 ===== + const handlePageChange = useCallback((page: number) => { + loadData(page); + }, [loadData]); + // ===== 핸들러 ===== const handleRowClick = useCallback((item: SalesRecord) => { router.push(`/ko/accounting/sales/${item.id}?mode=view`); @@ -254,11 +296,13 @@ export function SalesManagement({ initialData, initialPagination }: SalesManagem // API 액션 actions: { getList: async () => { - return { - success: true, - data: salesData, - totalCount: salesData.length, - }; + const result = await getSales({ perPage: 100, page: currentPage }); + if (result.success) { + setSalesData(result.data); + setPagination(result.pagination); + return { success: true, data: result.data, totalCount: result.pagination.total }; + } + return { success: true, data: salesData, totalCount: salesData.length }; }, deleteItem: async (id: string) => { const result = await deleteSale(id); @@ -500,6 +544,7 @@ export function SalesManagement({ initialData, initialPagination }: SalesManagem }), [ salesData, + currentPage, startDate, endDate, stats, @@ -518,7 +563,17 @@ export function SalesManagement({ initialData, initialPagination }: SalesManagem return ( <> - + {/* 계정과목명 저장 확인 다이얼로그 */} diff --git a/src/components/accounting/SalesManagement/types.ts b/src/components/accounting/SalesManagement/types.ts index cb9bb8fa..7bf28962 100644 --- a/src/components/accounting/SalesManagement/types.ts +++ b/src/components/accounting/SalesManagement/types.ts @@ -230,7 +230,7 @@ export function transformApiToFrontend(apiData: SaleApiData): SalesRecord { salesNo: apiData.sale_number, salesDate: apiData.sale_date, vendorId: String(apiData.client_id), - vendorName: apiData.client?.name ?? '', + vendorName: apiData.client?.name || '(거래처 미지정)', salesType: 'other', // API에 없음, 기본값 accountSubject: 'other', // API에 없음, 기본값 items, // 수주 품목에서 가져옴 diff --git a/src/components/molecules/MobileFilter.tsx b/src/components/molecules/MobileFilter.tsx index d9b49268..e6fdfedb 100644 --- a/src/components/molecules/MobileFilter.tsx +++ b/src/components/molecules/MobileFilter.tsx @@ -289,7 +289,7 @@ export function MobileFilter({ {field.allOptionLabel || '전체'} - {field.options.map((option) => ( + {field.options.filter(opt => opt.value !== '').map((option) => ( {option.label} @@ -299,10 +299,12 @@ export function MobileFilter({ ) : ( // 다중선택: MultiSelectCombobox ({ - value: opt.value, - label: opt.label, - }))} + options={field.options + .filter(opt => opt.value !== '') + .map((opt) => ({ + value: opt.value, + label: opt.label, + }))} value={(values[field.key] as string[]) || []} onChange={(value) => onChange(field.key, value)} placeholder="전체"