"use client"; import React, { useState, useEffect } from "react"; import { useSession } from "next-auth/react"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, } from "@/components/ui/dialog"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, } from "@/components/ui/command"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Button } from "@/components/ui/button"; import { Loader2, Check, ChevronsUpDown } from "lucide-react"; import { toast } from "sonner"; import { cn } from "@/lib/utils"; import { createSubmissionAction, // 새로운 액션 이름 fetchDocumentsByProjectAndPackage, // 업데이트된 액션 fetchStagesByDocumentIdPlant, fetchSubmissionsByStageParams, // revisions 대신 submissions } from "@/lib/vendor-document/service"; import type { StageDocument, StageIssueStage, } from "@/db/schema/vendorDocu"; interface PublishDialogProps { open: boolean; onOpenChange: (open: boolean) => void; projectCode: string; packageCode: string; formCode: string; fileBlob?: Blob; } export const PublishDialog: React.FC = ({ open, onOpenChange, projectCode, packageCode, formCode, fileBlob, }) => { // Get current user session from next-auth const { data: session } = useSession(); // State for form data const [documents, setDocuments] = useState([]); const [stages, setStages] = useState([]); const [latestRevisionCode, setLatestRevisionCode] = useState(""); const [latestRevisionNumber, setLatestRevisionNumber] = useState(0); // State for document search const [openDocumentCombobox, setOpenDocumentCombobox] = useState(false); const [documentSearchValue, setDocumentSearchValue] = useState(""); // Selected values const [selectedDocId, setSelectedDocId] = useState(""); const [selectedDocumentDisplay, setSelectedDocumentDisplay] = useState(""); const [selectedStage, setSelectedStage] = useState(""); const [revisionCodeInput, setRevisionCodeInput] = useState(""); const [submitterName, setSubmitterName] = useState(""); const [submissionTitle, setSubmissionTitle] = useState(""); const [submissionDescription, setSubmissionDescription] = useState(""); const [customFileName, setCustomFileName] = useState(`${formCode}_document.docx`); // Loading states const [isLoading, setIsLoading] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); // Filter documents by search const filteredDocuments = documentSearchValue ? documents.filter(doc => doc.docNumber.toLowerCase().includes(documentSearchValue.toLowerCase()) || doc.title.toLowerCase().includes(documentSearchValue.toLowerCase()) ) : documents; // Set submitter name from session when dialog opens useEffect(() => { if (open && session?.user?.name) { setSubmitterName(session.user.name); } }, [open, session]); // Reset all fields when dialog opens/closes useEffect(() => { if (open) { setSelectedDocId(""); setSelectedDocumentDisplay(""); setSelectedStage(""); setRevisionCodeInput(""); setSubmissionTitle(""); setSubmissionDescription(""); // Only set submitterName if not already set from session if (!session?.user?.name) setSubmitterName(""); setLatestRevisionCode(""); setLatestRevisionNumber(0); setCustomFileName(`${formCode}_document.docx`); setDocumentSearchValue(""); } }, [open, formCode, session]); // Fetch documents based on projectCode and packageCode useEffect(() => { async function loadDocuments() { if (projectCode && packageCode && open) { setIsLoading(true); try { const docs = await fetchDocumentsByProjectAndPackage(projectCode, packageCode); setDocuments(docs); } catch (error) { console.error("Error fetching documents:", error); toast.error("Failed to load documents"); } finally { setIsLoading(false); } } } loadDocuments(); }, [projectCode, packageCode, open]); // Fetch stages when document is selected useEffect(() => { async function loadStages() { if (selectedDocId) { setIsLoading(true); // Reset dependent fields setSelectedStage(""); setRevisionCodeInput(""); setLatestRevisionCode(""); setLatestRevisionNumber(0); try { const stagesList = await fetchStagesByDocumentIdPlant(parseInt(selectedDocId, 10)); setStages(stagesList); } catch (error) { console.error("Error fetching stages:", error); toast.error("Failed to load stages"); } finally { setIsLoading(false); } } else { setStages([]); } } loadStages(); }, [selectedDocId]); // Fetch latest submission (revision) when stage is selected useEffect(() => { async function loadLatestSubmission() { if (selectedDocId && selectedStage) { setIsLoading(true); try { const submissionsList = await fetchSubmissionsByStageParams( parseInt(selectedDocId, 10), selectedStage ); // Find the latest submission (assuming sorted by revision number) if (submissionsList.length > 0) { // Sort submissions by revision number descending const sortedSubmissions = [...submissionsList].sort((a, b) => b.revisionNumber - a.revisionNumber ); const latestSubmission = sortedSubmissions[0]; setLatestRevisionCode(latestSubmission.revisionCode); setLatestRevisionNumber(latestSubmission.revisionNumber); // Auto-increment revision code if (latestSubmission.revisionCode.match(/^\d+$/)) { // If it's a number, increment it const nextRevision = String(parseInt(latestSubmission.revisionCode, 10) + 1); setRevisionCodeInput(nextRevision); } else if (latestSubmission.revisionCode.match(/^[A-Za-z]$/)) { // If it's a single letter, get the next letter const currentChar = latestSubmission.revisionCode.charCodeAt(0); const nextChar = String.fromCharCode(currentChar + 1); setRevisionCodeInput(nextChar); } else if (latestSubmission.revisionCode.toLowerCase().startsWith("rev")) { // Handle "Rev0", "Rev1" format const numMatch = latestSubmission.revisionCode.match(/\d+$/); if (numMatch) { const nextNum = parseInt(numMatch[0], 10) + 1; setRevisionCodeInput(`Rev${nextNum}`); } else { setRevisionCodeInput(""); } } else { // For other formats, just show the latest as reference setRevisionCodeInput(""); } } else { // If no submissions exist, set default values setLatestRevisionCode(""); setLatestRevisionNumber(0); setRevisionCodeInput("Rev0"); // Start with Rev0 } } catch (error) { console.error("Error fetching submissions:", error); toast.error("Failed to load submission information"); } finally { setIsLoading(false); } } else { setLatestRevisionCode(""); setLatestRevisionNumber(0); setRevisionCodeInput(""); } } loadLatestSubmission(); }, [selectedDocId, selectedStage]); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!selectedDocId || !selectedStage || !revisionCodeInput || !fileBlob) { toast.error("Please fill in all required fields"); return; } setIsSubmitting(true); try { // Create FormData const formData = new FormData(); formData.append("documentId", selectedDocId); formData.append("stageName", selectedStage); formData.append("revisionCode", revisionCodeInput); formData.append("customFileName", customFileName); if (submitterName) { formData.append("submittedBy", submitterName); } if (session?.user?.email) { formData.append("submittedByEmail", session.user.email); } if (submissionTitle) { formData.append("submissionTitle", submissionTitle); } if (submissionDescription) { formData.append("submissionDescription", submissionDescription); } // Get vendor info from selected document const selectedDoc = documents.find(doc => doc.id === parseInt(selectedDocId, 10)); if (selectedDoc) { formData.append("vendorId", String(selectedDoc.vendorId)); } // Append file as attachment if (fileBlob) { const file = new File([fileBlob], customFileName, { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }); formData.append("attachment", file); } // Call server action const result = await createSubmissionAction(formData); if (result.success) { toast.success("Document published successfully!"); onOpenChange(false); } else { toast.error(result.error || "Failed to publish document"); } } catch (error) { console.error("Error publishing document:", error); toast.error("Failed to publish document"); } finally { setIsSubmitting(false); } }; return ( Publish Document Select document, stage, and revision to publish the vendor document.
{/* Document Selection with Search */}
No document found. {filteredDocuments.map((doc) => ( { setSelectedDocId(String(doc.id)); setSelectedDocumentDisplay(`${doc.docNumber} - ${doc.title}`); setOpenDocumentCombobox(false); }} className="flex items-center" > {doc.docNumber} - {doc.title} ))}
{/* Stage Selection */}
{/* Revision Code Input */}
setRevisionCodeInput(e.target.value)} placeholder="Enter revision code (e.g., Rev0, A, 1)" disabled={isLoading || !selectedStage} /> {latestRevisionCode && (

Latest revision: {latestRevisionCode} (#{latestRevisionNumber})

)}
setSubmissionTitle(e.target.value)} placeholder="Optional submission title" />
setCustomFileName(e.target.value)} placeholder="Custom file name" />
setSubmitterName(e.target.value)} placeholder="Your name" className={session?.user?.name ? "opacity-70" : ""} readOnly={!!session?.user?.name} /> {session?.user?.name && (

Using your account name from login

)}