"use client" import * as React from "react" import { useState } from "react" import { zodResolver } from "@hookform/resolvers/zod" import { useForm } from "react-hook-form" import { useParams } from "next/navigation" import { useTranslation } from "@/i18n/client" import { toast } from "sonner" import { Loader, Check, ChevronsUpDown } from "lucide-react" import { Button } from "@/components/ui/button" import { Dialog, DialogContent, DialogHeader, DialogTitle, } from "@/components/ui/dialog" import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form" import { Input } from "@/components/ui/input" import { Switch } from "@/components/ui/switch" import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover" import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem } from "@/components/ui/command" import { cn } from "@/lib/utils" import TiptapEditor from "@/components/qna/tiptap-editor" import { createNotice } from "@/lib/notice/service" import { createNoticeSchema, type CreateNoticeSchema } from "@/lib/notice/validations" import { Calendar } from "@/components/ui/calendar" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { CalendarIcon } from "lucide-react" import { format } from "date-fns" import { ko } from "date-fns/locale" interface NoticeCreateDialogProps { open: boolean onOpenChange: (open: boolean) => void pagePathOptions: Array<{ value: string; label: string }> currentUserId?: number onSuccess?: () => void } export function NoticeCreateDialog({ open, onOpenChange, pagePathOptions, currentUserId, onSuccess, }: NoticeCreateDialogProps) { const params = useParams() const lng = (params?.lng as string) || 'ko' const { t } = useTranslation(lng, 'menu') // 안전한 번역 함수 (키가 없을 때 원본 키 반환) const safeTranslate = (key: string): string => { try { const translated = t(key) // 번역 키가 그대로 반환되는 경우 원본 키 사용 if (translated === key) { return key } return translated || key } catch (error) { console.warn(`Translation failed for key: ${key}`, error) return key } } const [isLoading, setIsLoading] = useState(false) const form = useForm({ resolver: zodResolver(createNoticeSchema), defaultValues: { pagePath: "", title: "", content: "", authorId: currentUserId, isActive: true, isPopup: false, startAt: undefined, endAt: undefined, dontShowDuration: "never", // 기본값을 영구로 설정 }, }) React.useEffect(() => { if (open) { // 다이얼로그가 열릴 때마다 폼 초기화 form.reset({ pagePath: "", title: "", content: "", authorId: currentUserId, isActive: true, isPopup: false, startAt: undefined, endAt: undefined, dontShowDuration: "never", // 기본값을 영구로 설정 }) } }, [open, currentUserId, form]) const onSubmit = async (values: CreateNoticeSchema) => { setIsLoading(true) console.log("Form values:", values) // 디버깅용 try { const result = await createNotice(values) console.log("Create result:", result) // 디버깅용 if (result.success) { toast.success(result.message || "공지사항이 성공적으로 생성되었습니다.") if (onSuccess) onSuccess() onOpenChange(false) } else { toast.error(result.message || "공지사항 생성에 실패했습니다.") console.error("Create failed:", result.message) } } catch (error) { toast.error("공지사항 생성에 실패했습니다.") console.error("Create error:", error) } finally { setIsLoading(false) } } return ( 새 공지사항 작성
{ const [open, setOpen] = useState(false) const [searchTerm, setSearchTerm] = useState("") const filteredOptions = React.useMemo(() => { if (!searchTerm.trim()) return pagePathOptions const lowerSearch = searchTerm.toLowerCase() return pagePathOptions.filter( (option) => safeTranslate(option.label).toLowerCase().includes(lowerSearch) || option.value.toLowerCase().includes(lowerSearch) ) }, [pagePathOptions, searchTerm]) const selectedOption = pagePathOptions.find(option => option.value === field.value) return ( 페이지 경로 * 검색 결과가 없습니다 {filteredOptions.map((option) => ( { field.onChange(option.value) setOpen(false) }} > {safeTranslate(option.label)} - {option.value} ))} ) }} /> (
활성 상태
활성화하면 해당 페이지에서 공지사항이 표시됩니다.
)} />
{/* 팝업 여부 */} (
팝업 공지사항
팝업으로 표시할 공지사항인 경우 체크하세요.
)} /> ( 제목 * )} /> {/* 유효기간 설정 (팝업인 경우에만 표시) */} {form.watch('isPopup') && (
( 게시 시작 일시 * { if (date) { // 시간을 보존하거나 설정 const dateTime = new Date(date) if (field.value) { dateTime.setHours(field.value.getHours()) dateTime.setMinutes(field.value.getMinutes()) } else { dateTime.setHours(0, 0, 0, 0) } field.onChange(dateTime) } }} disabled={(date) => date < new Date("1900-01-01") } initialFocus />
{ if (field.value && e.target.value) { const [hours, minutes] = e.target.value.split(':') const newDate = new Date(field.value) newDate.setHours(parseInt(hours), parseInt(minutes)) field.onChange(newDate) } }} />
)} /> ( 게시 종료 일시 * { if (date) { const dateTime = new Date(date) if (field.value) { dateTime.setHours(field.value.getHours()) dateTime.setMinutes(field.value.getMinutes()) } else { dateTime.setHours(23, 59, 59, 999) } field.onChange(dateTime) } }} disabled={(date) => date < new Date("1900-01-01") } initialFocus />
{ if (field.value && e.target.value) { const [hours, minutes] = e.target.value.split(':') const newDate = new Date(field.value) newDate.setHours(parseInt(hours), parseInt(minutes)) field.onChange(newDate) } }} />
)} />
)} {/* 다시 보지 않기 설정 (팝업인 경우에만 표시) - 임시 주석처리 */} {/* {form.watch('isPopup') && ( ( '다시 보지 않기' 기간 설정 *
사용자가 '다시 보지 않기'를 체크하면 설정한 기간 동안 해당 공지사항이 표시되지 않습니다.
)} /> )} */} ( 내용 *
)} />
) }