summaryrefslogtreecommitdiff
path: root/lib/vendor-investigation/table/investigation-cancel-plan-button.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vendor-investigation/table/investigation-cancel-plan-button.tsx')
-rw-r--r--lib/vendor-investigation/table/investigation-cancel-plan-button.tsx91
1 files changed, 91 insertions, 0 deletions
diff --git a/lib/vendor-investigation/table/investigation-cancel-plan-button.tsx b/lib/vendor-investigation/table/investigation-cancel-plan-button.tsx
new file mode 100644
index 00000000..26016742
--- /dev/null
+++ b/lib/vendor-investigation/table/investigation-cancel-plan-button.tsx
@@ -0,0 +1,91 @@
+"use client"
+
+import * as React from "react"
+import { type Table } from "@tanstack/react-table"
+import { VendorInvestigationsViewWithContacts } from "@/config/vendorInvestigationsColumnsConfig"
+import { Button } from "@/components/ui/button"
+import { RotateCcw } from "lucide-react"
+import { toast } from "sonner"
+import { cancelInvestigationPlanAction } from "../service"
+import { getSiteVisitRequestAction } from "@/lib/site-visit/service"
+
+interface Props {
+ table: Table<VendorInvestigationsViewWithContacts>
+}
+
+export function InvestigationCancelPlanButton({ table }: Props) {
+ const [loading, setLoading] = React.useState(false)
+ const selected = table.getSelectedRowModel().rows[0]?.original as VendorInvestigationsViewWithContacts | undefined
+
+ const canCancel = React.useMemo(() => {
+ if (!selected) return false
+ // 이미 취소 상태로 되돌릴 필요가 없거나, QM_REVIEW_CONFIRMED이면 취소 불필요
+ if (selected.investigationStatus === "QM_REVIEW_CONFIRMED") return false
+ if (!selected.investigationMethod) return false
+
+ const method = selected.investigationMethod
+ // 1) 서류평가: 실사결과 입력 전까지 (평가 결과 없을 때)
+ if (method === "DOCUMENT_EVAL") {
+ return selected.evaluationResult == null
+ }
+ // 2) 구매자체평가: 자체평가 입력 전까지 (간주: investigationNotes가 비어있을 때)
+ if (method === "PURCHASE_SELF_EVAL") {
+ return !selected.investigationNotes && selected.evaluationResult == null
+ }
+ // 3) 방문/제품평가: 방문요청 전까지 (site visit request 없을 때)
+ if (method === "PRODUCT_INSPECTION" || method === "SITE_VISIT_EVAL") {
+ // 낙관적으로 UI에선 일단 true로 두고, 클릭 시 서버 확인
+ return true
+ }
+ return false
+ }, [selected])
+
+ const onCancel = async () => {
+ if (!selected) return
+ try {
+ setLoading(true)
+
+ // 방문/제품평가인 경우, 방문요청 존재 여부 서버 확인
+ if (selected.investigationMethod === "PRODUCT_INSPECTION" || selected.investigationMethod === "SITE_VISIT_EVAL") {
+ try {
+ const req = await getSiteVisitRequestAction(selected.investigationId)
+ if (req.success && req.data) {
+ toast.error("방문요청 이후에는 실사계획을 취소할 수 없습니다.")
+ setLoading(false)
+ return
+ }
+ } catch {}
+ }
+
+ const res = await cancelInvestigationPlanAction(selected.investigationId)
+ if (!res.success) {
+ toast.error(res.error || "실사계획 취소에 실패했습니다.")
+ setLoading(false)
+ return
+ }
+ toast.success("실사계획을 취소하고 상태를 'QM 검토 완료'로 되돌렸습니다.")
+ // 선택 해제 및 테이블 리프레시 유도
+ table.resetRowSelection()
+ } catch (e) {
+ toast.error("실사계획 취소 중 오류가 발생했습니다.")
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ return (
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={onCancel}
+ disabled={loading || !canCancel}
+ className="gap-2"
+ title="실사계획 취소"
+ >
+ <RotateCcw className="size-4" />
+ 취소
+ </Button>
+ )
+}
+
+