diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-06-13 07:11:18 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-06-13 07:11:18 +0000 |
| commit | 0fddf148402fd6b99a1b3800d73679899bcb2ed3 (patch) | |
| tree | eb51c02e6fa6037ddcc38a3b57d10d8c739125cf /lib/vendor-document-list/ship/enhanced-document-sheet.tsx | |
| parent | c72d0897f7b37843109c86f61d97eba05ba3ca0d (diff) | |
(대표님) 20250613 16시 10분 global css, b-rfq, document 등
Diffstat (limited to 'lib/vendor-document-list/ship/enhanced-document-sheet.tsx')
| -rw-r--r-- | lib/vendor-document-list/ship/enhanced-document-sheet.tsx | 939 |
1 files changed, 0 insertions, 939 deletions
diff --git a/lib/vendor-document-list/ship/enhanced-document-sheet.tsx b/lib/vendor-document-list/ship/enhanced-document-sheet.tsx deleted file mode 100644 index 88e342c8..00000000 --- a/lib/vendor-document-list/ship/enhanced-document-sheet.tsx +++ /dev/null @@ -1,939 +0,0 @@ -// enhanced-document-sheet.tsx -"use client" - -import * as React from "react" -import { zodResolver } from "@hookform/resolvers/zod" -import { useForm } from "react-hook-form" -import { toast } from "sonner" -import { z } from "zod" -import { useRouter } from "next/navigation" -import { - Loader, - Save, - Upload, - Calendar, - User, - FileText, - AlertTriangle, - CheckCircle, - Clock, - Plus, - X -} from "lucide-react" - -import { Button } from "@/components/ui/button" -import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form" -import { - Select, - SelectContent, - SelectGroup, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select" -import { - Sheet, - SheetClose, - SheetContent, - SheetDescription, - SheetFooter, - SheetHeader, - SheetTitle, -} from "@/components/ui/sheet" -import { - Tabs, - TabsContent, - TabsList, - TabsTrigger, -} from "@/components/ui/tabs" -import { Input } from "@/components/ui/input" -import { Textarea } from "@/components/ui/textarea" -import { Badge } from "@/components/ui/badge" -import { Separator } from "@/components/ui/separator" -import { ScrollArea } from "@/components/ui/scroll-area" -import { Calendar as CalendarComponent } from "@/components/ui/calendar" -import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover" -import { cn } from "@/lib/utils" -import { format } from "date-fns" -import { ko } from "date-fns/locale" -import { EnhancedDocumentsView } from "@/db/schema/vendorDocu" - -// 드롭존과 파일 관련 컴포넌트들 -import { - Dropzone, - DropzoneDescription, - DropzoneInput, - DropzoneTitle, - DropzoneUploadIcon, - DropzoneZone, -} from "@/components/ui/dropzone" -import { - FileList, - FileListAction, - FileListDescription, - FileListHeader, - FileListIcon, - FileListInfo, - FileListItem, - FileListName, - FileListSize, -} from "@/components/ui/file-list" -import prettyBytes from "pretty-bytes" - -// 스키마 정의 -const enhancedDocumentSchema = z.object({ - // 기본 문서 정보 - docNumber: z.string().min(1, "문서번호는 필수입니다"), - title: z.string().min(1, "제목은 필수입니다"), - pic: z.string().optional(), - status: z.string().min(1, "상태는 필수입니다"), - issuedDate: z.date().optional(), - - // 스테이지 관리 (plant 타입에서만 수정 가능) - stages: z.array(z.object({ - id: z.number().optional(), - stageName: z.string().min(1, "스테이지명은 필수입니다"), - stageOrder: z.number(), - priority: z.enum(["HIGH", "MEDIUM", "LOW"]).default("MEDIUM"), - planDate: z.date().optional(), - assigneeName: z.string().optional(), - description: z.string().optional(), - })).optional(), - - // 리비전 업로드 (현재 스테이지에 대한) - newRevision: z.object({ - stage: z.string().optional(), - revision: z.string().optional(), - uploaderType: z.enum(["vendor", "client", "shi"]).default("vendor"), - uploaderName: z.string().optional(), - comment: z.string().optional(), - attachments: z.array(z.instanceof(File)).optional(), - }).optional(), -}) - -type EnhancedDocumentSchema = z.infer<typeof enhancedDocumentSchema> - -// 상태 옵션 정의 -const statusOptions = [ - { value: "ACTIVE", label: "활성" }, - { value: "INACTIVE", label: "비활성" }, - { value: "COMPLETED", label: "완료" }, - { value: "CANCELLED", label: "취소" }, -] - -const priorityOptions = [ - { value: "HIGH", label: "높음" }, - { value: "MEDIUM", label: "보통" }, - { value: "LOW", label: "낮음" }, -] - -const stageStatusOptions = [ - { value: "PLANNED", label: "계획됨" }, - { value: "IN_PROGRESS", label: "진행중" }, - { value: "SUBMITTED", label: "제출됨" }, - { value: "APPROVED", label: "승인됨" }, - { value: "REJECTED", label: "반려됨" }, - { value: "COMPLETED", label: "완료됨" }, -] - -interface EnhancedDocumentSheetProps - extends React.ComponentPropsWithRef<typeof Sheet> { - document: EnhancedDocumentsView | null - projectType: "ship" | "plant" - mode: "view" | "edit" | "upload" | "schedule" | "approve" -} - -export function EnhancedDocumentSheet({ - document, - projectType, - mode = "view", - ...props -}: EnhancedDocumentSheetProps) { - const [isUpdatePending, startUpdateTransition] = React.useTransition() - const [selectedFiles, setSelectedFiles] = React.useState<File[]>([]) - const [uploadProgress, setUploadProgress] = React.useState(0) - const [activeTab, setActiveTab] = React.useState("info") - const router = useRouter() - - // 권한 계산 - const permissions = React.useMemo(() => { - const canEdit = projectType === "plant" || mode === "edit" - const canUpload = mode === "upload" || mode === "edit" - const canApprove = mode === "approve" && projectType === "ship" - const canSchedule = mode === "schedule" || (projectType === "plant" && mode === "edit") - - return { canEdit, canUpload, canApprove, canSchedule } - }, [projectType, mode]) - - const form = useForm<EnhancedDocumentSchema>({ - resolver: zodResolver(enhancedDocumentSchema), - defaultValues: { - docNumber: "", - title: "", - pic: "", - status: "ACTIVE", - issuedDate: undefined, - stages: [], - newRevision: { - stage: "", - revision: "", - uploaderType: "vendor", - uploaderName: "", - comment: "", - attachments: [], - }, - }, - }) - - // 폼 초기화 - React.useEffect(() => { - if (document) { - form.reset({ - docNumber: document.docNumber, - title: document.title, - pic: document.pic || "", - status: document.status, - issuedDate: document.issuedDate ? new Date(document.issuedDate) : undefined, - stages: document.allStages?.map((stage, index) => ({ - id: stage.id, - stageName: stage.stageName, - stageOrder: stage.stageOrder || index, - priority: stage.priority as "HIGH" | "MEDIUM" | "LOW" || "MEDIUM", - planDate: stage.planDate ? new Date(stage.planDate) : undefined, - assigneeName: stage.assigneeName || "", - description: "", - })) || [], - newRevision: { - stage: document.currentStageName || "", - revision: "", - uploaderType: "vendor", - uploaderName: "", - comment: "", - attachments: [], - }, - }) - - // 모드에 따른 기본 탭 설정 - if (mode === "upload") { - setActiveTab("upload") - } else if (mode === "schedule") { - setActiveTab("schedule") - } else if (mode === "approve") { - setActiveTab("approve") - } - } - }, [document, form, mode]) - - // 파일 처리 - const handleDropAccepted = (acceptedFiles: File[]) => { - const newFiles = [...selectedFiles, ...acceptedFiles] - setSelectedFiles(newFiles) - form.setValue('newRevision.attachments', newFiles) - } - - const removeFile = (index: number) => { - const updatedFiles = [...selectedFiles] - updatedFiles.splice(index, 1) - setSelectedFiles(updatedFiles) - form.setValue('newRevision.attachments', updatedFiles) - } - - // 스테이지 추가/제거 - const addStage = () => { - const currentStages = form.getValues("stages") || [] - const newStage = { - stageName: "", - stageOrder: currentStages.length, - priority: "MEDIUM" as const, - planDate: undefined, - assigneeName: "", - description: "", - } - form.setValue("stages", [...currentStages, newStage]) - } - - const removeStage = (index: number) => { - const currentStages = form.getValues("stages") || [] - const updatedStages = currentStages.filter((_, i) => i !== index) - form.setValue("stages", updatedStages) - } - - // 제출 처리 - function onSubmit(input: EnhancedDocumentSchema) { - startUpdateTransition(async () => { - if (!document) return - - try { - // 모드에 따른 다른 처리 - switch (mode) { - case "edit": - // 문서 정보 업데이트 + 스테이지 관리 - await updateDocumentInfo(input) - break - case "upload": - // 리비전 업로드 - await uploadRevision(input) - break - case "approve": - // 승인 처리 - await approveRevision(input) - break - case "schedule": - // 스케줄 관리 - await updateSchedule(input) - break - } - - form.reset() - setSelectedFiles([]) - props.onOpenChange?.(false) - toast.success("성공적으로 처리되었습니다") - router.refresh() - } catch (error) { - toast.error("처리 중 오류가 발생했습니다") - console.error(error) - } - }) - } - - // 개별 처리 함수들 - const updateDocumentInfo = async (input: EnhancedDocumentSchema) => { - // 문서 기본 정보 업데이트 API 호출 - console.log("문서 정보 업데이트:", input) - } - - const uploadRevision = async (input: EnhancedDocumentSchema) => { - if (!input.newRevision?.attachments?.length) { - throw new Error("파일을 선택해주세요") - } - - // 파일 업로드 처리 - const formData = new FormData() - formData.append("documentId", String(document?.documentId)) - formData.append("stage", input.newRevision.stage || "") - formData.append("revision", input.newRevision.revision || "") - formData.append("uploaderType", input.newRevision.uploaderType) - - input.newRevision.attachments.forEach((file) => { - formData.append("attachments", file) - }) - - // API 호출 - console.log("리비전 업로드:", formData) - } - - const approveRevision = async (input: EnhancedDocumentSchema) => { - // 승인 처리 API 호출 - console.log("리비전 승인:", input) - } - - const updateSchedule = async (input: EnhancedDocumentSchema) => { - // 스케줄 업데이트 API 호출 - console.log("스케줄 업데이트:", input) - } - - // 제목 및 설명 생성 - const getSheetTitle = () => { - switch (mode) { - case "edit": return "문서 정보 수정" - case "upload": return "리비전 업로드" - case "approve": return "문서 승인" - case "schedule": return "일정 관리" - default: return "문서 상세" - } - } - - const getSheetDescription = () => { - const docInfo = document ? `${document.docNumber} - ${document.title}` : "" - switch (mode) { - case "edit": return `문서 정보를 수정합니다. ${docInfo}` - case "upload": return `새 리비전을 업로드합니다. ${docInfo}` - case "approve": return `문서를 검토하고 승인 처리합니다. ${docInfo}` - case "schedule": return `문서의 일정을 관리합니다. ${docInfo}` - default: return docInfo - } - } - - return ( - <Sheet {...props}> - <SheetContent className="flex flex-col gap-6 sm:max-w-2xl w-full"> - <SheetHeader className="text-left"> - <SheetTitle className="flex items-center gap-2"> - {mode === "upload" && <Upload className="w-5 h-5" />} - {mode === "approve" && <CheckCircle className="w-5 h-5" />} - {mode === "schedule" && <Calendar className="w-5 h-5" />} - {mode === "edit" && <FileText className="w-5 h-5" />} - {getSheetTitle()} - </SheetTitle> - <SheetDescription> - {getSheetDescription()} - </SheetDescription> - - {/* 프로젝트 타입 및 권한 표시 */} - <div className="flex items-center gap-2 pt-2"> - <Badge variant={projectType === "ship" ? "default" : "secondary"}> - {projectType === "ship" ? "조선 프로젝트" : "플랜트 프로젝트"} - </Badge> - {document?.isOverdue && ( - <Badge variant="destructive" className="flex items-center gap-1"> - <AlertTriangle className="w-3 h-3" /> - 지연 - </Badge> - )} - {document?.currentStagePriority === "HIGH" && ( - <Badge variant="destructive">높은 우선순위</Badge> - )} - </div> - </SheetHeader> - - <Form {...form}> - <form onSubmit={form.handleSubmit(onSubmit)} className="flex-1 flex flex-col"> - <Tabs value={activeTab} onValueChange={setActiveTab} className="flex-1 flex flex-col"> - <TabsList className="grid w-full grid-cols-4"> - <TabsTrigger value="info">기본정보</TabsTrigger> - <TabsTrigger value="schedule" disabled={!permissions.canSchedule}> - 일정관리 - </TabsTrigger> - <TabsTrigger value="upload" disabled={!permissions.canUpload}> - 리비전업로드 - </TabsTrigger> - <TabsTrigger value="approve" disabled={!permissions.canApprove}> - 승인처리 - </TabsTrigger> - </TabsList> - - {/* 기본 정보 탭 */} - <TabsContent value="info" className="flex-1 space-y-4"> - <ScrollArea className="h-full pr-4"> - <div className="space-y-4"> - <FormField - control={form.control} - name="docNumber" - render={({ field }) => ( - <FormItem> - <FormLabel>문서번호</FormLabel> - <FormControl> - <Input {...field} disabled={!permissions.canEdit} /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - - <FormField - control={form.control} - name="title" - render={({ field }) => ( - <FormItem> - <FormLabel>제목</FormLabel> - <FormControl> - <Input {...field} disabled={!permissions.canEdit} /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - - <div className="grid grid-cols-2 gap-4"> - <FormField - control={form.control} - name="pic" - render={({ field }) => ( - <FormItem> - <FormLabel>담당자 (PIC)</FormLabel> - <FormControl> - <Input {...field} disabled={!permissions.canEdit} /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - - <FormField - control={form.control} - name="status" - render={({ field }) => ( - <FormItem> - <FormLabel>상태</FormLabel> - <Select - onValueChange={field.onChange} - value={field.value} - disabled={!permissions.canEdit} - > - <FormControl> - <SelectTrigger> - <SelectValue /> - </SelectTrigger> - </FormControl> - <SelectContent> - {statusOptions.map((option) => ( - <SelectItem key={option.value} value={option.value}> - {option.label} - </SelectItem> - ))} - </SelectContent> - </Select> - <FormMessage /> - </FormItem> - )} - /> - </div> - - <FormField - control={form.control} - name="issuedDate" - render={({ field }) => ( - <FormItem> - <FormLabel>발행일</FormLabel> - <Popover> - <PopoverTrigger asChild> - <FormControl> - <Button - variant="outline" - className={cn( - "w-full pl-3 text-left font-normal", - !field.value && "text-muted-foreground" - )} - disabled={!permissions.canEdit} - > - {field.value ? ( - format(field.value, "yyyy년 MM월 dd일", { locale: ko }) - ) : ( - <span>날짜를 선택하세요</span> - )} - <Calendar className="ml-auto h-4 w-4 opacity-50" /> - </Button> - </FormControl> - </PopoverTrigger> - <PopoverContent className="w-auto p-0" align="start"> - <CalendarComponent - mode="single" - selected={field.value} - onSelect={field.onChange} - disabled={(date) => date > new Date()} - initialFocus - /> - </PopoverContent> - </Popover> - <FormMessage /> - </FormItem> - )} - /> - - {/* 현재 상태 정보 표시 */} - {document && ( - <div className="space-y-3 p-4 bg-gray-50 rounded-lg"> - <h4 className="font-medium flex items-center gap-2"> - <Clock className="w-4 h-4" /> - 현재 진행 상황 - </h4> - <div className="grid grid-cols-2 gap-4 text-sm"> - <div> - <span className="text-gray-500">현재 스테이지:</span> - <p className="font-medium">{document.currentStageName || "-"}</p> - </div> - <div> - <span className="text-gray-500">진행률:</span> - <p className="font-medium">{document.progressPercentage || 0}%</p> - </div> - <div> - <span className="text-gray-500">최신 리비전:</span> - <p className="font-medium">{document.latestRevision || "-"}</p> - </div> - <div> - <span className="text-gray-500">담당자:</span> - <p className="font-medium">{document.currentStageAssigneeName || "-"}</p> - </div> - </div> - </div> - )} - </div> - </ScrollArea> - </TabsContent> - - {/* 일정 관리 탭 */} - <TabsContent value="schedule" className="flex-1 space-y-4"> - <ScrollArea className="h-full pr-4"> - <div className="space-y-4"> - <div className="flex items-center justify-between"> - <h4 className="font-medium">스테이지 일정 관리</h4> - {projectType === "plant" && ( - <Button - type="button" - variant="outline" - size="sm" - onClick={addStage} - className="flex items-center gap-1" - > - <Plus className="w-4 h-4" /> - 스테이지 추가 - </Button> - )} - </div> - - {form.watch("stages")?.map((stage, index) => ( - <div key={index} className="p-4 border rounded-lg space-y-3"> - <div className="flex items-center justify-between"> - <h5 className="font-medium">스테이지 {index + 1}</h5> - {projectType === "plant" && ( - <Button - type="button" - variant="ghost" - size="sm" - onClick={() => removeStage(index)} - > - <X className="w-4 h-4" /> - </Button> - )} - </div> - - <div className="grid grid-cols-2 gap-3"> - <FormField - control={form.control} - name={`stages.${index}.stageName`} - render={({ field }) => ( - <FormItem> - <FormLabel>스테이지명</FormLabel> - <FormControl> - <Input {...field} disabled={projectType === "ship"} /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - - <FormField - control={form.control} - name={`stages.${index}.priority`} - render={({ field }) => ( - <FormItem> - <FormLabel>우선순위</FormLabel> - <Select onValueChange={field.onChange} value={field.value}> - <FormControl> - <SelectTrigger> - <SelectValue /> - </SelectTrigger> - </FormControl> - <SelectContent> - {priorityOptions.map((option) => ( - <SelectItem key={option.value} value={option.value}> - {option.label} - </SelectItem> - ))} - </SelectContent> - </Select> - <FormMessage /> - </FormItem> - )} - /> - - <FormField - control={form.control} - name={`stages.${index}.planDate`} - render={({ field }) => ( - <FormItem> - <FormLabel>계획일</FormLabel> - <Popover> - <PopoverTrigger asChild> - <FormControl> - <Button - variant="outline" - className={cn( - "w-full text-left font-normal", - !field.value && "text-muted-foreground" - )} - > - {field.value ? ( - format(field.value, "MM/dd", { locale: ko }) - ) : ( - <span>날짜 선택</span> - )} - <Calendar className="ml-auto h-4 w-4 opacity-50" /> - </Button> - </FormControl> - </PopoverTrigger> - <PopoverContent className="w-auto p-0" align="start"> - <CalendarComponent - mode="single" - selected={field.value} - onSelect={field.onChange} - initialFocus - /> - </PopoverContent> - </Popover> - <FormMessage /> - </FormItem> - )} - /> - - <FormField - control={form.control} - name={`stages.${index}.assigneeName`} - render={({ field }) => ( - <FormItem> - <FormLabel>담당자</FormLabel> - <FormControl> - <Input {...field} /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - </div> - </div> - ))} - </div> - </ScrollArea> - </TabsContent> - - {/* 리비전 업로드 탭 */} - <TabsContent value="upload" className="flex-1 space-y-4"> - <ScrollArea className="h-full pr-4"> - <div className="space-y-4"> - <div className="grid grid-cols-2 gap-4"> - <FormField - control={form.control} - name="newRevision.stage" - render={({ field }) => ( - <FormItem> - <FormLabel>스테이지</FormLabel> - <FormControl> - <Input {...field} placeholder="예: Issued for Review" /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - - <FormField - control={form.control} - name="newRevision.revision" - render={({ field }) => ( - <FormItem> - <FormLabel>리비전</FormLabel> - <FormControl> - <Input {...field} placeholder="예: A, B, 1, 2..." /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - </div> - - <FormField - control={form.control} - name="newRevision.uploaderName" - render={({ field }) => ( - <FormItem> - <FormLabel>업로더명 (선택)</FormLabel> - <FormControl> - <Input {...field} placeholder="업로더 이름을 입력하세요" /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - - <FormField - control={form.control} - name="newRevision.comment" - render={({ field }) => ( - <FormItem> - <FormLabel>코멘트 (선택)</FormLabel> - <FormControl> - <Textarea {...field} placeholder="코멘트를 입력하세요" rows={3} /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - - {/* 파일 업로드 드롭존 */} - <FormField - control={form.control} - name="newRevision.attachments" - render={() => ( - <FormItem> - <FormLabel>파일 첨부</FormLabel> - <Dropzone - maxSize={3e9} // 3GB - multiple={true} - onDropAccepted={handleDropAccepted} - disabled={isUpdatePending} - > - <DropzoneZone className="flex justify-center"> - <FormControl> - <DropzoneInput /> - </FormControl> - <div className="flex items-center gap-6"> - <DropzoneUploadIcon /> - <div className="grid gap-0.5"> - <DropzoneTitle>파일을 여기에 드롭하세요</DropzoneTitle> - <DropzoneDescription> - 또는 클릭하여 파일을 선택하세요 - </DropzoneDescription> - </div> - </div> - </DropzoneZone> - </Dropzone> - <FormMessage /> - </FormItem> - )} - /> - - {/* 선택된 파일 목록 */} - {selectedFiles.length > 0 && ( - <div className="space-y-2"> - <div className="flex items-center justify-between"> - <h6 className="text-sm font-semibold"> - 선택된 파일 ({selectedFiles.length}) - </h6> - </div> - <FileList className="max-h-[200px]"> - {selectedFiles.map((file, index) => ( - <FileListItem key={index} className="p-3"> - <FileListHeader> - <FileListIcon /> - <FileListInfo> - <FileListName>{file.name}</FileListName> - <FileListDescription> - {prettyBytes(file.size)} - </FileListDescription> - </FileListInfo> - <FileListAction - onClick={() => removeFile(index)} - disabled={isUpdatePending} - > - <X className="h-4 w-4" /> - </FileListAction> - </FileListHeader> - </FileListItem> - ))} - </FileList> - </div> - )} - - {/* 업로드 진행 상태 */} - {isUpdatePending && uploadProgress > 0 && ( - <div className="space-y-2"> - <div className="flex items-center gap-2"> - <Loader className="h-4 w-4 animate-spin" /> - <span className="text-sm">{uploadProgress}% 업로드 중...</span> - </div> - <div className="h-2 w-full bg-muted rounded-full overflow-hidden"> - <div - className="h-full bg-primary rounded-full transition-all" - style={{ width: `${uploadProgress}%` }} - /> - </div> - </div> - )} - </div> - </ScrollArea> - </TabsContent> - - {/* 승인 처리 탭 */} - <TabsContent value="approve" className="flex-1 space-y-4"> - <ScrollArea className="h-full pr-4"> - <div className="space-y-4"> - <div className="p-4 bg-blue-50 rounded-lg"> - <h4 className="font-medium mb-2 flex items-center gap-2"> - <CheckCircle className="w-4 h-4 text-blue-600" /> - 승인 대상 문서 - </h4> - <div className="text-sm space-y-1"> - <p><span className="font-medium">문서:</span> {document?.docNumber} - {document?.title}</p> - <p><span className="font-medium">현재 스테이지:</span> {document?.currentStageName}</p> - <p><span className="font-medium">최신 리비전:</span> {document?.latestRevision}</p> - <p><span className="font-medium">업로더:</span> {document?.latestRevisionUploaderName}</p> - </div> - </div> - - <div className="space-y-3"> - <div className="flex gap-3"> - <Button - type="button" - className="flex-1 bg-green-600 hover:bg-green-700" - onClick={() => { - // 승인 처리 로직 - console.log("승인 처리") - }} - > - <CheckCircle className="w-4 h-4 mr-2" /> - 승인 - </Button> - <Button - type="button" - variant="destructive" - className="flex-1" - onClick={() => { - // 반려 처리 로직 - console.log("반려 처리") - }} - > - <X className="w-4 h-4 mr-2" /> - 반려 - </Button> - </div> - - <FormField - control={form.control} - name="newRevision.comment" - render={({ field }) => ( - <FormItem> - <FormLabel>검토 의견</FormLabel> - <FormControl> - <Textarea - {...field} - placeholder="승인/반려 사유를 입력하세요" - rows={4} - /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - </div> - </div> - </ScrollArea> - </TabsContent> - </Tabs> - - <Separator /> - - <SheetFooter className="gap-2 pt-4"> - <SheetClose asChild> - <Button type="button" variant="outline"> - 취소 - </Button> - </SheetClose> - <Button - type="submit" - disabled={isUpdatePending} - className={mode === "approve" ? "bg-green-600 hover:bg-green-700" : ""} - > - {isUpdatePending && <Loader className="mr-2 size-4 animate-spin" />} - {mode === "upload" && <Upload className="mr-2 size-4" />} - {mode === "approve" && <CheckCircle className="mr-2 size-4" />} - {mode === "schedule" && <Calendar className="mr-2 size-4" />} - {mode === "edit" && <Save className="mr-2 size-4" />} - - {mode === "upload" ? "업로드" : - mode === "approve" ? "승인 처리" : - mode === "schedule" ? "일정 저장" : "저장"} - </Button> - </SheetFooter> - </form> - </Form> - </SheetContent> - </Sheet> - ) -}
\ No newline at end of file |
