summaryrefslogtreecommitdiff
path: root/lib/compliance/table
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compliance/table')
-rw-r--r--lib/compliance/table/compliance-survey-templates-toolbar.tsx4
-rw-r--r--lib/compliance/table/red-flag-managers-dialog.tsx203
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>
+ );
+}
+