summaryrefslogtreecommitdiff
path: root/lib/vendor-regular-registrations/table
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vendor-regular-registrations/table')
-rw-r--r--lib/vendor-regular-registrations/table/vendor-regular-registrations-table-toolbar-actions.tsx663
1 files changed, 329 insertions, 334 deletions
diff --git a/lib/vendor-regular-registrations/table/vendor-regular-registrations-table-toolbar-actions.tsx b/lib/vendor-regular-registrations/table/vendor-regular-registrations-table-toolbar-actions.tsx
index f40a41f7..f879f065 100644
--- a/lib/vendor-regular-registrations/table/vendor-regular-registrations-table-toolbar-actions.tsx
+++ b/lib/vendor-regular-registrations/table/vendor-regular-registrations-table-toolbar-actions.tsx
@@ -1,334 +1,329 @@
-"use client"
-
-import { type Table } from "@tanstack/react-table"
-import { toast } from "sonner"
-
-import { Button } from "@/components/ui/button"
-import { Mail, FileWarning, Scale, FileText } from "lucide-react"
-import type { VendorRegularRegistration } from "@/config/vendorRegularRegistrationsColumnsConfig"
-import {
- sendMissingContractRequestEmails,
- sendAdditionalInfoRequestEmails,
- skipLegalReview
-} from "../service"
-import { useState } from "react"
-import { SkipReasonDialog } from "@/components/vendor-regular-registrations/skip-reason-dialog"
-import { RegistrationRequestDialog } from "@/components/vendor-regular-registrations/registration-request-dialog"
-import { ApprovalPreviewDialog } from "@/components/approval/ApprovalPreviewDialog"
-import { useRouter } from "next/navigation"
-import { useSession } from "next-auth/react"
-import { registerVendorWithApproval } from "../approval-actions"
-import { mapRegistrationToTemplateVariables } from "../handlers"
-import type { RegistrationRequestData } from "@/components/vendor-regular-registrations/registration-request-dialog"
-
-interface VendorRegularRegistrationsTableToolbarActionsProps {
- table: Table<VendorRegularRegistration>
-}
-
-export function VendorRegularRegistrationsTableToolbarActions({
- table,
-}: VendorRegularRegistrationsTableToolbarActionsProps) {
- const router = useRouter()
- const { data: session } = useSession()
-
- const [syncLoading, setSyncLoading] = useState<{
- missingContract: boolean;
- additionalInfo: boolean;
- legalSkip: boolean;
- registrationRequest: boolean;
- }>({
- missingContract: false,
- additionalInfo: false,
- legalSkip: false,
- registrationRequest: false,
- })
-
- const [skipDialogs, setSkipDialogs] = useState<{
- legalReview: boolean;
- }>({
- legalReview: false,
- })
-
- // 2-step 결재 프로세스를 위한 상태
- const [registrationRequestDialog, setRegistrationRequestDialog] = useState<{
- open: boolean;
- registration: VendorRegularRegistration | null;
- }>({
- open: false,
- registration: null,
- })
-
- const [approvalDialog, setApprovalDialog] = useState<{
- open: boolean;
- registration: VendorRegularRegistration | null;
- }>({
- open: false,
- registration: null,
- })
-
- // 결재를 위한 중간 상태 저장
- const [registrationFormData, setRegistrationFormData] = useState<RegistrationRequestData | null>(null)
- const [approvalVariables, setApprovalVariables] = useState<Record<string, string>>({})
-
- const selectedRows = table.getFilteredSelectedRowModel().rows.map(row => row.original)
-
-
-
- const handleSendMissingContractRequest = async () => {
- if (selectedRows.length === 0) {
- toast.error("이메일을 발송할 업체를 선택해주세요.")
- return
- }
-
- setSyncLoading(prev => ({ ...prev, missingContract: true }))
- try {
- const vendorIds = selectedRows.map(row => row.vendorId)
- const result = await sendMissingContractRequestEmails(vendorIds)
-
- if (result.success) {
- toast.success(result.message)
- } else {
- toast.error(result.error)
- }
- } catch (error) {
- console.error("Error sending missing contract request:", error)
- toast.error("누락계약요청 이메일 발송 중 오류가 발생했습니다.")
- } finally {
- setSyncLoading(prev => ({ ...prev, missingContract: false }))
- }
- }
-
- const handleSendAdditionalInfoRequest = async () => {
- if (selectedRows.length === 0) {
- toast.error("이메일을 발송할 업체를 선택해주세요.")
- return
- }
-
- setSyncLoading(prev => ({ ...prev, additionalInfo: true }))
- try {
- const vendorIds = selectedRows.map(row => row.vendorId)
- const result = await sendAdditionalInfoRequestEmails(vendorIds)
-
- if (result.success) {
- toast.success(result.message)
- } else {
- toast.error(result.error)
- }
- } catch (error) {
- console.error("Error sending additional info request:", error)
- toast.error("추가정보요청 이메일 발송 중 오류가 발생했습니다.")
- } finally {
- setSyncLoading(prev => ({ ...prev, additionalInfo: false }))
- }
- }
-
- const handleLegalReviewSkip = async (reason: string) => {
- const cpReviewRows = selectedRows.filter(row => row.status === "cp_review");
- if (cpReviewRows.length === 0) {
- toast.error("CP검토 상태인 업체를 선택해주세요.");
- return;
- }
-
- setSyncLoading(prev => ({ ...prev, legalSkip: true }));
- try {
- const vendorIds = cpReviewRows.map(row => row.vendorId);
- const result = await skipLegalReview(vendorIds, reason);
-
- if (result.success) {
- toast.success(result.message);
- router.refresh();
- } else {
- toast.error(result.error);
- }
- } catch (error) {
- console.error("Error skipping legal review:", error);
- toast.error("법무검토 Skip 처리 중 오류가 발생했습니다.");
- } finally {
- setSyncLoading(prev => ({ ...prev, legalSkip: false }));
- }
- };
-
- // 등록요청 핸들러 - Step 1: 정보 입력
- const handleRegistrationRequest = () => {
- const approvalReadyRows = selectedRows.filter(row => row.status === "approval_ready");
-
- if (approvalReadyRows.length === 0) {
- toast.error("조건충족 상태의 벤더를 선택해주세요.");
- return;
- }
-
- if (approvalReadyRows.length > 1) {
- toast.error("정규업체 등록 요청은 한 번에 하나씩만 가능합니다.");
- return;
- }
-
- setRegistrationRequestDialog({
- open: true,
- registration: approvalReadyRows[0],
- });
- };
-
- // 등록요청 정보 입력 완료 - Step 1에서 Step 2로 전환
- const handleRegistrationRequestSubmit = async (requestData: RegistrationRequestData) => {
- if (!registrationRequestDialog.registration || !session?.user) return;
-
- try {
- // 폼 데이터 저장
- setRegistrationFormData(requestData);
-
- // 결재 템플릿 변수 생성
- const requestedAt = new Date();
- const variables = await mapRegistrationToTemplateVariables({
- requestData,
- requestedAt,
- });
-
- setApprovalVariables(variables);
-
- // RegistrationRequestDialog 닫고 ApprovalPreviewDialog 열기
- setRegistrationRequestDialog({ open: false, registration: null });
- setApprovalDialog({
- open: true,
- registration: registrationRequestDialog.registration,
- });
- } catch (error) {
- console.error("결재 준비 중 오류 발생:", error);
- toast.error("결재 준비 중 오류가 발생했습니다.");
- }
- };
-
- // 결재 상신 - Step 2: 결재선 선택 후 최종 상신
- const handleApprovalSubmit = async (approvers: any[]) => {
- if (!approvalDialog.registration || !registrationFormData || !session?.user) {
- toast.error("세션 정보가 없습니다.");
- return;
- }
-
- setSyncLoading(prev => ({ ...prev, registrationRequest: true }));
- try {
- // 결재선에서 EP ID 추출 (상신자 제외)
- const approverEpIds = approvers
- .filter((line) => line.seq !== "0" && line.epId)
- .map((line) => line.epId!);
-
- // 결재 워크플로우 시작
- const result = await registerVendorWithApproval({
- registrationId: approvalDialog.registration.id,
- requestData: registrationFormData,
- vendorId: approvalDialog.registration.vendorId, // vendors 테이블에서 정보를 가져오기 위한 vendorId
- currentUser: {
- id: Number(session.user.id),
- epId: session.user.epId || null,
- email: session.user.email || undefined,
- },
- approvers: approverEpIds,
- });
-
- if (result.status === 'pending_approval') {
- // 성공 시에만 상태 초기화 및 페이지 리로드
- setRegistrationFormData(null);
- setApprovalVariables({});
- setApprovalDialog({ open: false, registration: null });
- toast.success("정규업체 등록 결재가 상신되었습니다.");
- router.refresh();
- }
- } catch (error) {
- console.error("결재 상신 중 오류:", error);
- toast.error("결재 상신 중 오류가 발생했습니다.");
- } finally {
- setSyncLoading(prev => ({ ...prev, registrationRequest: false }));
- }
- };
-
- // CP검토 상태인 선택된 행들 개수
- const cpReviewCount = selectedRows.filter(row => row.status === "cp_review").length;
-
- // 조건충족 상태인 선택된 행들 개수
- const approvalReadyCount = selectedRows.filter(row => row.status === "approval_ready").length;
-
- return (
- <div className="flex items-center gap-2">
- <Button
- variant="outline"
- size="sm"
- onClick={handleSendMissingContractRequest}
- disabled={syncLoading.missingContract || selectedRows.length === 0}
- >
- <FileWarning className="mr-2 h-4 w-4" />
- {syncLoading.missingContract ? "발송 중..." : "누락계약요청"}
- </Button>
-
- <Button
- variant="outline"
- size="sm"
- onClick={handleSendAdditionalInfoRequest}
- disabled={syncLoading.additionalInfo || selectedRows.length === 0}
- >
- <Mail className="mr-2 h-4 w-4" />
- {syncLoading.additionalInfo ? "발송 중..." : "추가정보요청"}
- </Button>
-
- <Button
- variant="outline"
- size="sm"
- onClick={() => setSkipDialogs(prev => ({ ...prev, legalReview: true }))}
- disabled={syncLoading.legalSkip || cpReviewCount === 0}
- >
- <Scale className="mr-2 h-4 w-4" />
- {syncLoading.legalSkip ? "처리 중..." : "GTC Skip"}
- </Button>
-
- <Button
- variant="default"
- size="sm"
- onClick={handleRegistrationRequest}
- disabled={syncLoading.registrationRequest || approvalReadyCount === 0}
- >
- <FileText className="mr-2 h-4 w-4" />
- {syncLoading.registrationRequest ? "처리 중..." : "등록요청"}
- </Button>
-
- <SkipReasonDialog
- open={skipDialogs.legalReview}
- onOpenChange={(open) => setSkipDialogs(prev => ({ ...prev, legalReview: open }))}
- title="GTC Skip"
- description={`선택된 ${cpReviewCount}개 업체의 GTC를 Skip하고 CP완료 상태로 변경합니다. Skip 사유를 입력해주세요.`}
- onConfirm={handleLegalReviewSkip}
- loading={syncLoading.legalSkip}
- />
-
- <RegistrationRequestDialog
- open={registrationRequestDialog.open}
- onOpenChange={(open) => setRegistrationRequestDialog(prev => ({ ...prev, open }))}
- registration={registrationRequestDialog.registration}
- onSubmit={handleRegistrationRequestSubmit}
- />
-
- {/* 결재 미리보기 Dialog - 정규업체 등록 */}
- {session?.user && approvalDialog.registration && (
- <ApprovalPreviewDialog
- open={approvalDialog.open}
- onOpenChange={(open) => {
- setApprovalDialog(prev => ({ ...prev, open }));
- if (!open) {
- // 다이얼로그가 닫히면 폼 데이터도 초기화
- setRegistrationFormData(null);
- setApprovalVariables({});
- }
- }}
- templateName="정규업체 등록"
- variables={approvalVariables}
- title={`정규업체 등록 - ${approvalDialog.registration.companyName}`}
- description={`${approvalDialog.registration.companyName} 정규업체 등록 요청`}
- currentUser={{
- id: Number(session.user.id),
- epId: session.user.epId || null,
- name: session.user.name || null,
- email: session.user.email || '',
- }}
- onSubmit={handleApprovalSubmit}
- />
- )}
- </div>
- )
-}
+"use client"
+
+import { type Table } from "@tanstack/react-table"
+import { toast } from "sonner"
+
+import { Button } from "@/components/ui/button"
+import { Mail, FileWarning, Scale, FileText } from "lucide-react"
+import type { VendorRegularRegistration } from "@/config/vendorRegularRegistrationsColumnsConfig"
+import {
+ sendMissingContractRequestEmails,
+ sendAdditionalInfoRequestEmails,
+ skipLegalReview
+} from "../service"
+import { useState } from "react"
+import { SkipReasonDialog } from "@/components/vendor-regular-registrations/skip-reason-dialog"
+import { RegistrationRequestDialog } from "@/components/vendor-regular-registrations/registration-request-dialog"
+import { ApprovalPreviewDialog } from "@/lib/approval/approval-preview-dialog"
+import { useRouter } from "next/navigation"
+import { useSession } from "next-auth/react"
+import { registerVendorWithApproval } from "../approval-actions"
+import { mapRegistrationToTemplateVariables } from "../handlers"
+import type { RegistrationRequestData } from "@/components/vendor-regular-registrations/registration-request-dialog"
+
+interface VendorRegularRegistrationsTableToolbarActionsProps {
+ table: Table<VendorRegularRegistration>
+}
+
+export function VendorRegularRegistrationsTableToolbarActions({
+ table,
+}: VendorRegularRegistrationsTableToolbarActionsProps) {
+ const router = useRouter()
+ const { data: session } = useSession()
+
+ const [syncLoading, setSyncLoading] = useState<{
+ missingContract: boolean;
+ additionalInfo: boolean;
+ legalSkip: boolean;
+ registrationRequest: boolean;
+ }>({
+ missingContract: false,
+ additionalInfo: false,
+ legalSkip: false,
+ registrationRequest: false,
+ })
+
+ const [skipDialogs, setSkipDialogs] = useState<{
+ legalReview: boolean;
+ }>({
+ legalReview: false,
+ })
+
+ // 2-step 결재 프로세스를 위한 상태
+ const [registrationRequestDialog, setRegistrationRequestDialog] = useState<{
+ open: boolean;
+ registration: VendorRegularRegistration | null;
+ }>({
+ open: false,
+ registration: null,
+ })
+
+ const [approvalDialog, setApprovalDialog] = useState<{
+ open: boolean;
+ registration: VendorRegularRegistration | null;
+ }>({
+ open: false,
+ registration: null,
+ })
+
+ // 결재를 위한 중간 상태 저장
+ const [registrationFormData, setRegistrationFormData] = useState<RegistrationRequestData | null>(null)
+ const [approvalVariables, setApprovalVariables] = useState<Record<string, string>>({})
+
+ const selectedRows = table.getFilteredSelectedRowModel().rows.map(row => row.original)
+
+
+
+ const handleSendMissingContractRequest = async () => {
+ if (selectedRows.length === 0) {
+ toast.error("이메일을 발송할 업체를 선택해주세요.")
+ return
+ }
+
+ setSyncLoading(prev => ({ ...prev, missingContract: true }))
+ try {
+ const vendorIds = selectedRows.map(row => row.vendorId)
+ const result = await sendMissingContractRequestEmails(vendorIds)
+
+ if (result.success) {
+ toast.success(result.message)
+ } else {
+ toast.error(result.error)
+ }
+ } catch (error) {
+ console.error("Error sending missing contract request:", error)
+ toast.error("누락계약요청 이메일 발송 중 오류가 발생했습니다.")
+ } finally {
+ setSyncLoading(prev => ({ ...prev, missingContract: false }))
+ }
+ }
+
+ const handleSendAdditionalInfoRequest = async () => {
+ if (selectedRows.length === 0) {
+ toast.error("이메일을 발송할 업체를 선택해주세요.")
+ return
+ }
+
+ setSyncLoading(prev => ({ ...prev, additionalInfo: true }))
+ try {
+ const vendorIds = selectedRows.map(row => row.vendorId)
+ const result = await sendAdditionalInfoRequestEmails(vendorIds)
+
+ if (result.success) {
+ toast.success(result.message)
+ } else {
+ toast.error(result.error)
+ }
+ } catch (error) {
+ console.error("Error sending additional info request:", error)
+ toast.error("추가정보요청 이메일 발송 중 오류가 발생했습니다.")
+ } finally {
+ setSyncLoading(prev => ({ ...prev, additionalInfo: false }))
+ }
+ }
+
+ const handleLegalReviewSkip = async (reason: string) => {
+ const cpReviewRows = selectedRows.filter(row => row.status === "cp_review");
+ if (cpReviewRows.length === 0) {
+ toast.error("CP검토 상태인 업체를 선택해주세요.");
+ return;
+ }
+
+ setSyncLoading(prev => ({ ...prev, legalSkip: true }));
+ try {
+ const vendorIds = cpReviewRows.map(row => row.vendorId);
+ const result = await skipLegalReview(vendorIds, reason);
+
+ if (result.success) {
+ toast.success(result.message);
+ router.refresh();
+ } else {
+ toast.error(result.error);
+ }
+ } catch (error) {
+ console.error("Error skipping legal review:", error);
+ toast.error("법무검토 Skip 처리 중 오류가 발생했습니다.");
+ } finally {
+ setSyncLoading(prev => ({ ...prev, legalSkip: false }));
+ }
+ };
+
+ // 등록요청 핸들러 - Step 1: 정보 입력
+ const handleRegistrationRequest = () => {
+ const approvalReadyRows = selectedRows.filter(row => row.status === "approval_ready");
+
+ if (approvalReadyRows.length === 0) {
+ toast.error("조건충족 상태의 벤더를 선택해주세요.");
+ return;
+ }
+
+ if (approvalReadyRows.length > 1) {
+ toast.error("정규업체 등록 요청은 한 번에 하나씩만 가능합니다.");
+ return;
+ }
+
+ setRegistrationRequestDialog({
+ open: true,
+ registration: approvalReadyRows[0],
+ });
+ };
+
+ // 등록요청 정보 입력 완료 - Step 1에서 Step 2로 전환
+ const handleRegistrationRequestSubmit = async (requestData: RegistrationRequestData) => {
+ if (!registrationRequestDialog.registration || !session?.user) return;
+
+ try {
+ // 폼 데이터 저장
+ setRegistrationFormData(requestData);
+
+ // 결재 템플릿 변수 생성 (vendorId 포함)
+ const requestedAt = new Date();
+ const variables = await mapRegistrationToTemplateVariables({
+ requestData,
+ requestedAt,
+ vendorId: registrationRequestDialog.registration.vendorId, // vendors 테이블에서 추가 정보 가져오기
+ });
+
+ setApprovalVariables(variables);
+
+ // RegistrationRequestDialog 닫고 ApprovalPreviewDialog 열기
+ setRegistrationRequestDialog({ open: false, registration: null });
+ setApprovalDialog({
+ open: true,
+ registration: registrationRequestDialog.registration,
+ });
+ } catch (error) {
+ console.error("결재 준비 중 오류 발생:", error);
+ toast.error("결재 준비 중 오류가 발생했습니다.");
+ }
+ };
+
+ // 결재 상신 - Step 2: 결재선 선택 후 최종 상신
+ const handleApprovalSubmit = async ({ approvers, title, attachments }: { approvers: string[], title: string, attachments?: File[] }) => {
+ if (!approvalDialog.registration || !registrationFormData || !session?.user) {
+ toast.error("세션 정보가 없습니다.");
+ return;
+ }
+
+ setSyncLoading(prev => ({ ...prev, registrationRequest: true }));
+ try {
+ // 결재 워크플로우 시작 (approvers는 이미 EP ID 배열)
+ const result = await registerVendorWithApproval({
+ registrationId: approvalDialog.registration.id,
+ requestData: registrationFormData,
+ vendorId: approvalDialog.registration.vendorId, // vendors 테이블에서 정보를 가져오기 위한 vendorId
+ currentUser: {
+ id: Number(session.user.id),
+ epId: session.user.epId || null,
+ email: session.user.email || undefined,
+ },
+ approvers: approvers,
+ });
+
+ if (result.status === 'pending_approval') {
+ // 성공 시에만 상태 초기화 및 페이지 리로드
+ setRegistrationFormData(null);
+ setApprovalVariables({});
+ setApprovalDialog({ open: false, registration: null });
+ toast.success("정규업체 등록 결재가 상신되었습니다.");
+ router.refresh();
+ }
+ } catch (error) {
+ console.error("결재 상신 중 오류:", error);
+ toast.error("결재 상신 중 오류가 발생했습니다.");
+ } finally {
+ setSyncLoading(prev => ({ ...prev, registrationRequest: false }));
+ }
+ };
+
+ // CP검토 상태인 선택된 행들 개수
+ const cpReviewCount = selectedRows.filter(row => row.status === "cp_review").length;
+
+ // 조건충족 상태인 선택된 행들 개수
+ const approvalReadyCount = selectedRows.filter(row => row.status === "approval_ready").length;
+
+ return (
+ <div className="flex items-center gap-2">
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={handleSendMissingContractRequest}
+ disabled={syncLoading.missingContract || selectedRows.length === 0}
+ >
+ <FileWarning className="mr-2 h-4 w-4" />
+ {syncLoading.missingContract ? "발송 중..." : "누락계약요청"}
+ </Button>
+
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={handleSendAdditionalInfoRequest}
+ disabled={syncLoading.additionalInfo || selectedRows.length === 0}
+ >
+ <Mail className="mr-2 h-4 w-4" />
+ {syncLoading.additionalInfo ? "발송 중..." : "추가정보요청"}
+ </Button>
+
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={() => setSkipDialogs(prev => ({ ...prev, legalReview: true }))}
+ disabled={syncLoading.legalSkip || cpReviewCount === 0}
+ >
+ <Scale className="mr-2 h-4 w-4" />
+ {syncLoading.legalSkip ? "처리 중..." : "GTC Skip"}
+ </Button>
+
+ <Button
+ variant="default"
+ size="sm"
+ onClick={handleRegistrationRequest}
+ disabled={syncLoading.registrationRequest || approvalReadyCount === 0}
+ >
+ <FileText className="mr-2 h-4 w-4" />
+ {syncLoading.registrationRequest ? "처리 중..." : "등록요청"}
+ </Button>
+
+ <SkipReasonDialog
+ open={skipDialogs.legalReview}
+ onOpenChange={(open) => setSkipDialogs(prev => ({ ...prev, legalReview: open }))}
+ title="GTC Skip"
+ description={`선택된 ${cpReviewCount}개 업체의 GTC를 Skip하고 CP완료 상태로 변경합니다. Skip 사유를 입력해주세요.`}
+ onConfirm={handleLegalReviewSkip}
+ loading={syncLoading.legalSkip}
+ />
+
+ <RegistrationRequestDialog
+ open={registrationRequestDialog.open}
+ onOpenChange={(open) => setRegistrationRequestDialog(prev => ({ ...prev, open }))}
+ registration={registrationRequestDialog.registration}
+ onSubmit={handleRegistrationRequestSubmit}
+ />
+
+ {/* 결재 미리보기 Dialog - 정규업체 등록 */}
+ {session?.user && session.user.epId && approvalDialog.registration && (
+ <ApprovalPreviewDialog
+ open={approvalDialog.open}
+ onOpenChange={(open) => {
+ setApprovalDialog(prev => ({ ...prev, open }));
+ if (!open) {
+ // 다이얼로그가 닫히면 폼 데이터도 초기화
+ setRegistrationFormData(null);
+ setApprovalVariables({});
+ }
+ }}
+ templateName="정규업체 등록"
+ variables={approvalVariables}
+ title={`정규업체 등록 - ${approvalDialog.registration.companyName}`}
+ currentUser={{
+ id: Number(session.user.id),
+ epId: session.user.epId,
+ name: session.user.name || undefined,
+ email: session.user.email || undefined,
+ }}
+ onConfirm={handleApprovalSubmit}
+ />
+ )}
+ </div>
+ )
+}