공용 — 영업 4 + 프로젝트 2 + 개발 5메뉴 PageHeader + CompactFilterBar 일괄 적용
총 11개 페이지를 동일한 페이지 구조 표준으로 마이그레이션. 페이지 메뉴명은 PageHeader 가 useMenu() 자동 매칭, 검색 영역은 CompactFilterBar/CompactFilterField, 날짜 범위는 CompactDateRange 로 통일. 모든 자체 grid 검색폼 + 자체 h1 + 자체 액션 버튼 그룹 제거. 영업관리 4: - sales/estimate (견적관리) — 7필드 + 결재상태 SmartSelect - sales/order (주문서관리) — 9필드 (날짜 2종) - sales/sale (판매관리) — 10필드 (출하지시상태 SmartSelect) - sales/revenue (매출관리) — 11필드 (날짜 3종) 프로젝트관리 2: - project/progress (진행관리) — 11필드 (그리드 6→자동 wrap) - project/wbs-template (제품구분_WBS관리) — 1필드 개발관리 5: - development/part-regist (PART 등록) — 2필드 (자동완성) + 7 액션 - development/part-search (PART 조회) — 2필드 + 5 액션 - development/ebom-regist (E-BOM 등록) — 4필드 + 3 액션 (잔재 Field helper 제거) - development/ebom-search (E-BOM 조회) — 3필드 + 4 액션 (정/역전개) - development/change-list (설계변경 리스트) — 8필드 (read-only) DB: - menu_info.menu_desc 11개 메뉴 보강 (PageHeader 자동 표시) - docs/migration/common/menu_desc_sync.sql (멱등 UPDATE) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,10 +7,10 @@
|
||||
|
||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { PageHeader } from "@/components/common/PageHeader";
|
||||
import { CompactFilterBar, CompactFilterField } from "@/components/common/CompactFilterBar";
|
||||
import {
|
||||
Search, Loader2, RotateCcw, Plus, Pencil, Trash2, FileSpreadsheet,
|
||||
Plus, Pencil, Trash2, FileSpreadsheet,
|
||||
} from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import { DataGrid, DataGridColumn } from "@/components/common/DataGrid";
|
||||
@@ -124,63 +124,55 @@ export default function PartSearchPage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col">
|
||||
<div className="border-b bg-card px-4 py-3">
|
||||
<div className="flex flex-wrap items-end gap-4">
|
||||
{/* wace partMngList.jsp 1:1 — select2-part 자동완성 (양방향 동기) */}
|
||||
<div className="min-w-[220px]">
|
||||
<Label className="mb-1 block text-xs text-muted-foreground">품번</Label>
|
||||
<DevPartSelect mode="partNo"
|
||||
value={filter.search_part_no ?? ""}
|
||||
onValueChange={(v, row) => setFilter((prev) => ({
|
||||
...prev,
|
||||
search_part_no: v,
|
||||
search_part_name: row?.part_name ?? prev.search_part_name,
|
||||
}))} />
|
||||
</div>
|
||||
<div className="min-w-[220px]">
|
||||
<Label className="mb-1 block text-xs text-muted-foreground">품명</Label>
|
||||
<DevPartSelect mode="partName"
|
||||
value={filter.search_part_name ?? ""}
|
||||
onValueChange={(v, row) => setFilter((prev) => ({
|
||||
...prev,
|
||||
search_part_name: v,
|
||||
search_part_no: row?.part_no ?? prev.search_part_no,
|
||||
}))} />
|
||||
</div>
|
||||
<div className="ml-auto flex items-end gap-2">
|
||||
<Button variant="outline" size="sm"
|
||||
onClick={() => { setFilter(EMPTY_FILTER); fetchList(EMPTY_FILTER); }}>
|
||||
<RotateCcw className="h-4 w-4" /><span className="ml-1">초기화</span>
|
||||
</Button>
|
||||
<Button size="sm" onClick={() => fetchList()} disabled={loading}>
|
||||
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <Search className="h-4 w-4" />}
|
||||
<span className="ml-1">조회</span>
|
||||
</Button>
|
||||
<Button size="sm" variant="default" onClick={handleCreate}>
|
||||
<Plus className="h-4 w-4" /><span className="ml-1">등록</span>
|
||||
</Button>
|
||||
<Button size="sm" variant="secondary" onClick={handleEdit}
|
||||
disabled={checkedIds.length !== 1}>
|
||||
<Pencil className="h-4 w-4" /><span className="ml-1">수정</span>
|
||||
</Button>
|
||||
<Button size="sm" variant="destructive" onClick={handleDelete}
|
||||
disabled={checkedIds.length === 0}>
|
||||
<Trash2 className="h-4 w-4" /><span className="ml-1">삭제</span>
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onClick={() => setExcelOpen(true)}>
|
||||
<FileSpreadsheet className="h-4 w-4" /><span className="ml-1">Excel Upload</span>
|
||||
</Button>
|
||||
{/* M2 조회 — partNoList 미전달: IS_LAST='1' 전체 part_mng 매칭 (페이지 밖도 허용) */}
|
||||
<PartDrawingMultiUploadButton onUploaded={() => fetchList()} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-2 text-xs text-muted-foreground">
|
||||
총 {total.toLocaleString()}건 (M2: status = 'release')
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex h-full flex-col gap-2 p-2">
|
||||
<PageHeader actions={
|
||||
<>
|
||||
<Button size="sm" className="h-8 gap-1 text-xs" onClick={handleCreate}>
|
||||
<Plus className="h-3.5 w-3.5" />등록
|
||||
</Button>
|
||||
<Button size="sm" variant="secondary" className="h-8 gap-1 text-xs" onClick={handleEdit}
|
||||
disabled={checkedIds.length !== 1}>
|
||||
<Pencil className="h-3.5 w-3.5" />수정
|
||||
</Button>
|
||||
<Button size="sm" variant="destructive" className="h-8 gap-1 text-xs" onClick={handleDelete}
|
||||
disabled={checkedIds.length === 0}>
|
||||
<Trash2 className="h-3.5 w-3.5" />삭제
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" className="h-8 gap-1 text-xs" onClick={() => setExcelOpen(true)}>
|
||||
<FileSpreadsheet className="h-3.5 w-3.5" />Excel Upload
|
||||
</Button>
|
||||
{/* M2 조회 — partNoList 미전달: IS_LAST='1' 전체 part_mng 매칭 (페이지 밖도 허용) */}
|
||||
<PartDrawingMultiUploadButton onUploaded={() => fetchList()} />
|
||||
</>
|
||||
} />
|
||||
|
||||
<div className="min-h-0 flex-1 p-2">
|
||||
<CompactFilterBar
|
||||
loading={loading}
|
||||
onSearch={() => fetchList()}
|
||||
onReset={() => { setFilter(EMPTY_FILTER); fetchList(EMPTY_FILTER); }}
|
||||
totalText={<>총 {total.toLocaleString()}건 (M2: status = 'release')</>}
|
||||
>
|
||||
<CompactFilterField label="품번" width={220}>
|
||||
<DevPartSelect mode="partNo"
|
||||
value={filter.search_part_no ?? ""}
|
||||
onValueChange={(v, row) => setFilter((prev) => ({
|
||||
...prev,
|
||||
search_part_no: v,
|
||||
search_part_name: row?.part_name ?? prev.search_part_name,
|
||||
}))} />
|
||||
</CompactFilterField>
|
||||
<CompactFilterField label="품명" width={220}>
|
||||
<DevPartSelect mode="partName"
|
||||
value={filter.search_part_name ?? ""}
|
||||
onValueChange={(v, row) => setFilter((prev) => ({
|
||||
...prev,
|
||||
search_part_name: v,
|
||||
search_part_no: row?.part_no ?? prev.search_part_no,
|
||||
}))} />
|
||||
</CompactFilterField>
|
||||
</CompactFilterBar>
|
||||
|
||||
<div className="min-h-0 flex-1">
|
||||
<DataGrid
|
||||
columns={columns}
|
||||
data={gridRows}
|
||||
|
||||
Reference in New Issue
Block a user