From e484964b1d78cedabbe182c789a8e4c9b53e29d3 Mon Sep 17 00:00:00 2001 From: joonhoekim <26rote@gmail.com> Date: Thu, 29 May 2025 05:12:36 +0000 Subject: (김준회) 기술영업 조선 RFQ 파일첨부 및 채팅 기능 구현 / menuConfig 수정 (벤더 기술영업) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/techsales-rfq/table/create-rfq-dialog.tsx | 244 +++++++++++++++++++++----- 1 file changed, 200 insertions(+), 44 deletions(-) (limited to 'lib/techsales-rfq/table/create-rfq-dialog.tsx') diff --git a/lib/techsales-rfq/table/create-rfq-dialog.tsx b/lib/techsales-rfq/table/create-rfq-dialog.tsx index cc652b44..5faa3a0b 100644 --- a/lib/techsales-rfq/table/create-rfq-dialog.tsx +++ b/lib/techsales-rfq/table/create-rfq-dialog.tsx @@ -197,20 +197,60 @@ export function CreateRfqDialog({ onCreated }: CreateRfqDialogProps) { throw new Error("로그인이 필요합니다") } - // 자재코드(item_code) 배열을 materialGroupCodes로 전달 - const result = await createTechSalesRfq({ - biddingProjectId: data.biddingProjectId, - materialGroupCodes: data.materialCodes, // item_code를 자재코드로 사용 - createdBy: Number(session.user.id), - dueDate: data.dueDate, + // 선택된 아이템들을 아이템명(itemList)으로 그룹핑 + const groupedItems = selectedItems.reduce((groups, item) => { + const actualItemName = item.itemList // 실제 조선 아이템명 + if (!actualItemName) { + throw new Error(`아이템 "${item.itemCode}"의 아이템명(itemList)이 없습니다.`) + } + if (!groups[actualItemName]) { + groups[actualItemName] = [] + } + groups[actualItemName].push(item) + return groups + }, {} as Record) + + const rfqGroups = Object.entries(groupedItems).map(([actualItemName, items]) => { + const itemCodes = items.map(item => item.itemCode) // 자재그룹코드들 + const joinedItemCodes = itemCodes.join(',') + return { + actualItemName, + items, + itemCodes, + joinedItemCodes, + codeLength: joinedItemCodes.length, + isOverLimit: joinedItemCodes.length > 255 + } }) + + // 255자 초과 그룹 확인 + const overLimitGroups = rfqGroups.filter(group => group.isOverLimit) + if (overLimitGroups.length > 0) { + const groupNames = overLimitGroups.map(g => `"${g.actualItemName}" (${g.codeLength}자)`).join(', ') + throw new Error(`다음 아이템 그룹의 자재코드가 255자를 초과합니다: ${groupNames}`) + } + + // 각 그룹별로 RFQ 생성 + const createPromises = rfqGroups.map(group => + createTechSalesRfq({ + biddingProjectId: data.biddingProjectId, + materialGroupCodes: [group.joinedItemCodes], // 그룹화된 자재코드들 + createdBy: Number(session.user.id), + dueDate: data.dueDate, + }) + ) + + const results = await Promise.all(createPromises) - if (result.error) { - throw new Error(result.error) + // 오류 확인 + const errors = results.filter(result => result.error) + if (errors.length > 0) { + throw new Error(errors.map(e => e.error).join(', ')) } // 성공적으로 생성되면 다이얼로그 닫기 및 메시지 표시 - toast.success(`${result.data?.length || 0}개의 RFQ가 성공적으로 생성되었습니다`) + const totalRfqs = results.reduce((sum, result) => sum + (result.data?.length || 0), 0) + toast.success(`${rfqGroups.length}개 아이템 그룹으로 총 ${totalRfqs}개의 RFQ가 성공적으로 생성되었습니다`) setIsDialogOpen(false) form.reset({ biddingProjectId: undefined, @@ -423,38 +463,45 @@ export function CreateRfqDialog({ onCreated }: CreateRfqDialogProps) { 아이템을 불러오는 중... ) : availableItems.length > 0 ? ( - availableItems.map((item) => { - const isSelected = selectedItems.some(selected => selected.id === item.id) - return ( -
handleItemToggle(item)} - > -
- {isSelected ? ( - - ) : ( - + [...availableItems] + .sort((a, b) => { + // itemList 기준으로 정렬 (없는 경우 itemName 사용, 둘 다 없으면 맨 뒤로) + const aName = a.itemList || a.itemName || 'zzz' + const bName = b.itemList || b.itemName || 'zzz' + return aName.localeCompare(bName, 'ko', { numeric: true }) + }) + .map((item) => { + const isSelected = selectedItems.some(selected => selected.id === item.id) + return ( +
-
- {item.itemList || item.itemName} -
-
- {item.itemCode} • {item.description || '설명 없음'} -
-
- 공종: {item.workType} • 선종: {item.shipTypes} + onClick={() => handleItemToggle(item)} + > +
+ {isSelected ? ( + + ) : ( + + )} +
+
+ {item.itemList || item.itemName || '아이템명 없음'} +
+
+ {item.itemCode} • {item.description || '설명 없음'} +
+
+ 공종: {item.workType} • 선종: {item.shipTypes} +
-
- ) - }) + ) + }) ) : (
{itemSearchQuery ? "검색 결과가 없습니다" : "아이템이 없습니다"} @@ -480,7 +527,7 @@ export function CreateRfqDialog({ onCreated }: CreateRfqDialogProps) { variant="secondary" className="flex items-center gap-1" > - {item.itemList || item.itemName} ({item.itemCode}) + {item.itemList || item.itemName || '아이템명 없음'} ({item.itemCode}) handleRemoveItem(item.id)} @@ -498,19 +545,93 @@ export function CreateRfqDialog({ onCreated }: CreateRfqDialogProps) { )} /> + + {/* RFQ 그룹핑 미리보기 */} + {selectedItems.length > 0 && ( +
+ 생성될 RFQ 그룹 미리보기 +
+ {(() => { + // 아이템명(itemList)으로 그룹핑 + const groupedItems = selectedItems.reduce((groups, item) => { + const actualItemName = item.itemList // 실제 조선 아이템명 + if (!actualItemName) { + return groups // itemList가 없는 경우 제외 + } + if (!groups[actualItemName]) { + groups[actualItemName] = [] + } + groups[actualItemName].push(item) + return groups + }, {} as Record) + + const rfqGroups = Object.entries(groupedItems).map(([actualItemName, items]) => { + const itemCodes = items.map(item => item.itemCode) // 자재그룹코드들 + const joinedItemCodes = itemCodes.join(',') + return { + actualItemName, + items, + itemCodes, + joinedItemCodes, + codeLength: joinedItemCodes.length, + isOverLimit: joinedItemCodes.length > 255 + } + }) + + return ( +
+
+ 총 {rfqGroups.length}개의 RFQ가 생성됩니다 (아이템명별로 그룹핑) +
+ {rfqGroups.map((group, index) => ( +
+
+
+ RFQ #{index + 1}: {group.actualItemName} +
+ + {group.itemCodes.length}개 자재코드 ({group.codeLength}/255자) + +
+
+ 자재코드: {group.joinedItemCodes} +
+ {group.isOverLimit && ( +
+ ⚠️ 자재코드 길이가 255자를 초과합니다. 일부 아이템을 제거해주세요. +
+ )} +
+ 포함된 아이템: {group.items.map(item => `${item.itemCode}`).join(', ')} +
+
+ ))} +
+ ) + })()} +
+
+ )}
)} {/* 안내 메시지 */} - {selectedProject && ( + {/* {selectedProject && (

• 공종별 조선 아이템을 선택하세요.

-

• 선택된 아이템의 자재코드(item_code)별로 개별 RFQ가 생성됩니다.

-

• 아이템 코드가 자재 그룹 코드로 사용됩니다.

+

같은 아이템명의 다른 자재코드들은 하나의 RFQ로 그룹핑됩니다.

+

• 그룹핑된 자재코드들은 comma로 구분되어 저장됩니다.

+

• 자재코드 길이는 최대 255자까지 가능합니다.

• 마감일은 벤더가 견적을 제출해야 하는 날짜입니다.

- )} + )} */}
-- cgit v1.2.3