diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-09-14 05:28:01 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-09-14 05:28:01 +0000 |
| commit | 675b4e3d8ffcb57a041db285417d81e61284d900 (patch) | |
| tree | 254f3d6a6c0ce39ae8fba35618f3810e08945f19 /lib/rfq-last/due-date-edit-button.tsx | |
| parent | 39f12cb19f29cbc5568057e154e6adf4789ae736 (diff) | |
(대표님) RFQ-last, tbe-last, 기본계약 템플릿 내 견적,입찰,계약 추가, env.dev NAS_PATH 수정
Diffstat (limited to 'lib/rfq-last/due-date-edit-button.tsx')
| -rw-r--r-- | lib/rfq-last/due-date-edit-button.tsx | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/lib/rfq-last/due-date-edit-button.tsx b/lib/rfq-last/due-date-edit-button.tsx new file mode 100644 index 00000000..85a18a63 --- /dev/null +++ b/lib/rfq-last/due-date-edit-button.tsx @@ -0,0 +1,216 @@ +"use client" + +import { useState } from "react" +import { format } from "date-fns" +import { ko } from "date-fns/locale" +import { Calendar as CalendarIcon, Clock, Edit2 } from "lucide-react" +import { Button } from "@/components/ui/button" +import { Calendar } from "@/components/ui/calendar" +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog" +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover" +import { cn } from "@/lib/utils" +import { useToast } from "@/hooks/use-toast" +import { useRouter } from "next/navigation" +import { updateRfqDueDate } from "./service" + +interface DueDateEditButtonProps { + rfqId: number + currentDueDate: Date | string | null + rfqCode: string + rfqTitle: string +} + +export function DueDateEditButton({ + rfqId, + currentDueDate, + rfqCode, + rfqTitle + }: DueDateEditButtonProps) { + const [open, setOpen] = useState(false) + const [date, setDate] = useState<Date | undefined>( + currentDueDate ? new Date(currentDueDate) : undefined + ) + const [time, setTime] = useState<string>( + currentDueDate + ? format(new Date(currentDueDate), "HH:mm") + : "17:00" // 기본값: 오후 5시 + ) + const [isLoading, setIsLoading] = useState(false) + const { toast } = useToast() + const router = useRouter() + + const handleSave = async () => { + if (!date) { + toast({ + title: "오류", + description: "마감일을 선택해주세요.", + variant: "destructive", + }) + return + } + + setIsLoading(true) + try { + // 날짜와 시간 결합 + const [hours, minutes] = time.split(':').map(Number) + const dateTime = new Date( + date.getFullYear(), + date.getMonth(), + date.getDate(), + hours, + minutes, + 0, + 0 + ) + + // ISO 문자열로 전송 (자동으로 로컬 타임존 포함) + const result = await updateRfqDueDate( + rfqId, + dateTime.toISOString(), + rfqCode, + rfqTitle + ) + + if (result.success) { + toast({ + title: "성공", + description: result.message, + }) + setOpen(false) + router.refresh() + } else { + toast({ + title: "오류", + description: result.message, + variant: "destructive", + }) + } + } catch (error) { + toast({ + title: "오류", + description: "마감일 수정 중 오류가 발생했습니다.", + variant: "destructive", + }) + } finally { + setIsLoading(false) + } + } + + return ( + <Dialog open={open} onOpenChange={setOpen}> + <DialogTrigger asChild> + <Button + variant="outline" + size="sm" + className="h-7 px-2" + > + <Edit2 className="h-3 w-3 mr-1" /> + 수정 + </Button> + </DialogTrigger> + <DialogContent className="sm:max-w-[425px]"> + <DialogHeader> + <DialogTitle>마감일 수정</DialogTitle> + <DialogDescription> + {rfqCode} {rfqTitle ? `- ${rfqTitle}` : ''}의 마감일을 수정합니다. + 변경 시 관련 업체에 이메일이 발송됩니다. + </DialogDescription> + </DialogHeader> + <div className="grid gap-4 py-4"> + {/* 날짜 선택 */} + <div className="grid gap-2"> + <label htmlFor="dueDate" className="text-sm font-medium"> + 마감 날짜 + </label> + <Popover> + <PopoverTrigger asChild> + <Button + id="dueDate" + variant="outline" + className={cn( + "w-full justify-start text-left font-normal", + !date && "text-muted-foreground" + )} + > + <CalendarIcon className="mr-2 h-4 w-4" /> + {date ? format(date, "yyyy년 MM월 dd일", { locale: ko }) : "날짜를 선택하세요"} + </Button> + </PopoverTrigger> + <PopoverContent className="w-auto p-0" align="start"> + <Calendar + mode="single" + selected={date} + onSelect={setDate} + initialFocus + locale={ko} + disabled={(date) => date < new Date(new Date().setHours(0, 0, 0, 0))} + /> + </PopoverContent> + </Popover> + </div> + + {/* 시간 선택 */} + <div className="grid gap-2"> + <label htmlFor="time" className="text-sm font-medium"> + 마감 시간 + </label> + <div className="flex items-center gap-2"> + <Clock className="h-4 w-4 text-muted-foreground" /> + <input + id="time" + type="time" + value={time} + onChange={(e) => setTime(e.target.value)} + className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm" + /> + </div> + </div> + + {/* 현재 마감일시 표시 */} + {currentDueDate && ( + <div className="text-sm text-muted-foreground"> + 현재 마감일시: {format(new Date(currentDueDate), "yyyy년 MM월 dd일 HH:mm", { locale: ko })} + </div> + )} + + {/* 선택한 날짜시간 미리보기 */} + {date && ( + <div className="rounded-md bg-muted p-3"> + <p className="text-sm font-medium">선택한 마감일시:</p> + <p className="text-sm text-muted-foreground"> + {format(date, "yyyy년 MM월 dd일", { locale: ko })} {time} + </p> + </div> + )} + </div> + <DialogFooter> + <Button + variant="outline" + onClick={() => setOpen(false)} + disabled={isLoading} + > + 취소 + </Button> + <Button + onClick={handleSave} + disabled={isLoading} + > + {isLoading ? "저장 중..." : "저장"} + </Button> + </DialogFooter> + </DialogContent> + </Dialog> + ) + }
\ No newline at end of file |
