diff options
Diffstat (limited to 'lib/compliance/table')
| -rw-r--r-- | lib/compliance/table/compliance-survey-templates-toolbar.tsx | 4 | ||||
| -rw-r--r-- | lib/compliance/table/red-flag-managers-dialog.tsx | 203 |
2 files changed, 207 insertions, 0 deletions
diff --git a/lib/compliance/table/compliance-survey-templates-toolbar.tsx b/lib/compliance/table/compliance-survey-templates-toolbar.tsx index e093550c..6776b70a 100644 --- a/lib/compliance/table/compliance-survey-templates-toolbar.tsx +++ b/lib/compliance/table/compliance-survey-templates-toolbar.tsx @@ -8,6 +8,7 @@ import { exportTableToExcel } from "@/lib/export"; import { Button } from "@/components/ui/button"; import { ComplianceTemplateCreateDialog } from "./compliance-template-create-dialog"; import { DeleteComplianceTemplatesDialog } from "./delete-compliance-templates-dialog"; +import { RedFlagManagersDialog } from "./red-flag-managers-dialog"; import { complianceSurveyTemplates } from "@/db/schema/compliance"; interface ComplianceSurveyTemplatesToolbarActionsProps { @@ -28,6 +29,9 @@ export function ComplianceSurveyTemplatesToolbarActions({ table }: ComplianceSur <ComplianceTemplateCreateDialog /> + {/** 2) 레드플래그 담당자 관리 */} + <RedFlagManagersDialog /> + {/** 3) Export 버튼 */} <Button variant="outline" diff --git a/lib/compliance/table/red-flag-managers-dialog.tsx b/lib/compliance/table/red-flag-managers-dialog.tsx new file mode 100644 index 00000000..08244f9e --- /dev/null +++ b/lib/compliance/table/red-flag-managers-dialog.tsx @@ -0,0 +1,203 @@ +"use client"; + +import * as React from "react"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { Label } from "@/components/ui/label"; +import { AlertCircle, Users } from "lucide-react"; +import { toast } from "sonner"; +import { useRouter } from "next/navigation"; +import { UserSelector, UserSelectItem } from "@/components/common/user/user-selector"; +import { + getOrCreateRedFlagManagers, + updateRedFlagManagers +} from "@/lib/compliance/services"; + +export function RedFlagManagersDialog() { + const [open, setOpen] = React.useState(false); + const [isLoading, setIsLoading] = React.useState(false); + const [isFetching, setIsFetching] = React.useState(false); + const router = useRouter(); + + // 담당자 state + const [managerId, setManagerId] = React.useState<number | null>(null); + const [purchasingManager, setPurchasingManager] = React.useState<UserSelectItem[]>([]); + const [complianceManager, setComplianceManager] = React.useState<UserSelectItem[]>([]); + + // 다이얼로그 열릴 때 현재 담당자 정보 가져오기 + React.useEffect(() => { + if (open) { + loadManagers(); + } + }, [open]); + + const loadManagers = async () => { + setIsFetching(true); + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const managers: any = await getOrCreateRedFlagManagers(); + + if (managers) { + setManagerId(managers.id); + + // 구매기획 담당자 설정 + if (managers.purchasingManager) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const pm: any = managers.purchasingManager; + setPurchasingManager([{ + id: pm.id, + name: pm.name, + email: pm.email, + epId: pm.epId, + deptCode: pm.deptCode, + deptName: pm.deptName, + imageUrl: pm.imageUrl, + domain: pm.domain, + }]); + } + + // 준법 담당자 설정 + if (managers.complianceManager) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const cm: any = managers.complianceManager; + setComplianceManager([{ + id: cm.id, + name: cm.name, + email: cm.email, + epId: cm.epId, + deptCode: cm.deptCode, + deptName: cm.deptName, + imageUrl: cm.imageUrl, + domain: cm.domain, + }]); + } + } + } catch (error) { + console.error("Error loading red flag managers:", error); + toast.error("담당자 정보를 불러오는 중 오류가 발생했습니다."); + } finally { + setIsFetching(false); + } + }; + + const handleSave = async () => { + if (!managerId) { + toast.error("담당자 정보를 불러오지 못했습니다."); + return; + } + + setIsLoading(true); + try { + await updateRedFlagManagers(managerId, { + purchasingManagerId: purchasingManager[0]?.id || null, + complianceManagerId: complianceManager[0]?.id || null, + }); + + toast.success("레드플래그 담당자가 저장되었습니다."); + setOpen(false); + router.refresh(); + } catch (error) { + console.error("Error saving red flag managers:", error); + toast.error("담당자 저장 중 오류가 발생했습니다."); + } finally { + setIsLoading(false); + } + }; + + return ( + <Dialog open={open} onOpenChange={setOpen}> + <DialogTrigger asChild> + <Button variant="outline" size="sm"> + <AlertCircle className="mr-2 h-4 w-4 text-red-600" /> + 레드플래그 담당자 + </Button> + </DialogTrigger> + <DialogContent className="sm:max-w-[600px]"> + <DialogHeader> + <DialogTitle className="flex items-center gap-2"> + <AlertCircle className="h-5 w-5 text-red-600" /> + 레드플래그 담당자 관리 + </DialogTitle> + <DialogDescription> + 레드플래그 발생 시 알림을 받을 담당자를 지정합니다. + </DialogDescription> + </DialogHeader> + + {isFetching ? ( + <div className="space-y-4 py-4"> + <div className="text-center text-sm text-muted-foreground"> + 담당자 정보를 불러오는 중... + </div> + </div> + ) : ( + <div className="space-y-6 py-4"> + {/* 구매기획 담당자 */} + <div className="space-y-2"> + <Label htmlFor="purchasing-manager" className="flex items-center gap-2"> + <Users className="h-4 w-4" /> + 구매기획 담당자 + </Label> + <UserSelector + selectedUsers={purchasingManager} + onUsersChange={setPurchasingManager} + singleSelect={true} + placeholder="구매기획 담당자를 검색하세요..." + domainFilter={{ type: "exclude", domains: ["partners"] }} + closeOnSelect={true} + /> + <p className="text-xs text-muted-foreground"> + 레드플래그 발생 시 알림을 받을 구매기획 담당자를 지정합니다. + </p> + </div> + + {/* 준법 담당자 */} + <div className="space-y-2"> + <Label htmlFor="compliance-manager" className="flex items-center gap-2"> + <Users className="h-4 w-4" /> + 준법 담당자 + </Label> + <UserSelector + selectedUsers={complianceManager} + onUsersChange={setComplianceManager} + singleSelect={true} + placeholder="준법 담당자를 검색하세요..." + domainFilter={{ type: "exclude", domains: ["partners"] }} + closeOnSelect={true} + /> + <p className="text-xs text-muted-foreground"> + 레드플래그 발생 시 알림을 받을 준법 담당자를 지정합니다. + </p> + </div> + </div> + )} + + <DialogFooter> + <Button + type="button" + variant="outline" + onClick={() => setOpen(false)} + disabled={isLoading || isFetching} + > + 취소 + </Button> + <Button + type="button" + onClick={handleSave} + disabled={isLoading || isFetching} + > + {isLoading ? "저장 중..." : "저장"} + </Button> + </DialogFooter> + </DialogContent> + </Dialog> + ); +} + |
