개발관리>PART 등록·조회 + E-BOM 등록 검색 폼 select2-part 자동완성 (운영판 1:1)

3 페이지 검색 폼이 단순 Input + 품번/품명 LIKE 였음. 운영판 wace 는 class="select2-part"
자동완성 (initPartSelect2Ajax) + 한쪽 선택 시 다른 쪽 자동 채움.

수정 페이지 (모두 DevPartSelect 적용):
- part-regist/page.tsx (M1 PART 등록 — wace partMngTempList.jsp)
- part-search/page.tsx (M2 PART 조회 — wace partMngList.jsp)
- ebom-regist/page.tsx (M3 E-BOM 등록 — wace structureList.jsp)

동작:
- 품번/품명 양쪽 DevPartSelect (part_mng IS_LAST='1' 캐시)
- 품번 선택 → 품명 자동 채움 (row.part_name)
- 품명 선택 → 품번 자동 채움 (row.part_no)
- setFilter 함수형 업데이트로 동시 setState 충돌 방지

ebom-search 와 동일 패턴 통일 — DevPartSelect 1개 컴포넌트로 4 페이지 공유.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hjjeong
2026-05-13 12:28:38 +09:00
parent b5bc7f3630
commit 7d67a5ab1d
3 changed files with 52 additions and 34 deletions
@@ -17,6 +17,7 @@ import { DataGrid, DataGridColumn } from "@/components/common/DataGrid";
import { CommCodeSelect } from "@/components/common/CommCodeSelect";
import { devBomApi, BomReportListFilter, BomReportRow } from "@/lib/api/devBom";
import { BomReportStatusDialog } from "@/components/development/BomReportStatusDialog";
import { DevPartSelect } from "@/components/development/DevPartSelect";
import { BomReportExcelImportDialog } from "@/components/development/BomReportExcelImportDialog";
import { BomReportTreeDialog } from "@/components/development/BomReportTreeDialog";
@@ -135,19 +136,24 @@ export default function EbomRegistPage() {
<option key={o.code} value={o.code}>{o.label}</option>)}
</select>
</Field>
{/* wace structureList.jsp 1:1 — select2-part 자동완성 (양방향 동기) */}
<Field label="품번">
<Input
value={filter.search_part_no ?? ""}
onChange={(e) => setFilter({ ...filter, search_part_no: e.target.value })}
placeholder="품번 LIKE"
/>
<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,
}))} />
</Field>
<Field label="품명">
<Input
value={filter.search_part_name ?? ""}
onChange={(e) => setFilter({ ...filter, search_part_name: e.target.value })}
placeholder="품명 LIKE"
/>
<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,
}))} />
</Field>
</div>
<div className="mt-3 flex items-center justify-between">
@@ -18,6 +18,7 @@ import { devPartApi, PartListFilter, PartRow } from "@/lib/api/devPart";
import { PartFormDialog } from "@/components/development/PartFormDialog";
import { PartDetailDialog } from "@/components/development/PartDetailDialog";
import { PartExcelImportDialog } from "@/components/development/PartExcelImportDialog";
import { DevPartSelect } from "@/components/development/DevPartSelect";
// wace 23셀 + 부속 (PARENT_PART_INFO/PARTNER_TITLE/Q_QTY)
const GRID_COLUMNS: DataGridColumn[] = [
@@ -157,21 +158,26 @@ export default function PartRegistPage() {
{/* 검색폼 — wace partMngTempList.jsp 활성 2필드 */}
<div className="border-b bg-card px-4 py-3">
<div className="flex flex-wrap items-end gap-4">
<div className="min-w-[200px]">
{/* wace partMngTempList.jsp 1:1 — select2-part 자동완성 (양방향 동기) */}
<div className="min-w-[220px]">
<Label className="mb-1 block text-xs text-muted-foreground"></Label>
<Input
value={filter.search_part_no ?? ""}
onChange={(e) => setFilter({ ...filter, search_part_no: e.target.value })}
placeholder="품번 LIKE"
/>
<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-[200px]">
<div className="min-w-[220px]">
<Label className="mb-1 block text-xs text-muted-foreground"></Label>
<Input
value={filter.search_part_name ?? ""}
onChange={(e) => setFilter({ ...filter, search_part_name: e.target.value })}
placeholder="품명 LIKE"
/>
<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"
@@ -18,6 +18,7 @@ import { devPartApi, PartListFilter, PartRow } from "@/lib/api/devPart";
import { PartFormDialog } from "@/components/development/PartFormDialog";
import { PartDetailDialog } from "@/components/development/PartDetailDialog";
import { PartExcelImportDialog } from "@/components/development/PartExcelImportDialog";
import { DevPartSelect } from "@/components/development/DevPartSelect";
const GRID_COLUMNS: DataGridColumn[] = [
{ key: "part_no", label: "품번", width: "w-[140px]", frozen: true },
@@ -125,21 +126,26 @@ export default function PartSearchPage() {
<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">
<div className="min-w-[200px]">
{/* wace partMngList.jsp 1:1 — select2-part 자동완성 (양방향 동기) */}
<div className="min-w-[220px]">
<Label className="mb-1 block text-xs text-muted-foreground"></Label>
<Input
value={filter.search_part_no ?? ""}
onChange={(e) => setFilter({ ...filter, search_part_no: e.target.value })}
placeholder="품번 LIKE"
/>
<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-[200px]">
<div className="min-w-[220px]">
<Label className="mb-1 block text-xs text-muted-foreground"></Label>
<Input
value={filter.search_part_name ?? ""}
onChange={(e) => setFilter({ ...filter, search_part_name: e.target.value })}
placeholder="품명 LIKE"
/>
<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"