summaryrefslogtreecommitdiff
path: root/lib/rfq-last/table/rfq-assign-pic-dialog.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rfq-last/table/rfq-assign-pic-dialog.tsx')
-rw-r--r--lib/rfq-last/table/rfq-assign-pic-dialog.tsx279
1 files changed, 80 insertions, 199 deletions
diff --git a/lib/rfq-last/table/rfq-assign-pic-dialog.tsx b/lib/rfq-last/table/rfq-assign-pic-dialog.tsx
index 94dde779..9ca34ccd 100644
--- a/lib/rfq-last/table/rfq-assign-pic-dialog.tsx
+++ b/lib/rfq-last/table/rfq-assign-pic-dialog.tsx
@@ -10,36 +10,15 @@ import {
DialogTitle,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
-import {
- Command,
- CommandEmpty,
- CommandGroup,
- CommandInput,
- CommandItem,
- CommandList,
-} from "@/components/ui/command";
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from "@/components/ui/popover";
-import { Check, ChevronsUpDown, Loader2, User, Users } from "lucide-react";
-import { cn } from "@/lib/utils";
+import { Loader2, Users } from "lucide-react";
import { toast } from "sonner";
-import { getPUsersForFilter } from "@/lib/rfq-last/service";
import { assignPicToRfqs } from "../service";
import { Badge } from "@/components/ui/badge";
import { Alert, AlertDescription } from "@/components/ui/alert";
-
-interface User {
- id: number;
- name: string | null;
- userCode: string | null;
- deptName: string | null;
- isAbsent: boolean | null;
- isDeletedOnNonSap: boolean | null;
- email?: string;
-}
+import {
+ PurchaseGroupCodeSingleSelector,
+ PurchaseGroupCodeWithUser
+} from "@/components/common/selectors/purchase-group-code";
interface RfqAssignPicDialogProps {
open: boolean;
@@ -56,12 +35,9 @@ export function RfqAssignPicDialog({
selectedRfqCodes,
onSuccess,
}: RfqAssignPicDialogProps) {
- const [users, setUsers] = React.useState<User[]>([] as User[]);
- const [isLoadingUsers, setIsLoadingUsers] = React.useState(false);
const [isAssigning, setIsAssigning] = React.useState(false);
- const [selectedUser, setSelectedUser] = React.useState<User | null>(null);
- const [userPopoverOpen, setUserPopoverOpen] = React.useState(false);
- const [userSearchTerm, setUserSearchTerm] = React.useState("");
+ const [selectedCode, setSelectedCode] = React.useState<PurchaseGroupCodeWithUser | undefined>(undefined);
+ const [selectorOpen, setSelectorOpen] = React.useState(false);
// ITB만 필터링 (rfqCode가 "I"로 시작하는 것)
const itbCodes = React.useMemo(() => {
@@ -72,50 +48,36 @@ export function RfqAssignPicDialog({
return selectedRfqIds.filter((id, index) => selectedRfqCodes[index]?.startsWith("I"));
}, [selectedRfqIds, selectedRfqCodes]);
- // 유저 목록 로드
+ // 다이얼로그 열릴 때 초기화
React.useEffect(() => {
- const loadUsers = async () => {
- setIsLoadingUsers(true);
- try {
- const userList = await getPUsersForFilter();
- setUsers(userList as User[]);
- } catch (error) {
- console.log("사용자 목록 로드 오류:", error);
- toast.error("사용자 목록을 불러오는데 실패했습니다");
- } finally {
- setIsLoadingUsers(false);
- }
- };
-
if (open) {
- loadUsers();
- // 다이얼로그 열릴 때 초기화
- setSelectedUser(null);
- setUserSearchTerm("");
+ setSelectedCode(undefined);
}
}, [open]);
- // 유저 검색
- const filteredUsers = React.useMemo(() => {
- if (!userSearchTerm || !userSearchTerm.trim()) return users;
-
- const searchTerm = userSearchTerm.trim();
- return users.filter(
- (user) =>
- (user.name && user.name.includes(searchTerm)) ||
- (user.userCode && user.userCode.toLowerCase().includes(searchTerm.toLowerCase())) ||
- (user.deptName && user.deptName.includes(searchTerm))
- );
- }, [users, userSearchTerm]);
-
- const handleSelectUser = (user: User) => {
- setSelectedUser(user);
- setUserPopoverOpen(false);
+ const handleCodeSelect = (code: PurchaseGroupCodeWithUser) => {
+ setSelectedCode(code);
+
+ // 유저 정보가 없는 경우 toast로 알림
+ if (!code.user) {
+ toast.warning(
+ `해당 구매그룹코드(${code.PURCHASE_GROUP_CODE})의 사번 정보의 유저가 없습니다`,
+ {
+ description: `사번: ${code.EMPLOYEE_NUMBER}`,
+ duration: 5000,
+ }
+ );
+ }
};
const handleAssign = async () => {
- if (!selectedUser) {
- toast.error("담당자를 선택해주세요");
+ if (!selectedCode) {
+ toast.error("구매그룹코드를 선택해주세요");
+ return;
+ }
+
+ if (!selectedCode.user) {
+ toast.error("선택한 구매그룹코드에 연결된 사용자가 없습니다");
return;
}
@@ -128,7 +90,7 @@ export function RfqAssignPicDialog({
try {
const result = await assignPicToRfqs({
rfqIds: itbIds,
- picUserId: selectedUser.id,
+ picUserId: selectedCode.user.id,
});
if (result.success) {
@@ -196,144 +158,63 @@ export function RfqAssignPicDialog({
)}
</div>
- {/* 담당자 선택 */}
+ {/* 구매 담당자 선택 (구매그룹코드) */}
<div className="space-y-2">
- <label className="text-sm font-medium">구매 담당자</label>
- <Popover open={userPopoverOpen} onOpenChange={setUserPopoverOpen}>
- <PopoverTrigger asChild>
- <Button
- type="button"
- variant="outline"
- className="w-full justify-between h-10"
- disabled={isLoadingUsers || itbCodes.length === 0}
- >
- {isLoadingUsers ? (
- <>
- <span>담당자 로딩 중...</span>
- <Loader2 className="ml-2 h-4 w-4 animate-spin" />
- </>
- ) : (
- <>
- <span className="flex items-center gap-2">
- <User className="h-4 w-4" />
- {selectedUser ? (
- <>
- {selectedUser.name}
- {selectedUser.userCode && (
- <span className="text-muted-foreground">
- ({selectedUser.userCode})
- </span>
- )}
- </>
- ) : (
- <span className="text-muted-foreground">
- 구매 담당자를 선택하세요
- </span>
- )}
- </span>
- <ChevronsUpDown className="h-4 w-4 opacity-50" />
- </>
+ <label className="text-sm font-medium">구매 담당자 (구매그룹코드)</label>
+ <Button
+ type="button"
+ variant="outline"
+ className="w-full justify-start h-auto min-h-[40px]"
+ disabled={itbCodes.length === 0}
+ onClick={() => setSelectorOpen(true)}
+ >
+ {selectedCode ? (
+ <div className="flex flex-col items-start gap-1 w-full">
+ <div className="flex items-center gap-2">
+ <Badge variant="secondary" className="font-mono">
+ {selectedCode.PURCHASE_GROUP_CODE}
+ </Badge>
+ <span>{selectedCode.DISPLAY_NAME}</span>
+ </div>
+ {selectedCode.user && (
+ <div className="text-xs text-muted-foreground">
+ 사용자: {selectedCode.user.name} ({selectedCode.user.email})
+ </div>
)}
- </Button>
- </PopoverTrigger>
- <PopoverContent className="w-[460px] p-0">
- <Command shouldFilter={false}>
- <CommandInput
- placeholder="이름, 구매그룹코드 또는 부서로 검색..."
- value={userSearchTerm}
- onValueChange={setUserSearchTerm}
- />
- <CommandList className="max-h-[300px]">
- <CommandEmpty>검색 결과가 없습니다</CommandEmpty>
- <CommandGroup
- className="overflow-y-auto"
- onWheel={(e) => {
- // 마우스 휠 스크롤이 제대로 작동하도록 이벤트 전파 허용
- e.stopPropagation();
- }}
- >
- {filteredUsers.map((user) => (
- <CommandItem
- key={user.id}
- value={`${user.name || ''} ${user.userCode || ''} ${user.deptName || ''}`}
- onSelect={() => handleSelectUser(user)}
- className="flex items-center justify-between"
- >
- <div className="flex flex-col gap-1">
- <div className="flex items-center gap-2">
- <User className="h-4 w-4" />
- <span>{user.name || '이름 없음'}</span>
- {user.userCode && (
- <span className="text-muted-foreground text-sm">
- ({user.userCode})
- </span>
- )}
- {(user.isAbsent || user.isDeletedOnNonSap) && (
- <div className="flex gap-1">
- {user.isAbsent && (
- <Badge variant="outline" className="text-xs px-1 py-0">
- 휴직
- </Badge>
- )}
- {user.isDeletedOnNonSap && (
- <Badge variant="outline" className="text-xs px-1 py-0">
- 퇴직
- </Badge>
- )}
- </div>
- )}
- </div>
- {user.deptName && (
- <span className="text-xs text-muted-foreground">
- {user.deptName}
- </span>
- )}
- </div>
- <Check
- className={cn(
- "h-4 w-4",
- selectedUser?.id === user.id
- ? "opacity-100"
- : "opacity-0"
- )}
- />
- </CommandItem>
- ))}
- </CommandGroup>
- </CommandList>
- </Command>
- </PopoverContent>
- </Popover>
- {selectedUser && (
- <div className="text-xs text-muted-foreground">
- <div className="flex items-center gap-2">
- <p>
- 선택한 담당자: {selectedUser.name || '이름 없음'}
- {selectedUser.userCode && ` (${selectedUser.userCode})`}
- </p>
- {(selectedUser.isAbsent || selectedUser.isDeletedOnNonSap) && (
- <div className="flex gap-1">
- {selectedUser.isAbsent && (
- <Badge variant="outline" className="text-xs px-1 py-0 border-orange-300 text-orange-700 bg-orange-50">
- 휴직
- </Badge>
- )}
- {selectedUser.isDeletedOnNonSap && (
- <Badge variant="outline" className="text-xs px-1 py-0 border-red-300 text-red-700 bg-red-50">
- 퇴직
- </Badge>
- )}
+ {!selectedCode.user && (
+ <div className="text-xs text-orange-600">
+ ⚠️ 연결된 사용자가 없습니다
</div>
)}
</div>
- {selectedUser.deptName && (
- <p>{selectedUser.deptName}</p>
- )}
- </div>
+ ) : (
+ <span className="text-muted-foreground">
+ 구매그룹코드를 선택하세요
+ </span>
+ )}
+ </Button>
+
+ {selectedCode && !selectedCode.user && (
+ <Alert className="border-orange-200 bg-orange-50">
+ <AlertDescription className="text-orange-800 text-xs">
+ 선택한 구매그룹코드에 연결된 사용자가 없습니다. 다른 구매그룹코드를 선택해주세요.
+ </AlertDescription>
+ </Alert>
)}
</div>
</div>
+ {/* 구매그룹코드 선택 다이얼로그 */}
+ <PurchaseGroupCodeSingleSelector
+ open={selectorOpen}
+ onOpenChange={setSelectorOpen}
+ selectedCode={selectedCode}
+ onCodeSelect={handleCodeSelect}
+ title="구매 담당자 선택"
+ description="ITB에 지정할 구매 담당자의 구매그룹코드를 선택하세요"
+ showConfirmButtons={false}
+ />
+
<DialogFooter>
<Button
type="button"
@@ -346,7 +227,7 @@ export function RfqAssignPicDialog({
<Button
type="submit"
onClick={handleAssign}
- disabled={!selectedUser || itbCodes.length === 0 || isAssigning}
+ disabled={!selectedCode || !selectedCode.user || itbCodes.length === 0 || isAssigning}
>
{isAssigning ? (
<>
@@ -361,4 +242,4 @@ export function RfqAssignPicDialog({
</DialogContent>
</Dialog>
);
-} \ No newline at end of file
+}