@@ -17,14 +17,15 @@ const NewMaterialsPage = ({
} ) => {
const [ materials , setMaterials ] = useState ( [ ] ) ;
const [ loading , setLoading ] = useState ( true ) ;
const [ selectedCategory , setSelectedCategory ] = useState ( 'PIPE ' ) ;
const [ selectedCategory , setSelectedCategory ] = useState ( 'ALL ' ) ;
const [ selectedMaterials , setSelectedMaterials ] = useState ( new Set ( ) ) ;
const [ viewMode , setViewMode ] = useState ( 'detailed' ) ; // 'detailed' or 'simple'
const [ availableRevisions , setAvailableRevisions ] = useState ( [ ] ) ;
const [ currentRevision , setCurrentRevision ] = useState ( revision || 'Rev.0' ) ;
// 사용자 요구사항 상태 관리
const [ userRequirements , setUserRequirements ] = useState ( { } ) ; // materialId: requirement 형태
const [ userRequirements , setUserRequirements ] = useState ( { } ) ;
// materialId: requirement 형태
const [ savingRequirements , setSavingRequirements ] = useState ( false ) ;
// 같은 BOM의 다른 리비전들 조회
@@ -101,16 +102,28 @@ const NewMaterialsPage = ({
params : { file _id : parseInt ( id ) }
} ) ;
if ( response . data ? . success && response . data ? . requirements ) {
if ( response . data && Array . isArray ( response . data ) ) {
const requirements = { } ;
resp onse. data . requirements . forEach ( req => {
c onsol e. log ( '📦 API 응답 데이터:' , response . data ) ;
response . data . forEach ( req => {
// material_id를 키로 사용하여 요구사항 저장
if ( req . material _id ) {
requirements [ req . material _id ] = req . requirement _description || req . requirement _title || '' ;
console . log ( ` 📥 로드된 요구사항: 자재 ID ${ req . material _id } = " ${ requirements [ req . material _id ] } " ` ) ;
} else {
console . warn ( '⚠️ material_id가 없는 요구사항:' , req ) ;
}
} ) ;
console . log ( '🔄 setUserRequirements 호출 전 상태:' , userRequirements ) ;
setUserRequirements ( requirements ) ;
console . log ( '🔄 setUserRequirements 호출 후 새로운 상태:' , requirements ) ;
console . log ( '✅ 사용자 요구사항 로드 완료:' , Object . keys ( requirements ) . length , '개' ) ;
// 상태 업데이트 확인을 위한 지연된 로그
setTimeout ( ( ) => {
console . log ( '⏰ 1초 후 실제 userRequirements 상태:' , userRequirements ) ;
} , 1000 ) ;
}
} catch ( error ) {
console . error ( '❌ 사용자 요구사항 로딩 실패:' , error ) ;
@@ -122,11 +135,30 @@ const NewMaterialsPage = ({
const saveUserRequirements = async ( ) => {
try {
setSavingRequirements ( true ) ;
// 강제 테스트: userRequirements가 비어있으면 첫 번째 자재에 테스트 데이터 추가
let currentRequirements = { ... userRequirements } ;
if ( Object . keys ( currentRequirements ) . length === 0 && materials . length > 0 ) {
const firstMaterialId = materials [ 0 ] . id ;
currentRequirements [ firstMaterialId ] = '강제 테스트 요구사항' ;
setUserRequirements ( currentRequirements ) ;
console . log ( '⚠️ 테스트 데이터 강제 추가:' , currentRequirements ) ;
}
// 디버깅: 현재 userRequirements 상태 확인
console . log ( '💾 저장 시작 - 현재 userRequirements:' , currentRequirements ) ;
console . log ( '💾 저장 시작 - userRequirements 키 개수:' , Object . keys ( currentRequirements ) . length ) ;
console . log ( '💾 사용자 요구사항 저장 중...' , userRequirements ) ;
console . log ( '📋 전체 userRequirements 객체:' , Object . keys ( userRequirements ) . length , '개' ) ;
// 요구사항이 있는 자재들만 저장
const requirementsToSave = Object . entries ( user Requirements)
. filter ( ( [ materialId , requirement ] ) => requirement && requirement . trim ( ) )
const requirementsToSave = Object . entries ( current Requirements)
. filter ( ( [ materialId , requirement ] ) => {
const hasValue = requirement && requirement . trim ( ) && requirement . trim ( ) . length > 0 ;
console . log ( ` 🔍 자재 ID ${ materialId } : " ${ requirement } " (길이: ${ requirement ? requirement . length : 0 } ) -> ${ hasValue ? '저장' : '제외' } ` ) ;
return hasValue ;
} )
. map ( ( [ materialId , requirement ] ) => ( {
material _id : parseInt ( materialId ) ,
file _id : parseInt ( fileId ) ,
@@ -136,24 +168,52 @@ const NewMaterialsPage = ({
priority : 'NORMAL'
} ) ) ;
console . log ( '📝 저장할 요구사항 개수:' , requirementsToSave . length ) ;
if ( requirementsToSave . length === 0 ) {
alert ( '저장할 요구사항이 없습니다.' ) ;
return ;
}
// 기존 요구사항 삭제 후 새로 저장
await api . delete ( ` /files/user-requirements ` , {
params : { file _id : parseInt ( fileId ) }
} ) ;
console . log ( '🗑️ 기존 요구사항 삭제 중...' , { file _id : parseInt ( fileId ) } ) ;
console . log ( '🌐 API Base URL:' , api . defaults . baseURL ) ;
console . log ( '🔑 Authorization Header:' , api . defaults . headers . Authorization ) ;
try {
const deleteResponse = await api . delete ( ` /files/user-requirements ` , {
params : { file _id : parseInt ( fileId ) }
} ) ;
console . log ( '✅ 기존 요구사항 삭제 완료:' , deleteResponse . data ) ;
} catch ( deleteError ) {
console . error ( '❌ 기존 요구사항 삭제 실패:' , deleteError ) ;
console . error ( '❌ 삭제 에러 상세:' , deleteError . response ? . data ) ;
console . error ( '❌ 삭제 에러 전체:' , deleteError ) ;
// 삭제 실패해도 계속 진행
}
// 새 요구사항들 저장
console . log ( '🚀 API 호출 시작 - 저장할 데이터:' , requirementsToSave ) ;
for ( const req of requirementsToSave ) {
await api . post ( '/files/user-requirements ' , req ) ;
console . log ( '🚀 개별 API 호출: ' , req ) ;
try {
const response = await api . post ( '/files/user-requirements' , req ) ;
console . log ( '✅ API 응답:' , response . data ) ;
} catch ( apiError ) {
console . error ( '❌ API 호출 실패:' , apiError ) ;
console . error ( '❌ API 에러 상세:' , apiError . response ? . data ) ;
throw apiError ;
}
}
alert ( ` ${ requirementsToSave . length } 개의 사용자 요구사항이 저장되었습니다. ` ) ;
console . log ( '✅ 사용자 요구사항 저장 완료' ) ;
// 저장 후 다시 로드하여 최신 상태 반영
console . log ( '🔄 저장 완료 후 다시 로드 시작...' ) ;
await loadUserRequirements ( fileId ) ;
console . log ( '🔄 저장 완료 후 다시 로드 완료!' ) ;
} catch ( error ) {
console . error ( '❌ 사용자 요구사항 저장 실패:' , error ) ;
alert ( '사용자 요구사항 저장에 실패했습니다: ' + ( error . response ? . data ? . detail || error . message ) ) ;
@@ -164,10 +224,15 @@ const NewMaterialsPage = ({
// 사용자 요구사항 입력 핸들러
const handleUserRequirementChange = ( materialId , value ) => {
setUserRequirements ( prev => ( {
... prev ,
[ materialId ] : value
} ) ) ;
console . log ( ` 📝 사용자 요구사항 입력: 자재 ID ${ materialId } = " ${ value } " ` ) ;
setUserRequirements ( prev => {
const updated = {
... prev ,
[ materialId ] : value
} ;
console . log ( '🔄 업데이트된 userRequirements:' , updated ) ;
return updated ;
} ) ;
} ;
// 카테고리별 자재 수 계산
@@ -204,6 +269,78 @@ const NewMaterialsPage = ({
} ;
} ;
// 카테고리 표시명 매핑
const getCategoryDisplayName = ( category ) => {
const categoryMap = {
'SUPPORT' : 'U-BOLT' ,
'PIPE' : 'PIPE' ,
'FITTING' : 'FITTING' ,
'FLANGE' : 'FLANGE' ,
'VALVE' : 'VALVE' ,
'BOLT' : 'BOLT' ,
'GASKET' : 'GASKET' ,
'INSTRUMENT' : 'INSTRUMENT' ,
'UNKNOWN' : 'UNKNOWN'
} ;
return categoryMap [ category ] || category ;
} ;
// 니플 끝단 정보 추출
const extractNippleEndInfo = ( description ) => {
const descUpper = description . toUpperCase ( ) ;
// 니플 끝단 패턴들
const endPatterns = {
'PBE' : 'PBE' , // Plain Both End
'BBE' : 'BBE' , // Bevel Both End
'POE' : 'POE' , // Plain One End
'BOE' : 'BOE' , // Bevel One End
'TOE' : 'TOE' , // Thread One End
'SW X NPT' : 'SW× NPT' , // Socket Weld x NPT
'SW X SW' : 'SW× SW' , // Socket Weld x Socket Weld
'NPT X NPT' : 'NPT× NPT' , // NPT x NPT
} ;
for ( const [ pattern , display ] of Object . entries ( endPatterns ) ) {
if ( descUpper . includes ( pattern ) ) {
return display ;
}
}
return '' ;
} ;
// 볼트 추가요구사항 추출
const extractBoltAdditionalRequirements = ( description ) => {
const descUpper = description . toUpperCase ( ) ;
const additionalReqs = [ ] ;
// 표면처리 패턴들
const surfaceTreatments = {
'ELEC.GALV' : '전기아연도금' ,
'ELEC GALV' : '전기아연도금' ,
'GALVANIZED' : '아연도금' ,
'GALV' : '아연도금' ,
'HOT DIP GALV' : '용융아연도금' ,
'HDG' : '용융아연도금' ,
'ZINC PLATED' : '아연도금' ,
'ZINC' : '아연도금' ,
'STAINLESS' : '스테인리스' ,
'SS' : '스테인리스'
} ;
// 표면처리 확인
for ( const [ pattern , korean ] of Object . entries ( surfaceTreatments ) ) {
if ( descUpper . includes ( pattern ) ) {
additionalReqs . push ( korean ) ;
}
}
// 중복 제거
const uniqueReqs = [ ... new Set ( additionalReqs ) ] ;
return uniqueReqs . join ( ', ' ) ;
} ;
// 자재 정보 파싱
const parseMaterialInfo = ( material ) => {
const category = material . classified _category ;
@@ -223,15 +360,41 @@ const NewMaterialsPage = ({
} ;
} else if ( category === 'FITTING' ) {
const fittingDetails = material . fitting _details || { } ;
const fittingType = fittingDetails . fitting _type || '' ;
const fittingSubtype = fittingDetails . fitting _subtype || '' ;
const classificationDetails = material . classification _details || { } ;
// 개선된 분류기 결과 우선 사용
const fittingTypeInfo = classificationDetails . fitting _type || { } ;
const scheduleInfo = classificationDetails . schedule _info || { } ;
// 기존 필드와 새 필드 통합
const fittingType = fittingTypeInfo . type || fittingDetails . fitting _type || '' ;
const fittingSubtype = fittingTypeInfo . subtype || fittingDetails . fitting _subtype || '' ;
const mainSchedule = scheduleInfo . main _schedule || fittingDetails . schedule || '' ;
const redSchedule = scheduleInfo . red _schedule || '' ;
const hasDifferentSchedules = scheduleInfo . has _different _schedules || false ;
const description = material . original _description || '' ;
// 피팅 타입별 상세 표시
let displayType = '' ;
// CAP과 PLUG 먼저 확인 (fitting_type이 없을 수 있음)
if ( description . toUpperCase ( ) . includes ( 'CAP' ) ) {
// 개선된 분류기 결과 우선 표시
if ( fittingType === 'TEE' && fittingSubtype === 'REDUCING' ) {
displayType = 'TEE REDUCING' ;
} else if ( fittingType === 'REDUCER' && fittingSubtype === 'CONCENTRIC' ) {
displayType = 'REDUCER CONC' ;
} else if ( fittingType === 'REDUCER' && fittingSubtype === 'ECCENTRIC' ) {
displayType = 'REDUCER ECC' ;
} else if ( description . toUpperCase ( ) . includes ( 'TEE RED' ) ) {
// 기존 데이터의 TEE RED 패턴
displayType = 'TEE REDUCING' ;
} else if ( description . toUpperCase ( ) . includes ( 'RED CONC' ) ) {
// 기존 데이터의 RED CONC 패턴
displayType = 'REDUCER CONC' ;
} else if ( description . toUpperCase ( ) . includes ( 'RED ECC' ) ) {
// 기존 데이터의 RED ECC 패턴
displayType = 'REDUCER ECC' ;
} else if ( description . toUpperCase ( ) . includes ( 'CAP' ) ) {
// CAP: 연결 방식 표시 (예: CAP, NPT(F), 3000LB, ASTM A105)
if ( description . includes ( 'NPT(F)' ) ) {
displayType = 'CAP NPT(F)' ;
@@ -260,7 +423,13 @@ const NewMaterialsPage = ({
} else if ( fittingType === 'NIPPLE' ) {
// 니플: 길이와 끝단 가공 정보
const length = fittingDetails . length _mm || fittingDetails . avg _length _mm ;
displayType = l ength ? ` NIPPLE ${ length } mm ` : 'NIPPLE' ;
const endInfo = extractNippleEndInfo ( description ) ;
let nippleType = 'NIPPLE' ;
if ( length ) nippleType += ` ${ length } mm ` ;
if ( endInfo ) nippleType += ` ${ endInfo } ` ;
displayType = nippleType ;
} else if ( fittingType === 'ELBOW' ) {
// 엘보: 각도와 연결 방식
const angle = fittingSubtype === '90DEG' ? '90°' : fittingSubtype === '45DEG' ? '45°' : '' ;
@@ -295,11 +464,25 @@ const NewMaterialsPage = ({
pressure = ` ${ pressureMatch [ 1 ] } LB ` ;
}
// 스케줄 찾기
if ( description . includes ( 'SCH' ) ) {
const schMatch = description . match ( /SCH\s*(\d+[A-Z]*)/i ) ;
if ( schMatch ) {
schedule = ` SCH ${ schMatch [ 1 ] } ` ;
// 스케줄 표시 (분리 스케줄 지원)
if ( hasDifferentSchedules && mainSchedule && redSchedule ) {
// 분리 스케줄: "SCH 40 x SCH 80"
schedule = ` ${ mainSchedule } x ${ redSchedule } ` ;
} else if ( mainSchedule && mainSchedule !== 'UNKNOWN' ) {
// 단일 스케줄: "SCH 40"
schedule = mainSchedule ;
} else if ( description . includes ( 'SCH' ) ) {
// 기존 데이터에서 분리 스케줄 패턴 확인
const separatedSchMatch = description . match ( /SCH\s*(\d+[A-Z]*)\s*[xX× ]\s*SCH\s*(\d+[A-Z]*)/i ) ;
if ( separatedSchMatch ) {
// 분리 스케줄 발견: "SCH 40 x SCH 80"
schedule = ` SCH ${ separatedSchMatch [ 1 ] } x SCH ${ separatedSchMatch [ 2 ] } ` ;
} else {
// 단일 스케줄
const schMatch = description . match ( /SCH\s*(\d+[A-Z]*)/i ) ;
if ( schMatch ) {
schedule = ` SCH ${ schMatch [ 1 ] } ` ;
}
}
}
@@ -397,12 +580,37 @@ const NewMaterialsPage = ({
const qty = Math . round ( material . quantity || 0 ) ;
const safetyQty = Math . ceil ( qty * 1.05 ) ; // 5% 여유율
const purchaseQty = Math . ceil ( safetyQty / 4 ) * 4 ; // 4의 배수
// 볼트 길이 추출 (원본 설명에서)
const description = material . original _description || '' ;
let boltLength = '-' ;
// 길이 패턴 추출 (75 LG, 90.0000 LG, 50mm 등)
const lengthPatterns = [
/(\d+(?:\.\d+)?)\s*LG/i , // 75 LG, 90.0000 LG
/(\d+(?:\.\d+)?)\s*mm/i , // 50mm
/(\d+(?:\.\d+)?)\s*MM/i , // 50MM
/LG[,\s]*(\d+(?:\.\d+)?)/i // LG, 75 형태
] ;
for ( const pattern of lengthPatterns ) {
const match = description . match ( pattern ) ;
if ( match ) {
boltLength = ` ${ match [ 1 ] } mm ` ;
break ;
}
}
// 추가요구사항 추출 (ELEC.GALV 등)
const additionalReq = extractBoltAdditionalRequirements ( description ) ;
return {
type : 'BOLT' ,
subtype : material . bolt _details ? . bolt _type || '- ' ,
subtype : material . bolt _details ? . bolt _type || 'BOLT_GENERAL ' ,
size : material . size _spec || '-' ,
schedule : material . bolt _details ? . length || '-' ,
grade : material . material _grade || '-' ,
schedule : boltLength , // 길이 정보
grade : material . full _material _grade || material . material _grade || '-' ,
additionalReq : additionalReq , // 추가요구사항
quantity : purchaseQty ,
unit : 'SETS'
} ;
@@ -473,6 +681,9 @@ const NewMaterialsPage = ({
// 필터링된 자재 목록
const filteredMaterials = materials . filter ( material => {
if ( selectedCategory === 'ALL' ) {
return true ; // 전체 카테고리일 때는 모든 자재 표시
}
return material . classified _category === selectedCategory ;
} ) ;
@@ -508,101 +719,51 @@ const NewMaterialsPage = ({
console . log ( '📊 엑셀 내보내기:' , dataToExport . length , '개 항목' ) ;
// 카테고리별 컬럼 구성
// 새로운 엑셀 양식에 맞춘 컬럼 구성
const getExcelData = ( material ) => {
const info = parseMaterialInfo ( material ) ;
// 품목명 생성 (간단하게)
let itemName = '' ;
if ( selectedCategory === 'PIPE' ) {
return {
'종류' : info . type ,
'타입' : info . subtype ,
'크기' : info . size ,
'스케줄' : info . schedule ,
'재질' : info . grade ,
'추가요구' : '- ' ,
'사용자요구' : userRequirements [ material . id ] || '' ,
'수량' : ` ${ info . quantity } ${ info . unit } ` ,
'상세' : ` 단관 ${ info . itemCount || 0 } 개 → ${ info . totalLength || 0 } mm `
} ;
} else if ( selectedCategory === 'FLANGE' && info . isFlange ) {
return {
'종류' : info . type ,
'타입' : info . subtype ,
'크기' : info . size ,
'압력(파운드)' : info . pressure ,
'스케줄' : info . schedule ,
'재질' : info . grade ,
'추가요구' : '-' ,
'사용자요구' : userRequirements [ material . id ] || '' ,
'수량' : ` ${ info . quantity } ${ info . unit } `
} ;
} else if ( selectedCategory === 'FITTING' && info . isFitting ) {
return {
'종류' : info . type ,
'타입/상세' : info . subtype ,
'크기' : info . size ,
'압력' : info . pressure ,
'스케줄' : info . schedule ,
'재질' : info . grade ,
'추가요구' : '-' ,
'사용자요구' : userRequirements [ material . id ] || '' ,
'수량' : ` ${ info . quantity } ${ info . unit } `
} ;
} else if ( selectedCategory === 'VALVE' && info . isValve ) {
return {
'타입' : info . valveType ,
'연결방식' : info . connectionType ,
'크기' : info . size ,
'압력' : info . pressure ,
'재질' : info . grade ,
'추가요구' : '-' ,
'사용자요구' : userRequirements [ material . id ] || '' ,
'수량' : ` ${ info . quantity } ${ info . unit } `
} ;
} else if ( selectedCategory === 'GASKET' && info . isGasket ) {
return {
'종류' : info . type ,
'타입' : info . subtype ,
'크기' : info . size ,
'압력' : info . pressure ,
'재질' : info . materialStructure ,
'상세내역' : info . materialDetail ,
'두께' : info . thickness ,
'추가요구' : '-' ,
'사용자요구' : '' ,
'수량' : ` ${ info . quantity } ${ info . unit } `
} ;
itemName = info . subtype || 'PIPE' ;
} else if ( selectedCategory === 'FITTING' ) {
itemName = info . subtype || 'FITTING' ;
} else if ( selectedCategory === 'FLANGE' ) {
itemName = info . subtype || 'FLANGE' ;
} else if ( selectedCategory === 'VALVE' ) {
itemName = info . valveType || info . subtype || 'VALVE ';
} else if ( selectedCategory === 'GASKET' ) {
itemName = info . subtype || 'GASKET' ;
} else if ( selectedCategory === 'BOLT' ) {
return {
'종류' : info . type ,
'타입' : info . subtype ,
'크기' : info . size ,
'스케줄' : info . schedule ,
'재질' : info . grade ,
'추가요구' : '-' ,
'사용자요구' : userRequirements [ material . id ] || '' ,
'수량' : ` ${ info . quantity } ${ info . unit } `
} ;
} else if ( selectedCategory === 'UNKNOWN' && info . isUnknown ) {
return {
'종류' : info . type ,
'설명' : info . description ,
'사용자요구' : '' ,
'수량' : ` ${ info . quantity } ${ info . unit } `
} ;
itemName = info . subtype || 'BOLT' ;
} else {
// 기본 형식
return {
'종류' : info . type ,
'타입' : info . subtype ,
'크기' : info . size ,
'스케줄' : info . schedule ,
'재질' : info . grade ,
'추가요구' : '-' ,
'사용자요구' : userRequirements [ material . id ] || '' ,
'수량' : ` ${ info . quantity } ${ info . unit } `
} ;
itemName = info . subtype || info . type || 'UNKNOWN' ;
}
// 사용자 요구사항 확인
const userReq = userRequirements [ material . id ] || '' ;
console . log ( ` 📋 엑셀 내보내기 - 자재 ID ${ material . id } : 사용자요구 = " ${ userReq } " ` ) ;
// 통일된 엑셀 양식 반환
return {
'TAGNO' : '' , // 비워둠
'품목명' : itemName . trim ( ) ,
'수량' : info . quantity ,
'통화구분' : 'KRW' , // 기본값
'단가' : 1 , // 일괄 1로 설정
'크기' : info . size ,
'압력등급' : info . pressure || '-' ,
'스케줄' : info . schedule || '-' ,
'재질' : info . grade ,
'사용자요구' : userReq ,
'관리항목1' : '' , // 빈칸
'관리항목7' : '' , // 빈칸
'관리항목8' : '' , // 빈칸
'관리항목9' : '' , // 빈칸
'관리항목10' : '' , // 빈칸
'납기일(YYYY-MM-DD)' : new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] // 오늘 날짜
} ;
} ;
// 엑셀 데이터 생성
@@ -694,6 +855,25 @@ const NewMaterialsPage = ({
) }
< / div >
< div className = "header-right" >
< button
onClick = { ( ) => {
const currentUrl = window . location . href ;
const pageInfo = ` 페이지: NewMaterialsPage \ nURL: ${ currentUrl } \ nJob: ${ jobNo } \ nRevision: ${ currentRevision } ` ;
alert ( pageInfo ) ;
} }
style = { {
padding : '4px 8px' ,
background : '#667eea' ,
color : 'white' ,
border : 'none' ,
borderRadius : '4px' ,
fontSize : '11px' ,
cursor : 'pointer' ,
marginRight : '12px'
} }
>
🔗 URL
< / button >
< span className = "material-count" >
총 { materials . length } 개 자재 ( { currentRevision } )
< / span >
@@ -702,13 +882,22 @@ const NewMaterialsPage = ({
{ /* 카테고리 필터 */ }
< div className = "category-filters" >
{ /* 전체 카테고리 버튼 */ }
< button
key = "ALL"
className = { ` category-btn ${ selectedCategory === 'ALL' ? 'active' : '' } ` }
onClick = { ( ) => setSelectedCategory ( 'ALL' ) }
>
전체 < span className = "count" > { materials . length } < / span >
< / button >
{ Object . entries ( categoryCounts ) . map ( ( [ category , count ] ) => (
< button
key = { category }
className = { ` category-btn ${ selectedCategory === category ? 'active' : '' } ` }
onClick = { ( ) => setSelectedCategory ( category ) }
>
{ category } < span className = "count" > { count } < / span >
{ getCategoryDisplayName ( category) } < span className = "count" > { count } < / span >
< / button >
) ) }
< / div >
@@ -885,7 +1074,7 @@ const NewMaterialsPage = ({
{ /* 추가요구 */ }
< div className = "material-cell" >
< span > - < / span >
< span > { info . additionalReq || '-' } < / span >
< / div >
{ /* 사용자요구 */ }
@@ -895,7 +1084,10 @@ const NewMaterialsPage = ({
className = "user-req-input"
placeholder = "요구사항 입력"
value = { userRequirements [ material . id ] || '' }
onChange = { ( e ) => handleUserRequirementChange ( material . id , e . target . value ) }
onChange = { ( e ) => {
console . log ( '🎯 입력 이벤트 발생!' , material . id , e . target . value ) ;
handleUserRequirementChange ( material . id , e . target . value ) ;
} }
/ >
< / div >
@@ -954,7 +1146,7 @@ const NewMaterialsPage = ({
{ /* 추가요구 */ }
< div className = "material-cell" >
< span > - < / span >
< span > { info . additionalReq || '-' } < / span >
< / div >
{ /* 사용자요구 */ }
@@ -964,7 +1156,10 @@ const NewMaterialsPage = ({
className = "user-req-input"
placeholder = "요구사항 입력"
value = { userRequirements [ material . id ] || '' }
onChange = { ( e ) => handleUserRequirementChange ( material . id , e . target . value ) }
onChange = { ( e ) => {
console . log ( '🎯 입력 이벤트 발생!' , material . id , e . target . value ) ;
handleUserRequirementChange ( material . id , e . target . value ) ;
} }
/ >
< / div >
@@ -1030,7 +1225,7 @@ const NewMaterialsPage = ({
{ /* 추가요구 */ }
< div className = "material-cell" >
< span > - < / span >
< span > { info . additionalReq || '-' } < / span >
< / div >
{ /* 사용자요구 */ }
@@ -1040,7 +1235,10 @@ const NewMaterialsPage = ({
className = "user-req-input"
placeholder = "요구사항 입력"
value = { userRequirements [ material . id ] || '' }
onChange = { ( e ) => handleUserRequirementChange ( material . id , e . target . value ) }
onChange = { ( e ) => {
console . log ( '🎯 입력 이벤트 발생!' , material . id , e . target . value ) ;
handleUserRequirementChange ( material . id , e . target . value ) ;
} }
/ >
< / div >
@@ -1093,7 +1291,10 @@ const NewMaterialsPage = ({
className = "user-req-input"
placeholder = "요구사항 입력"
value = { userRequirements [ material . id ] || '' }
onChange = { ( e ) => handleUserRequirementChange ( material . id , e . target . value ) }
onChange = { ( e ) => {
console . log ( '🎯 입력 이벤트 발생!' , material . id , e . target . value ) ;
handleUserRequirementChange ( material . id , e . target . value ) ;
} }
/ >
< / div >
@@ -1164,7 +1365,7 @@ const NewMaterialsPage = ({
{ /* 추가요구 */ }
< div className = "material-cell" >
< span > - < / span >
< span > { info . additionalReq || '-' } < / span >
< / div >
{ /* 사용자요구 */ }
@@ -1174,7 +1375,10 @@ const NewMaterialsPage = ({
className = "user-req-input"
placeholder = "요구사항 입력"
value = { userRequirements [ material . id ] || '' }
onChange = { ( e ) => handleUserRequirementChange ( material . id , e . target . value ) }
onChange = { ( e ) => {
console . log ( '🎯 입력 이벤트 발생!' , material . id , e . target . value ) ;
handleUserRequirementChange ( material . id , e . target . value ) ;
} }
/ >
< / div >
@@ -1234,7 +1438,7 @@ const NewMaterialsPage = ({
{ /* 추가요구 */ }
< div className = "material-cell" >
< span > - < / span >
< span > { info . additionalReq || '-' } < / span >
< / div >
{ /* 사용자요구 */ }
@@ -1243,6 +1447,8 @@ const NewMaterialsPage = ({
type = "text"
className = "user-req-input"
placeholder = "요구사항 입력"
value = { userRequirements [ material . id ] || '' }
onChange = { ( e ) => handleUserRequirementChange ( material . id , e . target . value ) }
/ >
< / div >