"use client" import * as React from "react" import { useForm } from "react-hook-form" import { zodResolver } from "@hookform/resolvers/zod" import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from "@/components/ui/dialog" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Textarea } from "@/components/ui/textarea" import { Badge } from "@/components/ui/badge" import { Switch } from "@/components/ui/switch" import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, FormDescription, } from "@/components/ui/form" import { Loader, Edit, AlertCircle } from "lucide-react" import { toast } from "sonner" import { bulkUpdateGtcClausesSchema, type BulkUpdateGtcClausesSchema } from "@/lib/gtc-contract/gtc-clauses/validations" import { bulkUpdateGtcClauses } from "@/lib/gtc-contract/gtc-clauses/service" import { type GtcClauseTreeView } from "@/db/schema/gtc" import { useSession } from "next-auth/react" interface BulkUpdateGtcClausesDialogProps extends React.ComponentPropsWithRef { selectedClauses: GtcClauseTreeView[] } export function BulkUpdateGtcClausesDialog({ selectedClauses, ...props }: BulkUpdateGtcClausesDialogProps) { const [isUpdatePending, startUpdateTransition] = React.useTransition() const { data: session } = useSession() const currentUserId = React.useMemo(() => { return session?.user?.id ? Number(session.user.id) : null }, [session]) const form = useForm({ resolver: zodResolver(bulkUpdateGtcClausesSchema), defaultValues: { clauseIds: selectedClauses.map(clause => clause.id), updates: { category: "", isActive: true, }, editReason: "", }, }) React.useEffect(() => { if (selectedClauses.length > 0) { form.setValue("clauseIds", selectedClauses.map(clause => clause.id)) } }, [selectedClauses, form]) async function onSubmit(data: BulkUpdateGtcClausesSchema) { startUpdateTransition(async () => { if (!currentUserId) { toast.error("로그인이 필요합니다") return } try { const result = await bulkUpdateGtcClauses({ ...data, updatedById: currentUserId }) if (result.error) { toast.error(`에러: ${result.error}`) return } form.reset() props.onOpenChange?.(false) toast.success(`${selectedClauses.length}개의 조항이 수정되었습니다.`) } catch (error) { toast.error("조항 일괄 수정 중 오류가 발생했습니다.") } }) } function handleDialogOpenChange(nextOpen: boolean) { if (!nextOpen) { form.reset() } props.onOpenChange?.(nextOpen) } // 선택된 조항들의 통계 const categoryCounts = React.useMemo(() => { const counts: Record = {} selectedClauses.forEach(clause => { const category = clause.category || "미분류" counts[category] = (counts[category] || 0) + 1 }) return counts }, [selectedClauses]) const activeCount = selectedClauses.filter(clause => clause.isActive).length const inactiveCount = selectedClauses.length - activeCount if (selectedClauses.length === 0) { return null } return ( 조항 일괄 수정 선택한 {selectedClauses.length}개 조항의 공통 속성을 일괄 수정합니다. {/* 선택된 조항 요약 */}
선택된 조항 정보
총 조항 수
{selectedClauses.length}개
상태
{activeCount}개 활성 {inactiveCount > 0 && ( {inactiveCount}개 비활성 )}
{/* 분류별 통계 */}
현재 분류 현황
{Object.entries(categoryCounts).map(([category, count]) => ( {category}: {count}개 ))}
{/* 조항 미리보기 (최대 5개) */}
포함된 조항 (일부)
{selectedClauses.slice(0, 5).map(clause => (
{clause.itemNumber} {clause.subtitle}
))} {selectedClauses.length > 5 && (
... 외 {selectedClauses.length - 5}개 조항
)}
{/* 분류 수정 */} ( 분류 변경 (선택사항) 모든 선택된 조항의 분류가 동일한 값으로 변경됩니다. )} /> {/* 활성 상태 변경 */} (
활성 상태 선택된 모든 조항의 활성 상태를 설정합니다.
)} /> {/* 편집 사유 */} ( 편집 사유 *