From 2b59582194fc5c23140f52c42c793c324856a35e Mon Sep 17 00:00:00 2001 From: joonhoekim <26rote@gmail.com> Date: Tue, 25 Nov 2025 22:04:56 +0900 Subject: (김준회) 벤더풀&AVL 구매 추가요청사항 반영 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/discipline-hardcoded/discipline-data.ts | 14 ++++++ .../discipline-hardcoded-selector.tsx | 48 ++++++++++++++++++ components/common/discipline-hardcoded/index.ts | 3 ++ .../material-group-selector-dialog-single.tsx | 4 +- .../place-of-shipping-selector.tsx | 31 ++++++------ .../vendor/vendor-selector-dialog-single.tsx | 4 +- components/data-table/editable-cell.tsx | 58 ++++++++++++++-------- 7 files changed, 123 insertions(+), 39 deletions(-) create mode 100644 components/common/discipline-hardcoded/discipline-data.ts create mode 100644 components/common/discipline-hardcoded/discipline-hardcoded-selector.tsx create mode 100644 components/common/discipline-hardcoded/index.ts (limited to 'components') diff --git a/components/common/discipline-hardcoded/discipline-data.ts b/components/common/discipline-hardcoded/discipline-data.ts new file mode 100644 index 00000000..4910e272 --- /dev/null +++ b/components/common/discipline-hardcoded/discipline-data.ts @@ -0,0 +1,14 @@ +export const HARDCODED_DISCIPLINES = [ + 'ARCHITECTURE', + 'CCS', + 'ELECTRICAL', + 'INSTRUMENT', + 'INSULATION', + 'MACHINERY', + 'MECHANICAL', + 'PIPING', + 'STRUCTURE', + 'SURFACE PROTECTION', +] as const + +export type HardcodedDiscipline = typeof HARDCODED_DISCIPLINES[number] diff --git a/components/common/discipline-hardcoded/discipline-hardcoded-selector.tsx b/components/common/discipline-hardcoded/discipline-hardcoded-selector.tsx new file mode 100644 index 00000000..6de0a285 --- /dev/null +++ b/components/common/discipline-hardcoded/discipline-hardcoded-selector.tsx @@ -0,0 +1,48 @@ +'use client' + +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select' +import { HARDCODED_DISCIPLINES, HardcodedDiscipline } from './discipline-data' + +export interface DisciplineHardcodedSelectorProps { + selectedDiscipline?: string + onDisciplineSelect: (discipline: string) => void + disabled?: boolean + placeholder?: string + className?: string +} + +export function DisciplineHardcodedSelector({ + selectedDiscipline, + onDisciplineSelect, + disabled, + placeholder = "설계공종 선택", + className +}: DisciplineHardcodedSelectorProps) { + + return ( + + + + {selectedDiscipline || {placeholder}} + + + + {HARDCODED_DISCIPLINES.map((discipline) => ( + + {discipline} + + ))} + + + ) +} diff --git a/components/common/discipline-hardcoded/index.ts b/components/common/discipline-hardcoded/index.ts new file mode 100644 index 00000000..bd55175f --- /dev/null +++ b/components/common/discipline-hardcoded/index.ts @@ -0,0 +1,3 @@ +export * from './discipline-data' +export * from './discipline-hardcoded-selector' + diff --git a/components/common/material/material-group-selector-dialog-single.tsx b/components/common/material/material-group-selector-dialog-single.tsx index bb039d0a..1aeaec33 100644 --- a/components/common/material/material-group-selector-dialog-single.tsx +++ b/components/common/material/material-group-selector-dialog-single.tsx @@ -130,9 +130,9 @@ export function MaterialGroupSelectorDialogSingle({ return ( - + {selectedMaterial ? ( - + {selectedMaterial.displayText} ) : ( diff --git a/components/common/selectors/place-of-shipping/place-of-shipping-selector.tsx b/components/common/selectors/place-of-shipping/place-of-shipping-selector.tsx index 2e9756a0..1d1aaa5e 100644 --- a/components/common/selectors/place-of-shipping/place-of-shipping-selector.tsx +++ b/components/common/selectors/place-of-shipping/place-of-shipping-selector.tsx @@ -236,27 +236,30 @@ export function PlaceOfShippingSelectorDialogSingle({ }, []) useEffect(() => { - const loadData = async () => { - try { - const data = await getPlaceOfShippingForSelection() - setPlaceOfShippingData(data) - } catch (error) { - console.error('선적지/하역지 데이터 로드 실패:', error) - setPlaceOfShippingData([]) - } finally { - setIsLoading(false) + if (open && placeOfShippingData.length === 0) { + const loadData = async () => { + setIsLoading(true) + try { + const data = await getPlaceOfShippingForSelection() + setPlaceOfShippingData(data) + } catch (error) { + console.error('선적지/하역지 데이터 로드 실패:', error) + setPlaceOfShippingData([]) + } finally { + setIsLoading(false) + } } - } - loadData() - }, []) + loadData() + } + }, [open, placeOfShippingData.length]) return ( - + {selectedPlace ? ( - + {selectedPlace.code} - {selectedPlace.description} ) : ( diff --git a/components/common/vendor/vendor-selector-dialog-single.tsx b/components/common/vendor/vendor-selector-dialog-single.tsx index 7bb4b14c..ba4243cf 100644 --- a/components/common/vendor/vendor-selector-dialog-single.tsx +++ b/components/common/vendor/vendor-selector-dialog-single.tsx @@ -135,9 +135,9 @@ export function VendorSelectorDialogSingle({ return ( - + {selectedVendor ? ( - + {selectedVendor.displayText} ) : ( diff --git a/components/data-table/editable-cell.tsx b/components/data-table/editable-cell.tsx index 05f5c4cb..aa43606e 100644 --- a/components/data-table/editable-cell.tsx +++ b/components/data-table/editable-cell.tsx @@ -59,13 +59,6 @@ export function EditableCell({ const [isEditing, setIsEditing] = React.useState(initialEditMode) const [editValue, setEditValue] = React.useState(value) const [error, setError] = React.useState(null) - const handleStartEdit = useCallback((e: React.MouseEvent) => { - e.stopPropagation() - if (disabled) return - setIsEditing(true) - setEditValue(value) - setError(null) - }, [disabled, value]) const handleFinishEdit = useCallback((overrideValue?: T) => { const currentValue = overrideValue !== undefined ? overrideValue : editValue @@ -88,6 +81,35 @@ export function EditableCell({ setError(null) }, [editValue, validation, value, onSave]) + const handleCheckboxChange = useCallback((checked: boolean) => { + const convertedValue = checked as T + setEditValue(convertedValue) + if (autoSave) { + // 체크박스 변경 시 자동 저장 - 값 직접 전달 + handleFinishEdit(convertedValue) + } else { + // 일괄 저장 모드에서는 실시간 표시를 위해 즉시 onSave 호출 + onSave(convertedValue) + } + }, [autoSave, handleFinishEdit, onSave]) + + const handleStartEdit = useCallback((e: React.MouseEvent) => { + e.stopPropagation() + if (disabled) return + + // 체크박스의 경우 즉시 토글 + if (type === "checkbox") { + // T가 boolean이라고 가정 + const newValue = !value + handleCheckboxChange(!!newValue) + return + } + + setIsEditing(true) + setEditValue(value) + setError(null) + }, [disabled, value, type, handleCheckboxChange]) + const handleCancelEdit = useCallback(() => { // 취소 시 원래 값으로 복원하되, pendingChanges에서도 제거 onCancel?.() @@ -165,18 +187,6 @@ export function EditableCell({ } }, [type, value, autoSave, onSave, onChange]) - const handleCheckboxChange = useCallback((checked: boolean) => { - const convertedValue = checked as T - setEditValue(convertedValue) - if (autoSave) { - // 체크박스 변경 시 자동 저장 - 값 직접 전달 - handleFinishEdit(convertedValue) - } else { - // 일괄 저장 모드에서는 실시간 표시를 위해 즉시 onSave 호출 - onSave(convertedValue) - } - }, [autoSave, handleFinishEdit, onSave]) - // 읽기 전용 모드 if (!isEditing) { return ( @@ -192,7 +202,13 @@ export function EditableCell({ > {type === "checkbox" ? ( - + e.stopPropagation()}> + + ) : ( ({ )} - {!disabled && ( + {!disabled && type !== "checkbox" && ( )} -- cgit v1.2.3