From c228a89c2834ee63b209bad608837c39643f350e Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Mon, 28 Jul 2025 11:44:16 +0000
Subject: (대표님) 의존성 docx 추가, basicContract API, gtc(계약일반조건),
 벤더평가 esg 평가데이터 내보내기 개선, S-EDP 피드백 대응(CLS_ID, ITEM NO 등)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../table/delete-gtc-clauses-dialog.tsx            | 175 +++++++++++++++++++++
 1 file changed, 175 insertions(+)
 create mode 100644 lib/gtc-contract/gtc-clauses/table/delete-gtc-clauses-dialog.tsx

(limited to 'lib/gtc-contract/gtc-clauses/table/delete-gtc-clauses-dialog.tsx')

diff --git a/lib/gtc-contract/gtc-clauses/table/delete-gtc-clauses-dialog.tsx b/lib/gtc-contract/gtc-clauses/table/delete-gtc-clauses-dialog.tsx
new file mode 100644
index 00000000..29483c57
--- /dev/null
+++ b/lib/gtc-contract/gtc-clauses/table/delete-gtc-clauses-dialog.tsx
@@ -0,0 +1,175 @@
+"use client"
+
+import * as React from "react"
+import { Loader, Trash2, AlertTriangle } from "lucide-react"
+import { toast } from "sonner"
+
+import {
+  AlertDialog,
+  AlertDialogAction,
+  AlertDialogCancel,
+  AlertDialogContent,
+  AlertDialogDescription,
+  AlertDialogFooter,
+  AlertDialogHeader,
+  AlertDialogTitle,
+  AlertDialogTrigger,
+} from "@/components/ui/alert-dialog"
+import { Button } from "@/components/ui/button"
+import { Badge } from "@/components/ui/badge"
+
+import { type GtcClauseTreeView } from "@/db/schema/gtc"
+import { useSession } from "next-auth/react"
+import { deleteGtcClauses } from "../service"
+
+interface DeleteGtcClausesDialogProps
+  extends React.ComponentPropsWithRef<typeof AlertDialog> {
+  gtcClauses: GtcClauseTreeView[]
+  showTrigger?: boolean
+  onSuccess?: () => void
+}
+
+export function DeleteGtcClausesDialog({
+  gtcClauses,
+  showTrigger = true,
+  onSuccess,
+  ...props
+}: DeleteGtcClausesDialogProps) {
+  const [isDeletePending, startDeleteTransition] = React.useTransition()
+  const { data: session } = useSession()
+
+  const currentUserId = React.useMemo(() => {
+    return session?.user?.id ? Number(session.user.id) : null
+  }, [session])
+
+  function onDelete() {
+    startDeleteTransition(async () => {
+      if (!currentUserId) {
+        toast.error("로그인이 필요합니다")
+        return
+      }
+
+      try {
+        // ✅ 한 번에 모든 조항 삭제 (배열로 전달)
+        const ids = gtcClauses.map(clause => clause.id)
+        const result = await deleteGtcClauses(ids)
+        
+        if (result.error) {
+          toast.error(`삭제 중 오류가 발생했습니다: ${result.error}`)
+          return
+        }
+
+        props.onOpenChange?.(false)
+        toast.success(
+          `${result.deletedCount}개의 조항이 삭제되었습니다.`
+        )
+        onSuccess?.()
+      } catch (error) {
+        console.error("Delete error:", error)
+        toast.error("조항 삭제 중 오류가 발생했습니다.")
+      }
+    })
+  }
+
+  const clausesWithChildren = gtcClauses.filter(clause => clause.childrenCount > 0)
+  const hasChildrenWarning = clausesWithChildren.length > 0
+  
+  // 총 삭제될 하위 조항 수 계산
+  const totalChildrenCount = clausesWithChildren.reduce((sum, clause) => sum + clause.childrenCount, 0)
+
+  return (
+    <AlertDialog {...props}>
+      {showTrigger && (
+        <AlertDialogTrigger asChild>
+          <Button variant="outline" size="sm">
+            <Trash2 className="mr-2 h-4 w-4" />
+            삭제 ({gtcClauses.length})
+          </Button>
+        </AlertDialogTrigger>
+      )}
+
+      <AlertDialogContent className="max-w-md">
+        <AlertDialogHeader>
+          <div className="flex items-center gap-2">
+            <AlertTriangle className="h-5 w-5 text-destructive" />
+            <AlertDialogTitle>조항 삭제</AlertDialogTitle>
+          </div>
+          <AlertDialogDescription asChild>
+            <div className="space-y-3">
+              <p>
+                선택한 {gtcClauses.length}개의 조항을 <strong>완전히 삭제</strong>하시겠습니까?
+                이 작업은 되돌릴 수 없습니다.
+              </p>
+
+              {/* 삭제할 조항 목록 */}
+              <div className="space-y-2">
+                <div className="text-sm font-medium">삭제할 조항:</div>
+                <div className="max-h-32 overflow-y-auto space-y-1">
+                  {gtcClauses.map((clause) => (
+                    <div key={clause.id} className="flex items-center gap-2 text-sm p-2 bg-muted/50 rounded">
+                      <Badge variant="outline" className="text-xs">
+                        {clause.itemNumber}
+                      </Badge>
+                      <span className="flex-1 truncate">{clause.subtitle}</span>
+                      {clause.childrenCount > 0 && (
+                        <Badge variant="destructive" className="text-xs">
+                          하위 {clause.childrenCount}개
+                        </Badge>
+                      )}
+                    </div>
+                  ))}
+                </div>
+              </div>
+
+              {/* 하위 조항 경고 */}
+              {hasChildrenWarning && (
+                <div className="p-3 bg-destructive/10 border border-destructive/20 rounded-md">
+                  <div className="flex items-center gap-2 text-destructive text-sm font-medium mb-2">
+                    <AlertTriangle className="h-4 w-4" />
+                    중요: 하위 조항 포함 삭제
+                  </div>
+                  <div className="space-y-1 text-sm text-destructive/80">
+                    <p>하위 조항이 있는 조항을 삭제하면 모든 하위 조항도 함께 삭제됩니다.</p>
+                    <p className="font-medium">
+                      총 삭제될 조항: {gtcClauses.length + totalChildrenCount}개
+                      <span className="text-xs ml-1">
+                        (선택한 {gtcClauses.length}개 + 하위 {totalChildrenCount}개)
+                      </span>
+                    </p>
+                  </div>
+                  <div className="mt-2 text-xs text-destructive/70">
+                    영향받는 조항: {clausesWithChildren.map(c => c.itemNumber).join(', ')}
+                  </div>
+                </div>
+              )}
+
+              <div className="p-2 bg-amber-50 border border-amber-200 rounded text-xs text-amber-800">
+                ⚠️ <strong>실제 삭제</strong>: 데이터베이스에서 완전히 제거되며 복구할 수 없습니다.
+              </div>
+            </div>
+          </AlertDialogDescription>
+        </AlertDialogHeader>
+
+        <AlertDialogFooter>
+          <AlertDialogCancel disabled={isDeletePending}>
+            Cancel
+          </AlertDialogCancel>
+          <AlertDialogAction
+            onClick={onDelete}
+            disabled={isDeletePending}
+            className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
+          >
+            {isDeletePending && (
+              <Loader
+                className="mr-2 size-4 animate-spin"
+                aria-hidden="true"
+              />
+            )}
+            <Trash2 className="mr-2 h-4 w-4" />
+            Delete Permanently
+          </AlertDialogAction>
+        </AlertDialogFooter>
+      </AlertDialogContent>
+    </AlertDialog>
+  )
+}
\ No newline at end of file
-- 
cgit v1.2.3

