Implement pagination for source data in Outbound, Receiving, and Shipping Order pages
- Add pagination state management for source data in OutboundPage and ReceivingPage. - Introduce pagination controls with navigation buttons for better user experience. - Update data loading functions to accommodate pagination parameters. - Enhance SalesItemPage to include isDefault property in category options. This commit improves the usability of logistics and sales item management by allowing users to navigate through large sets of data more efficiently.
This commit is contained in:
parent
f980bffed4
commit
42bb3a18fb
|
|
@ -37,6 +37,9 @@ import {
|
|||
X,
|
||||
Save,
|
||||
ChevronRight,
|
||||
ChevronLeft,
|
||||
ChevronsLeft,
|
||||
ChevronsRight,
|
||||
} from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
|
|
@ -131,6 +134,10 @@ export default function OutboundPage() {
|
|||
const [items, setItems] = useState<ItemSource[]>([]);
|
||||
const [warehouses, setWarehouses] = useState<WarehouseOption[]>([]);
|
||||
|
||||
// 소스 데이터 페이징 (클라이언트 사이드)
|
||||
const [sourcePage, setSourcePage] = useState(1);
|
||||
const [sourcePageSize, setSourcePageSize] = useState(20);
|
||||
|
||||
// 날짜 초기화
|
||||
useEffect(() => {
|
||||
const today = new Date();
|
||||
|
|
@ -261,13 +268,44 @@ export default function OutboundPage() {
|
|||
};
|
||||
|
||||
const searchSourceData = useCallback(async () => {
|
||||
setSourcePage(1);
|
||||
await loadSourceData(modalOutboundType, sourceKeyword || undefined);
|
||||
}, [modalOutboundType, sourceKeyword, loadSourceData]);
|
||||
|
||||
// 현재 출고유형에 따른 전체 소스 데이터
|
||||
const allSourceData = useMemo(() => {
|
||||
if (modalOutboundType === "판매출고") return shipmentInstructions;
|
||||
if (modalOutboundType === "반품출고") return purchaseOrders;
|
||||
return items;
|
||||
}, [modalOutboundType, shipmentInstructions, purchaseOrders, items]);
|
||||
|
||||
const sourceTotalCount = allSourceData.length;
|
||||
const sourceTotalPages = Math.max(1, Math.ceil(sourceTotalCount / sourcePageSize));
|
||||
|
||||
// 현재 페이지에 해당하는 slice
|
||||
const pagedShipmentInstructions = useMemo(() => {
|
||||
if (modalOutboundType !== "판매출고") return [];
|
||||
const start = (sourcePage - 1) * sourcePageSize;
|
||||
return shipmentInstructions.slice(start, start + sourcePageSize);
|
||||
}, [modalOutboundType, shipmentInstructions, sourcePage, sourcePageSize]);
|
||||
|
||||
const pagedPurchaseOrders = useMemo(() => {
|
||||
if (modalOutboundType !== "반품출고") return [];
|
||||
const start = (sourcePage - 1) * sourcePageSize;
|
||||
return purchaseOrders.slice(start, start + sourcePageSize);
|
||||
}, [modalOutboundType, purchaseOrders, sourcePage, sourcePageSize]);
|
||||
|
||||
const pagedItems = useMemo(() => {
|
||||
if (modalOutboundType !== "기타출고") return [];
|
||||
const start = (sourcePage - 1) * sourcePageSize;
|
||||
return items.slice(start, start + sourcePageSize);
|
||||
}, [modalOutboundType, items, sourcePage, sourcePageSize]);
|
||||
|
||||
const handleOutboundTypeChange = useCallback(
|
||||
(type: string) => {
|
||||
setModalOutboundType(type);
|
||||
setSourceKeyword("");
|
||||
setSourcePage(1);
|
||||
setShipmentInstructions([]);
|
||||
setPurchaseOrders([]);
|
||||
setItems([]);
|
||||
|
|
@ -686,6 +724,7 @@ export default function OutboundPage() {
|
|||
defaultMaxWidth="sm:max-w-[1600px]"
|
||||
defaultWidth="w-[95vw]"
|
||||
className="h-[90vh] p-0"
|
||||
contentClassName="overflow-hidden flex flex-col"
|
||||
footer={
|
||||
<div className="flex w-full items-center justify-between px-6 py-3">
|
||||
<div className="text-muted-foreground text-xs">
|
||||
|
|
@ -774,43 +813,87 @@ export default function OutboundPage() {
|
|||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-auto px-4 py-2">
|
||||
<h4 className="text-muted-foreground mb-2 text-xs font-semibold">
|
||||
<div className="flex items-center justify-between border-b px-4 py-2 shrink-0">
|
||||
<h4 className="text-muted-foreground text-xs font-semibold">
|
||||
{modalOutboundType === "판매출고"
|
||||
? "미출고 출하지시 목록"
|
||||
: modalOutboundType === "반품출고"
|
||||
? "입고된 발주 목록"
|
||||
: "품목 목록"}
|
||||
</h4>
|
||||
{sourceTotalCount > 0 && (
|
||||
<span className="text-muted-foreground text-[11px]">총 {sourceTotalCount}건</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-auto">
|
||||
{sourceLoading ? (
|
||||
<div className="flex h-40 items-center justify-center">
|
||||
<Loader2 className="h-5 w-5 animate-spin" />
|
||||
</div>
|
||||
) : modalOutboundType === "판매출고" ? (
|
||||
<SourceShipmentInstructionTable
|
||||
data={shipmentInstructions}
|
||||
data={pagedShipmentInstructions}
|
||||
onAdd={addShipmentInstruction}
|
||||
selectedKeys={selectedItems.map((s) => s.key)}
|
||||
/>
|
||||
) : modalOutboundType === "반품출고" ? (
|
||||
<SourcePurchaseOrderTable
|
||||
data={purchaseOrders}
|
||||
data={pagedPurchaseOrders}
|
||||
onAdd={addPurchaseOrder}
|
||||
selectedKeys={selectedItems.map((s) => s.key)}
|
||||
/>
|
||||
) : (
|
||||
<SourceItemTable
|
||||
data={items}
|
||||
data={pagedItems}
|
||||
onAdd={addItem}
|
||||
selectedKeys={selectedItems.map((s) => s.key)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 페이징 바 */}
|
||||
{sourceTotalCount > 0 && (
|
||||
<div className="flex items-center justify-between border-t bg-muted/10 px-4 py-2 shrink-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-muted-foreground text-[11px]">표시:</span>
|
||||
<Input
|
||||
type="number"
|
||||
min={1}
|
||||
max={500}
|
||||
value={sourcePageSize}
|
||||
onChange={(e) => {
|
||||
const v = parseInt(e.target.value, 10);
|
||||
if (v > 0) { setSourcePageSize(v); setSourcePage(1); }
|
||||
}}
|
||||
className="h-7 w-[60px] text-center text-[11px]"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Button variant="outline" size="icon" className="h-7 w-7" disabled={sourcePage <= 1}
|
||||
onClick={() => setSourcePage(1)}>
|
||||
<ChevronsLeft className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
<Button variant="outline" size="icon" className="h-7 w-7" disabled={sourcePage <= 1}
|
||||
onClick={() => setSourcePage((p) => p - 1)}>
|
||||
<ChevronLeft className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
<span className="text-xs font-medium px-2">{sourcePage} / {sourceTotalPages}</span>
|
||||
<Button variant="outline" size="icon" className="h-7 w-7" disabled={sourcePage >= sourceTotalPages}
|
||||
onClick={() => setSourcePage((p) => p + 1)}>
|
||||
<ChevronRight className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
<Button variant="outline" size="icon" className="h-7 w-7" disabled={sourcePage >= sourceTotalPages}
|
||||
onClick={() => setSourcePage(sourceTotalPages)}>
|
||||
<ChevronsRight className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</ResizablePanel>
|
||||
|
||||
<ResizableHandle withHandle />
|
||||
<ResizableHandle withHandle onPointerDown={(e) => e.stopPropagation()} />
|
||||
|
||||
{/* 우측: 출고 정보 + 선택 품목 */}
|
||||
<ResizablePanel defaultSize={40} minSize={25}>
|
||||
|
|
|
|||
|
|
@ -37,6 +37,9 @@ import {
|
|||
X,
|
||||
Save,
|
||||
ChevronRight,
|
||||
ChevronLeft,
|
||||
ChevronsLeft,
|
||||
ChevronsRight,
|
||||
} from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
|
|
@ -132,6 +135,11 @@ export default function ReceivingPage() {
|
|||
const [items, setItems] = useState<ItemSource[]>([]);
|
||||
const [warehouses, setWarehouses] = useState<WarehouseOption[]>([]);
|
||||
|
||||
// 소스 데이터 페이징
|
||||
const [sourcePage, setSourcePage] = useState(1);
|
||||
const [sourcePageSize, setSourcePageSize] = useState(20);
|
||||
const [sourceTotalCount, setSourceTotalCount] = useState(0);
|
||||
|
||||
// 날짜 초기화
|
||||
useEffect(() => {
|
||||
const today = new Date();
|
||||
|
|
@ -214,18 +222,32 @@ export default function ReceivingPage() {
|
|||
|
||||
// 소스 데이터 로드 함수
|
||||
const loadSourceData = useCallback(
|
||||
async (type: string, keyword?: string) => {
|
||||
async (type: string, keyword?: string, pageOverride?: number) => {
|
||||
setSourceLoading(true);
|
||||
try {
|
||||
const params = {
|
||||
keyword: keyword || undefined,
|
||||
page: pageOverride ?? sourcePage,
|
||||
pageSize: sourcePageSize,
|
||||
};
|
||||
if (type === "구매입고") {
|
||||
const res = await getPurchaseOrderSources(keyword || undefined);
|
||||
if (res.success) setPurchaseOrders(res.data);
|
||||
const res = await getPurchaseOrderSources(params);
|
||||
if (res.success) {
|
||||
setPurchaseOrders(res.data);
|
||||
setSourceTotalCount(res.totalCount || 0);
|
||||
}
|
||||
} else if (type === "반품입고") {
|
||||
const res = await getShipmentSources(keyword || undefined);
|
||||
if (res.success) setShipments(res.data);
|
||||
const res = await getShipmentSources(params);
|
||||
if (res.success) {
|
||||
setShipments(res.data);
|
||||
setSourceTotalCount(res.totalCount || 0);
|
||||
}
|
||||
} else {
|
||||
const res = await getItemSources(keyword || undefined);
|
||||
if (res.success) setItems(res.data);
|
||||
const res = await getItemSources(params);
|
||||
if (res.success) {
|
||||
setItems(res.data);
|
||||
setSourceTotalCount(res.totalCount || 0);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
|
|
@ -233,7 +255,7 @@ export default function ReceivingPage() {
|
|||
setSourceLoading(false);
|
||||
}
|
||||
},
|
||||
[]
|
||||
[sourcePage, sourcePageSize]
|
||||
);
|
||||
|
||||
const openRegisterModal = async () => {
|
||||
|
|
@ -250,13 +272,15 @@ export default function ReceivingPage() {
|
|||
setPurchaseOrders([]);
|
||||
setShipments([]);
|
||||
setItems([]);
|
||||
setSourcePage(1);
|
||||
setSourceTotalCount(0);
|
||||
setIsModalOpen(true);
|
||||
|
||||
// 입고번호 생성 + 발주 데이터 동시 로드
|
||||
try {
|
||||
const [numRes] = await Promise.all([
|
||||
generateReceivingNumber(),
|
||||
loadSourceData(defaultType),
|
||||
loadSourceData(defaultType, undefined, 1),
|
||||
]);
|
||||
if (numRes.success) setModalInboundNo(numRes.data);
|
||||
} catch {
|
||||
|
|
@ -266,7 +290,8 @@ export default function ReceivingPage() {
|
|||
|
||||
// 검색 버튼 클릭 시
|
||||
const searchSourceData = useCallback(async () => {
|
||||
await loadSourceData(modalInboundType, sourceKeyword || undefined);
|
||||
setSourcePage(1);
|
||||
await loadSourceData(modalInboundType, sourceKeyword || undefined, 1);
|
||||
}, [modalInboundType, sourceKeyword, loadSourceData]);
|
||||
|
||||
// 입고유형 변경 시 소스 데이터 자동 리로드
|
||||
|
|
@ -278,7 +303,9 @@ export default function ReceivingPage() {
|
|||
setShipments([]);
|
||||
setItems([]);
|
||||
setSelectedItems([]);
|
||||
loadSourceData(type);
|
||||
setSourcePage(1);
|
||||
setSourceTotalCount(0);
|
||||
loadSourceData(type, undefined, 1);
|
||||
},
|
||||
[loadSourceData]
|
||||
);
|
||||
|
|
@ -303,7 +330,7 @@ export default function ReceivingPage() {
|
|||
inbound_qty: po.remain_qty,
|
||||
unit_price: po.unit_price,
|
||||
total_amount: po.remain_qty * po.unit_price,
|
||||
source_table: "purchase_order_mng",
|
||||
source_table: po.source_table || "purchase_order_mng",
|
||||
source_id: po.id,
|
||||
},
|
||||
]);
|
||||
|
|
@ -694,6 +721,7 @@ export default function ReceivingPage() {
|
|||
defaultMaxWidth="sm:max-w-[1600px]"
|
||||
defaultWidth="w-[95vw]"
|
||||
className="h-[90vh] p-0"
|
||||
contentClassName="overflow-hidden flex flex-col"
|
||||
footer={
|
||||
<div className="flex w-full items-center justify-between px-6 py-3">
|
||||
<div className="text-muted-foreground text-xs">
|
||||
|
|
@ -817,10 +845,56 @@ export default function ReceivingPage() {
|
|||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 페이징 */}
|
||||
{sourceTotalCount > 0 && (
|
||||
<div className="flex shrink-0 items-center justify-between border-t bg-muted/10 px-4 py-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-muted-foreground text-[11px]">표시:</span>
|
||||
<Input
|
||||
type="number"
|
||||
min={1}
|
||||
max={500}
|
||||
value={sourcePageSize}
|
||||
onChange={(e) => {
|
||||
const v = parseInt(e.target.value, 10);
|
||||
if (v > 0) {
|
||||
setSourcePageSize(v);
|
||||
setSourcePage(1);
|
||||
loadSourceData(modalInboundType, sourceKeyword || undefined, 1);
|
||||
}
|
||||
}}
|
||||
className="h-7 w-[60px] text-center text-[11px]"
|
||||
/>
|
||||
<span className="text-muted-foreground text-[11px]">
|
||||
총 {sourceTotalCount}건
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Button variant="outline" size="icon" className="h-7 w-7" disabled={sourcePage <= 1}
|
||||
onClick={() => { setSourcePage(1); loadSourceData(modalInboundType, sourceKeyword || undefined, 1); }}>
|
||||
<ChevronsLeft className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
<Button variant="outline" size="icon" className="h-7 w-7" disabled={sourcePage <= 1}
|
||||
onClick={() => { const p = sourcePage - 1; setSourcePage(p); loadSourceData(modalInboundType, sourceKeyword || undefined, p); }}>
|
||||
<ChevronLeft className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
<span className="px-2 text-xs font-medium">{sourcePage} / {Math.max(1, Math.ceil(sourceTotalCount / sourcePageSize))}</span>
|
||||
<Button variant="outline" size="icon" className="h-7 w-7" disabled={sourcePage >= Math.ceil(sourceTotalCount / sourcePageSize)}
|
||||
onClick={() => { const p = sourcePage + 1; setSourcePage(p); loadSourceData(modalInboundType, sourceKeyword || undefined, p); }}>
|
||||
<ChevronRight className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
<Button variant="outline" size="icon" className="h-7 w-7" disabled={sourcePage >= Math.ceil(sourceTotalCount / sourcePageSize)}
|
||||
onClick={() => { const p = Math.ceil(sourceTotalCount / sourcePageSize); setSourcePage(p); loadSourceData(modalInboundType, sourceKeyword || undefined, p); }}>
|
||||
<ChevronsRight className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</ResizablePanel>
|
||||
|
||||
<ResizableHandle withHandle />
|
||||
<ResizableHandle withHandle onPointerDown={(e) => e.stopPropagation()} />
|
||||
|
||||
{/* 우측: 입고 정보 + 선택 품목 */}
|
||||
<ResizablePanel defaultSize={40} minSize={25}>
|
||||
|
|
@ -1030,7 +1104,7 @@ function SourcePurchaseOrderTable({
|
|||
|
||||
return (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableHeader className="sticky top-0 bg-background z-10">
|
||||
<TableRow className="text-[11px]">
|
||||
<TableHead className="w-[40px] p-2" />
|
||||
<TableHead className="p-2">발주번호</TableHead>
|
||||
|
|
@ -1109,7 +1183,7 @@ function SourceShipmentTable({
|
|||
|
||||
return (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableHeader className="sticky top-0 bg-background z-10">
|
||||
<TableRow className="text-[11px]">
|
||||
<TableHead className="w-[40px] p-2" />
|
||||
<TableHead className="p-2">출하번호</TableHead>
|
||||
|
|
@ -1186,7 +1260,7 @@ function SourceItemTable({
|
|||
|
||||
return (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableHeader className="sticky top-0 bg-background z-10">
|
||||
<TableRow className="text-[11px]">
|
||||
<TableHead className="w-[40px] p-2" />
|
||||
<TableHead className="p-2">품목</TableHead>
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ export default function SalesItemPage() {
|
|||
const [customerLoading, setCustomerLoading] = useState(false);
|
||||
|
||||
// 카테고리
|
||||
const [categoryOptions, setCategoryOptions] = useState<Record<string, { code: string; label: string }[]>>({});
|
||||
const [categoryOptions, setCategoryOptions] = useState<Record<string, { code: string; label: string; isDefault?: boolean }[]>>({});
|
||||
|
||||
// 거래처 추가 모달
|
||||
const [custSelectOpen, setCustSelectOpen] = useState(false);
|
||||
|
|
@ -125,11 +125,11 @@ export default function SalesItemPage() {
|
|||
// 카테고리 로드
|
||||
useEffect(() => {
|
||||
const load = async () => {
|
||||
const optMap: Record<string, { code: string; label: string }[]> = {};
|
||||
const flatten = (vals: any[]): { code: string; label: string }[] => {
|
||||
const result: { code: string; label: string }[] = [];
|
||||
const optMap: Record<string, { code: string; label: string; isDefault?: boolean }[]> = {};
|
||||
const flatten = (vals: any[]): { code: string; label: string; isDefault?: boolean }[] => {
|
||||
const result: { code: string; label: string; isDefault?: boolean }[] = [];
|
||||
for (const v of vals) {
|
||||
result.push({ code: v.valueCode, label: v.valueLabel });
|
||||
result.push({ code: v.valueCode, label: v.valueLabel, isDefault: v.isDefault });
|
||||
if (v.children?.length) result.push(...flatten(v.children));
|
||||
}
|
||||
return result;
|
||||
|
|
@ -164,7 +164,11 @@ export default function SalesItemPage() {
|
|||
const fetchItems = useCallback(async () => {
|
||||
setItemLoading(true);
|
||||
try {
|
||||
const filters = searchFilters.map((f) => ({ columnName: f.columnName, operator: f.operator, value: f.value }));
|
||||
const filters: { columnName: string; operator: string; value: any }[] = searchFilters.map((f) => ({ columnName: f.columnName, operator: f.operator, value: f.value }));
|
||||
|
||||
// 판매품목 division 필터 (다중값 컬럼이므로 contains로 매칭)
|
||||
filters.push({ columnName: "division", operator: "contains", value: "CAT_DIV_SALES" });
|
||||
|
||||
const res = await apiClient.post(`/table-management/tables/${ITEM_TABLE}/data`, {
|
||||
page: 1, size: 500,
|
||||
dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogD
|
|||
import { Label } from "@/components/ui/label";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable";
|
||||
import { Plus, Trash2, RotateCcw, Save, X, ChevronDown, ChevronRight, ChevronLeft, Truck, Search, Loader2, FileSpreadsheet } from "lucide-react";
|
||||
import { Plus, Trash2, RotateCcw, Save, X, ChevronDown, ChevronRight, ChevronLeft, ChevronsLeft, ChevronsRight, Truck, Search, Loader2, FileSpreadsheet } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { FormDatePicker } from "@/components/screen/filters/FormDatePicker";
|
||||
import {
|
||||
|
|
@ -117,7 +117,7 @@ export default function ShippingOrderPage() {
|
|||
const [sourceLoading, setSourceLoading] = useState(false);
|
||||
const [selectedItems, setSelectedItems] = useState<SelectedItem[]>([]);
|
||||
const [sourcePage, setSourcePage] = useState(1);
|
||||
const [sourcePageSize] = useState(20);
|
||||
const [sourcePageSize, setSourcePageSize] = useState(20);
|
||||
const [sourceTotalCount, setSourceTotalCount] = useState(0);
|
||||
|
||||
// 텍스트 입력 debounce (500ms)
|
||||
|
|
@ -592,6 +592,8 @@ export default function ShippingOrderPage() {
|
|||
description={isEditMode ? "출하지시 정보를 수정합니다." : "왼쪽에서 데이터를 선택하고 오른쪽에서 출하지시 정보를 입력하세요."}
|
||||
defaultMaxWidth="max-w-[90vw]"
|
||||
defaultWidth="w-[1400px]"
|
||||
className="h-[90vh]"
|
||||
contentClassName="overflow-hidden flex flex-col"
|
||||
footer={
|
||||
<>
|
||||
<Button variant="outline" onClick={() => setIsModalOpen(false)}>취소</Button>
|
||||
|
|
@ -694,10 +696,28 @@ export default function ShippingOrderPage() {
|
|||
{/* 페이징 */}
|
||||
{sourceTotalCount > 0 && (
|
||||
<div className="px-4 py-2 border-t bg-muted/10 flex items-center justify-between shrink-0">
|
||||
<span className="text-xs text-muted-foreground">
|
||||
총 {sourceTotalCount}건 중 {(sourcePage - 1) * sourcePageSize + 1}-{Math.min(sourcePage * sourcePageSize, sourceTotalCount)}건
|
||||
</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-muted-foreground text-[11px]">표시:</span>
|
||||
<Input
|
||||
type="number"
|
||||
min={1}
|
||||
max={500}
|
||||
value={sourcePageSize}
|
||||
onChange={(e) => {
|
||||
const v = parseInt(e.target.value, 10);
|
||||
if (v > 0) { setSourcePageSize(v); setSourcePage(1); fetchSourceData(1); }
|
||||
}}
|
||||
className="h-7 w-[60px] text-center text-[11px]"
|
||||
/>
|
||||
<span className="text-muted-foreground text-[11px]">
|
||||
총 {sourceTotalCount}건
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Button variant="outline" size="icon" className="h-7 w-7" disabled={sourcePage <= 1}
|
||||
onClick={() => { setSourcePage(1); fetchSourceData(1); }}>
|
||||
<ChevronsLeft className="w-3.5 h-3.5" />
|
||||
</Button>
|
||||
<Button variant="outline" size="icon" className="h-7 w-7" disabled={sourcePage <= 1}
|
||||
onClick={() => { const p = sourcePage - 1; setSourcePage(p); fetchSourceData(p); }}>
|
||||
<ChevronLeft className="w-3.5 h-3.5" />
|
||||
|
|
@ -707,13 +727,17 @@ export default function ShippingOrderPage() {
|
|||
onClick={() => { const p = sourcePage + 1; setSourcePage(p); fetchSourceData(p); }}>
|
||||
<ChevronRight className="w-3.5 h-3.5" />
|
||||
</Button>
|
||||
<Button variant="outline" size="icon" className="h-7 w-7" disabled={sourcePage >= Math.ceil(sourceTotalCount / sourcePageSize)}
|
||||
onClick={() => { const p = Math.ceil(sourceTotalCount / sourcePageSize); setSourcePage(p); fetchSourceData(p); }}>
|
||||
<ChevronsRight className="w-3.5 h-3.5" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</ResizablePanel>
|
||||
|
||||
<ResizableHandle withHandle />
|
||||
<ResizableHandle withHandle onPointerDown={(e) => e.stopPropagation()} />
|
||||
|
||||
{/* 오른쪽: 폼 */}
|
||||
<ResizablePanel defaultSize={45} minSize={30}>
|
||||
|
|
|
|||
Loading…
Reference in New Issue