summaryrefslogtreecommitdiff
path: root/lib/qna/table/create-qna-dialog.tsx
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-07-02 00:45:49 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-07-02 00:45:49 +0000
commit2acf5f8966a40c1c9a97680c8dc263ee3f1ad3d1 (patch)
treef406b5c86f563347c7fd088a85fd1a82284dc5ff /lib/qna/table/create-qna-dialog.tsx
parent6a9ca20deddcdcbe8495cf5a73ec7ea5f53f9b55 (diff)
(대표님/최겸) 20250702 변경사항 업데이트
Diffstat (limited to 'lib/qna/table/create-qna-dialog.tsx')
-rw-r--r--lib/qna/table/create-qna-dialog.tsx203
1 files changed, 203 insertions, 0 deletions
diff --git a/lib/qna/table/create-qna-dialog.tsx b/lib/qna/table/create-qna-dialog.tsx
new file mode 100644
index 00000000..d5af932b
--- /dev/null
+++ b/lib/qna/table/create-qna-dialog.tsx
@@ -0,0 +1,203 @@
+"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 {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from "@/components/ui/dialog"
+import {
+ Form,
+ FormControl,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from "@/components/ui/form"
+import { Input } from "@/components/ui/input"
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select"
+
+import { createQnaSchema, type CreateQnaSchema } from "@/lib/qna/validation"
+import { createQna } from "../service"
+import { QNA_CATEGORY_LABELS } from "@/db/schema"
+import TiptapEditor from "@/components/qna/tiptap-editor"
+
+interface CreateQnaDialogProps {
+ open: boolean
+ onOpenChange: (open: boolean) => void
+}
+
+export function CreateQnaDialog({ open, onOpenChange }: CreateQnaDialogProps) {
+ const [isCreatePending, startCreateTransition] = React.useTransition()
+
+ const form = useForm<CreateQnaSchema>({
+ resolver: zodResolver(createQnaSchema),
+ defaultValues: {
+ title: "",
+ content: "",
+ category: undefined,
+ },
+ })
+
+ function onSubmit(input: CreateQnaSchema) {
+ startCreateTransition(async () => {
+ try {
+ const result = await createQna(input)
+
+ if (result.success) {
+ toast.success(result.message || "질문이 성공적으로 등록되었습니다.")
+ form.reset()
+ onOpenChange(false)
+ } else {
+ toast.error(result.error || "질문 등록에 실패했습니다.")
+ }
+ } catch (error) {
+ toast.error("예기치 못한 오류가 발생했습니다.")
+ console.error("질문 생성 오류:", error)
+ }
+ })
+ }
+
+ // 다이얼로그가 닫힐 때 폼 리셋
+ React.useEffect(() => {
+ if (!open) {
+ form.reset()
+ }
+ }, [open, form])
+
+
+ console.log(form.getValues(),"생성")
+
+ return (
+ <Dialog open={open} onOpenChange={onOpenChange}>
+ <DialogContent className="max-w-6xl max-h-[90vh] flex flex-col">
+ <DialogHeader className="flex-shrink-0">
+ <DialogTitle>새 질문 작성</DialogTitle>
+ <DialogDescription>
+ 질문의 제목과 내용을 입력해주세요. 다른 사용자들이 이해하기 쉽도록 구체적으로 작성해주세요.
+ </DialogDescription>
+ </DialogHeader>
+
+ {/* 폼 영역: flex-1으로 남은 공간 모두 사용 */}
+ <div className="flex-1 overflow-hidden">
+ <Form {...form}>
+ <form onSubmit={form.handleSubmit(onSubmit)} className="h-full flex flex-col space-y-4">
+ {/* 카테고리와 제목은 스크롤 없이 고정 */}
+ <div className="flex-shrink-0 space-y-4">
+ {/* 카테고리 선택 */}
+ <FormField
+ control={form.control}
+ name="category"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>카테고리 *</FormLabel>
+ <Select
+ onValueChange={field.onChange}
+ defaultValue={field.value}
+ disabled={isCreatePending}
+ >
+ <FormControl>
+ <SelectTrigger>
+ <SelectValue placeholder="카테고리를 선택해주세요" />
+ </SelectTrigger>
+ </FormControl>
+ <SelectContent>
+ {QNA_CATEGORY_LABELS.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={isCreatePending}
+ className="text-base"
+ {...field}
+ />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ </div>
+
+ {/* 내용 입력 영역: 고정 높이로 스크롤 생성 */}
+ <FormField
+ control={form.control}
+ name="content"
+ render={({ field }) => (
+ <FormItem className="flex-1 flex flex-col min-h-0">
+ <FormLabel className="flex-shrink-0">내용 *</FormLabel>
+ <FormControl className="flex-1 min-h-0">
+ {/* 고정 높이 400px로 설정하여 스크롤 보장 */}
+ <div className="h-[400px]">
+ <TiptapEditor
+ content={field.value}
+ setContent={field.onChange}
+ disabled={isCreatePending}
+ height="400px"
+ />
+ </div>
+ </FormControl>
+ <FormMessage className="flex-shrink-0" />
+ <div className="text-sm text-muted-foreground flex-shrink-0 mt-2">
+ • 문제 상황을 구체적으로 설명해주세요<br/>
+ • 이미지 복사&붙여넣기, 드래그&드롭 지원<br/>
+ • 예상하는 결과와 실제 결과를 명시해주세요
+ </div>
+ </FormItem>
+ )}
+ />
+ </form>
+ </Form>
+ </div>
+
+ <DialogFooter className="gap-2 pt-4 border-t flex-shrink-0">
+ <Button
+ type="button"
+ variant="outline"
+ onClick={() => onOpenChange(false)}
+ disabled={isCreatePending}
+ >
+ 취소
+ </Button>
+ <Button
+ type="submit"
+ onClick={form.handleSubmit(onSubmit)}
+ disabled={isCreatePending}
+ >
+ {isCreatePending ? "등록 중..." : "질문 등록"}
+ </Button>
+ </DialogFooter>
+ </DialogContent>
+ </Dialog>
+ )
+} \ No newline at end of file