summaryrefslogtreecommitdiff
path: root/components/notice/notice-edit-sheet.tsx
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-07-01 11:46:33 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-07-01 11:46:33 +0000
commit795b4915069c44f500a91638e16ded67b9e16618 (patch)
tree6306adceb723a08391af6f968fee25ef4f66446a /components/notice/notice-edit-sheet.tsx
parenta382208003044caa45bb1cecb67dade544d44ada (diff)
(최겸) 정보시스템 공지사항 개발
Diffstat (limited to 'components/notice/notice-edit-sheet.tsx')
-rw-r--r--components/notice/notice-edit-sheet.tsx246
1 files changed, 246 insertions, 0 deletions
diff --git a/components/notice/notice-edit-sheet.tsx b/components/notice/notice-edit-sheet.tsx
new file mode 100644
index 00000000..91bcae3b
--- /dev/null
+++ b/components/notice/notice-edit-sheet.tsx
@@ -0,0 +1,246 @@
+"use client"
+
+import * as React from "react"
+import { zodResolver } from "@hookform/resolvers/zod"
+import { useForm } from "react-hook-form"
+import { toast } from "sonner"
+
+import { Button } from "@/components/ui/button"
+import {
+ Form,
+ FormControl,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from "@/components/ui/form"
+import { Input } from "@/components/ui/input"
+import {
+ Sheet,
+ SheetContent,
+ SheetDescription,
+ SheetFooter,
+ SheetHeader,
+ SheetTitle,
+} from "@/components/ui/sheet"
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select"
+import { Switch } from "@/components/ui/switch"
+
+import { updateNoticeSchema, type UpdateNoticeSchema } from "@/lib/notice/validations"
+import type { Notice } from "@/db/schema/notice"
+import { updateNoticeData } from "@/lib/notice/service"
+import TiptapEditor from "@/components/qna/tiptap-editor"
+
+type NoticeWithAuthor = Notice & {
+ authorName: string | null
+ authorEmail: string | null
+}
+
+interface UpdateNoticeSheetProps {
+ open: boolean
+ onOpenChange: (open: boolean) => void
+ notice: NoticeWithAuthor | null
+ pagePathOptions: Array<{ value: string; label: string }>
+ onSuccess?: () => void
+}
+
+export function UpdateNoticeSheet({
+ open,
+ onOpenChange,
+ notice,
+ pagePathOptions,
+ onSuccess
+}: UpdateNoticeSheetProps) {
+ const [isUpdatePending, startUpdateTransition] = React.useTransition()
+
+ const form = useForm<UpdateNoticeSchema>({
+ resolver: zodResolver(updateNoticeSchema),
+ defaultValues: {
+ id: 0,
+ pagePath: "",
+ title: "",
+ content: "",
+ isActive: true,
+ },
+ })
+
+ // notice 데이터가 변경될 때 폼 초기화
+ React.useEffect(() => {
+ if (notice) {
+ form.reset({
+ id: notice.id,
+ pagePath: notice.pagePath,
+ title: notice.title,
+ content: notice.content,
+ isActive: notice.isActive,
+ })
+ }
+ }, [notice, form])
+
+ function onSubmit(input: UpdateNoticeSchema) {
+ if (!notice) return
+
+ startUpdateTransition(async () => {
+ try {
+ const result = await updateNoticeData(input)
+
+ if (result.success) {
+ toast.success(result.message || "공지사항이 성공적으로 수정되었습니다.")
+ if (onSuccess) onSuccess()
+ onOpenChange(false)
+ } else {
+ toast.error(result.message || "공지사항 수정에 실패했습니다.")
+ }
+ } catch (error) {
+ toast.error("예기치 못한 오류가 발생했습니다.")
+ console.error("공지사항 수정 오류:", error)
+ }
+ })
+ }
+
+ return (
+ <Sheet open={open} onOpenChange={onOpenChange}>
+ <SheetContent className="flex h-full w-full flex-col gap-6 sm:max-w-4xl">
+ <SheetHeader className="text-left">
+ <SheetTitle>공지사항 수정</SheetTitle>
+ <SheetDescription>
+ 공지사항의 제목과 내용을 수정할 수 있습니다. 수정된 내용은 즉시 반영됩니다.
+ </SheetDescription>
+ </SheetHeader>
+
+ <Form {...form}>
+ <form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col flex-1 min-h-0 space-y-6">
+ {/* 공지사항 정보 표시 */}
+ {notice && (
+ <div className="rounded-md border border-muted bg-muted/50 p-3 text-sm">
+ <div className="font-medium text-muted-foreground mb-1">공지사항 정보</div>
+ <div className="space-y-1">
+ <div>작성자: {notice.authorName} ({notice.authorEmail})</div>
+ <div>작성일: {new Date(notice.createdAt).toLocaleDateString("ko-KR")}</div>
+ <div>수정일: {new Date(notice.updatedAt).toLocaleDateString("ko-KR")}</div>
+ <div>상태: {notice.isActive ? "활성" : "비활성"}</div>
+ </div>
+ </div>
+ )}
+
+ {/* 페이지 경로 선택 */}
+ <FormField
+ control={form.control}
+ name="pagePath"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>페이지 경로 *</FormLabel>
+ <Select
+ onValueChange={field.onChange}
+ defaultValue={field.value}
+ disabled={isUpdatePending}
+ >
+ <FormControl>
+ <SelectTrigger>
+ <SelectValue placeholder="페이지를 선택해주세요" />
+ </SelectTrigger>
+ </FormControl>
+ <SelectContent>
+ {pagePathOptions.map((option) => (
+ <SelectItem key={option.value} value={option.value}>
+ {option.label}
+ </SelectItem>
+ ))}
+ </SelectContent>
+ </Select>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ {/* 제목 입력 */}
+ <FormField
+ control={form.control}
+ name="title"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>제목 *</FormLabel>
+ <FormControl>
+ <Input
+ placeholder="공지사항의 제목을 입력해주세요"
+ disabled={isUpdatePending}
+ className="text-base"
+ {...field}
+ />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ {/* 활성 상태 */}
+ <FormField
+ control={form.control}
+ name="isActive"
+ render={({ field }) => (
+ <FormItem className="flex flex-row items-center justify-between rounded-lg border p-3">
+ <div className="space-y-0.5">
+ <FormLabel className="text-base">활성 상태</FormLabel>
+ <div className="text-sm text-muted-foreground">
+ 활성화하면 해당 페이지에서 공지사항이 표시됩니다.
+ </div>
+ </div>
+ <FormControl>
+ <Switch
+ checked={field.value}
+ onCheckedChange={field.onChange}
+ disabled={isUpdatePending}
+ />
+ </FormControl>
+ </FormItem>
+ )}
+ />
+
+ {/* 내용 입력 (리치텍스트 에디터) */}
+ <FormField
+ control={form.control}
+ name="content"
+ render={({ field }) => (
+ <FormItem className="flex flex-col flex-1 min-h-0">
+ <FormLabel>내용 *</FormLabel>
+ <FormControl className="flex flex-col flex-1 min-h-0">
+ <TiptapEditor
+ content={field.value}
+ setContent={field.onChange}
+ disabled={isUpdatePending}
+ />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ </form>
+ </Form>
+
+ <SheetFooter className="gap-2 pt-4 border-t">
+ <Button
+ type="button"
+ variant="outline"
+ onClick={() => onOpenChange(false)}
+ disabled={isUpdatePending}
+ >
+ 취소
+ </Button>
+ <Button
+ type="submit"
+ onClick={form.handleSubmit(onSubmit)}
+ disabled={isUpdatePending}
+ >
+ {isUpdatePending ? "수정 중..." : "수정 완료"}
+ </Button>
+ </SheetFooter>
+ </SheetContent>
+ </Sheet>
+ )
+} \ No newline at end of file