summaryrefslogtreecommitdiff
path: root/components/common/material/material-group-selector-dialog-single.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'components/common/material/material-group-selector-dialog-single.tsx')
-rw-r--r--components/common/material/material-group-selector-dialog-single.tsx208
1 files changed, 208 insertions, 0 deletions
diff --git a/components/common/material/material-group-selector-dialog-single.tsx b/components/common/material/material-group-selector-dialog-single.tsx
new file mode 100644
index 00000000..bb039d0a
--- /dev/null
+++ b/components/common/material/material-group-selector-dialog-single.tsx
@@ -0,0 +1,208 @@
+"use client";
+
+import React, { useState, useCallback } from "react";
+import { Button } from "@/components/ui/button";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog";
+import { MaterialGroupSelector } from "./material-group-selector";
+import { MaterialSearchItem } from "@/lib/material/material-group-service";
+
+/**
+ * 자재그룹 단일 선택 Dialog 컴포넌트
+ *
+ * @description
+ * - MaterialGroupSelector를 Dialog로 래핑한 단일 선택 컴포넌트
+ * - 버튼 클릭 시 Dialog가 열리고, 자재를 선택하면 Dialog가 닫히며 결과를 반환
+ *
+ * @MaterialSearchItem_Structure
+ * 상태에서 관리되는 자재그룹 객체의 형태:
+ * ```typescript
+ * interface MaterialSearchItem {
+ * materialGroupCode: string; // 자재그룹코드 (예: "BG2001")
+ * materialGroupDescription: string; // 자재그룹명 (예: "DOUBLE WETDOOR HINGE")
+ * materialGroupUom?: string; // 단위 (예: "EA", "KG")
+ * displayText: string; // 표시용 텍스트 (code + " - " + description)
+ * }
+ * ```
+ *
+ * @state
+ * - open: Dialog 열림/닫힘 상태
+ * - selectedMaterial: 현재 선택된 자재 (단일)
+ * - tempSelectedMaterial: Dialog 내에서 임시로 선택된 자재 (확인 버튼 클릭 전까지)
+ *
+ * @callback
+ * - onMaterialSelect: 자재 선택 완료 시 호출되는 콜백
+ * - 매개변수: MaterialSearchItem | null
+ * - 선택된 자재 정보 또는 null (선택 해제 시)
+ *
+ * @usage
+ * ```tsx
+ * <MaterialGroupSelectorDialogSingle
+ * triggerLabel="자재 선택"
+ * selectedMaterial={selectedMaterial}
+ * onMaterialSelect={(material) => {
+ * setSelectedMaterial(material);
+ * console.log('선택된 자재:', material);
+ * }}
+ * placeholder="자재를 검색하세요..."
+ * />
+ * ```
+ */
+
+interface MaterialGroupSelectorDialogSingleProps {
+ /** Dialog를 여는 트리거 버튼 텍스트 */
+ triggerLabel?: string;
+ /** 현재 선택된 자재 */
+ selectedMaterial?: MaterialSearchItem | null;
+ /** 자재 선택 완료 시 호출되는 콜백 */
+ onMaterialSelect?: (material: MaterialSearchItem | null) => void;
+ /** 검색 입력창 placeholder */
+ placeholder?: string;
+ /** Dialog 제목 */
+ title?: string;
+ /** Dialog 설명 */
+ description?: string;
+ /** 트리거 버튼 비활성화 여부 */
+ disabled?: boolean;
+ /** 트리거 버튼 variant */
+ triggerVariant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link";
+ /** 제외할 자재그룹코드들 */
+ excludeMaterialCodes?: Set<string>;
+ /** 초기 데이터 표시 여부 */
+ showInitialData?: boolean;
+}
+
+export function MaterialGroupSelectorDialogSingle({
+ triggerLabel = "자재 선택",
+ selectedMaterial = null,
+ onMaterialSelect,
+ placeholder = "자재를 검색하세요...",
+ title = "자재 선택",
+ description = "원하는 자재를 검색하고 선택해주세요.",
+ disabled = false,
+ triggerVariant = "outline",
+ excludeMaterialCodes,
+ showInitialData = true,
+}: MaterialGroupSelectorDialogSingleProps) {
+ // Dialog 열림/닫힘 상태
+ const [open, setOpen] = useState(false);
+
+ // Dialog 내에서 임시로 선택된 자재 (확인 버튼 클릭 전까지)
+ const [tempSelectedMaterial, setTempSelectedMaterial] = useState<MaterialSearchItem | null>(null);
+
+ // Dialog 열림 시 현재 선택된 자재로 임시 선택 초기화
+ const handleOpenChange = useCallback((newOpen: boolean) => {
+ setOpen(newOpen);
+ if (newOpen) {
+ setTempSelectedMaterial(selectedMaterial || null);
+ }
+ }, [selectedMaterial]);
+
+ // 자재 선택 처리 (Dialog 내에서)
+ const handleMaterialChange = useCallback((materials: MaterialSearchItem[]) => {
+ setTempSelectedMaterial(materials.length > 0 ? materials[0] : null);
+ }, []);
+
+ // 확인 버튼 클릭 시 선택 완료
+ const handleConfirm = useCallback(() => {
+ onMaterialSelect?.(tempSelectedMaterial);
+ setOpen(false);
+ }, [tempSelectedMaterial, onMaterialSelect]);
+
+ // 취소 버튼 클릭 시
+ const handleCancel = useCallback(() => {
+ setTempSelectedMaterial(selectedMaterial || null);
+ setOpen(false);
+ }, [selectedMaterial]);
+
+ // 선택 해제
+ const handleClear = useCallback(() => {
+ setTempSelectedMaterial(null);
+ }, []);
+
+ return (
+ <Dialog open={open} onOpenChange={handleOpenChange}>
+ <DialogTrigger asChild>
+ <Button variant={triggerVariant} disabled={disabled}>
+ {selectedMaterial ? (
+ <span className="truncate">
+ {selectedMaterial.displayText}
+ </span>
+ ) : (
+ triggerLabel
+ )}
+ </Button>
+ </DialogTrigger>
+
+ <DialogContent className="max-w-md">
+ <DialogHeader>
+ <DialogTitle>{title}</DialogTitle>
+ <DialogDescription>{description}</DialogDescription>
+ </DialogHeader>
+
+ <div className="py-4">
+ <MaterialGroupSelector
+ selectedMaterials={tempSelectedMaterial ? [tempSelectedMaterial] : []}
+ onMaterialsChange={handleMaterialChange}
+ singleSelect={true}
+ placeholder={placeholder}
+ noValuePlaceHolder="자재를 선택해주세요"
+ closeOnSelect={false}
+ excludeMaterialCodes={excludeMaterialCodes}
+ showInitialData={showInitialData}
+ />
+ </div>
+
+ <DialogFooter className="gap-2">
+ <Button variant="outline" onClick={handleCancel}>
+ 취소
+ </Button>
+ {tempSelectedMaterial && (
+ <Button variant="ghost" onClick={handleClear}>
+ 선택 해제
+ </Button>
+ )}
+ <Button onClick={handleConfirm}>
+ 확인
+ </Button>
+ </DialogFooter>
+ </DialogContent>
+ </Dialog>
+ );
+}
+
+/**
+ * 사용 예시:
+ *
+ * ```tsx
+ * const [selectedMaterial, setSelectedMaterial] = useState<MaterialSearchItem | null>(null);
+ *
+ * return (
+ * <MaterialGroupSelectorDialogSingle
+ * triggerLabel="자재 선택"
+ * selectedMaterial={selectedMaterial}
+ * onMaterialSelect={(material) => {
+ * setSelectedMaterial(material);
+ * if (material) {
+ * console.log('선택된 자재:', {
+ * code: material.materialGroupCode,
+ * description: material.materialGroupDescription,
+ * uom: material.materialGroupUom
+ * });
+ * } else {
+ * console.log('자재 선택이 해제되었습니다.');
+ * }
+ * }}
+ * title="자재 선택"
+ * description="필요한 자재를 검색하고 선택해주세요."
+ * />
+ * );
+ * ```
+ */ \ No newline at end of file