diff options
| author | 0-Zz-ang <s1998319@gmail.com> | 2025-11-13 11:36:15 +0900 |
|---|---|---|
| committer | 0-Zz-ang <s1998319@gmail.com> | 2025-11-13 11:36:15 +0900 |
| commit | a4ceade24d28af0bde985bf750017efc02f053ff (patch) | |
| tree | 45b03d3e6b5d36a60bea464e37155f0a18e73153 /lib/compliance/questions | |
| parent | 1cdbf77cabcead1a7f76379d3786ede6f43355fb (diff) | |
(박서영)준법설문조사 RedFlag관련 요청사항 반영
Diffstat (limited to 'lib/compliance/questions')
| -rw-r--r-- | lib/compliance/questions/compliance-question-create-dialog.tsx | 114 | ||||
| -rw-r--r-- | lib/compliance/questions/compliance-questions-draggable-list.tsx | 7 |
2 files changed, 97 insertions, 24 deletions
diff --git a/lib/compliance/questions/compliance-question-create-dialog.tsx b/lib/compliance/questions/compliance-question-create-dialog.tsx index b05c2e0d..4abf1eb2 100644 --- a/lib/compliance/questions/compliance-question-create-dialog.tsx +++ b/lib/compliance/questions/compliance-question-create-dialog.tsx @@ -40,12 +40,19 @@ import { QUESTION_TYPES } from "@/db/schema/compliance"; import { toast } from "sonner"; import { useRouter } from "next/navigation"; +type OptionItem = { optionValue: string; optionText: string; allowsOtherInput: boolean; displayOrder: number }; +const RED_FLAG_OPTIONS: OptionItem[] = [ + { optionValue: "YES", optionText: "YES", allowsOtherInput: false, displayOrder: 1 }, + { optionValue: "NO", optionText: "NO", allowsOtherInput: false, displayOrder: 2 }, +]; + const questionSchema = z.object({ questionNumber: z.string().min(1, "질문 번호를 입력하세요"), questionText: z.string().min(1, "질문 내용을 입력하세요"), questionType: z.string().min(1, "질문 유형을 선택하세요"), isRequired: z.boolean(), isConditional: z.boolean(), + isRedFlag: z.boolean(), hasDetailText: z.boolean(), hasFileUpload: z.boolean(), conditionalValue: z.string().optional(), @@ -80,6 +87,7 @@ export function ComplianceQuestionCreateDialog({ questionType: "", isRequired: false, isConditional: false, + isRedFlag: false, hasDetailText: false, hasFileUpload: false, conditionalValue: "", @@ -98,11 +106,32 @@ export function ComplianceQuestionCreateDialog({ const [newOptionOther, setNewOptionOther] = React.useState(false); const [showOptionForm, setShowOptionForm] = React.useState(false); + const isRedFlag = form.watch("isRedFlag"); + const isRequired = form.watch("isRequired"); + const isConditional = form.watch("isConditional"); + const questionTypeValue = form.watch("questionType"); + // 선택형 질문인지 확인 const isSelectionType = React.useMemo(() => { - const questionType = form.watch("questionType"); - return [QUESTION_TYPES.RADIO, QUESTION_TYPES.CHECKBOX, QUESTION_TYPES.DROPDOWN].includes((questionType || "").toUpperCase() as any); - }, [form.watch("questionType")]); + return [QUESTION_TYPES.RADIO, QUESTION_TYPES.CHECKBOX, QUESTION_TYPES.DROPDOWN].includes((questionTypeValue || "").toUpperCase() as any); + }, [questionTypeValue]); + + // 레드플래그 선택 시 질문 유형을 RADIO로 자동 설정 + React.useEffect(() => { + if (isRedFlag) { + form.setValue("questionType", QUESTION_TYPES.RADIO); + } + }, [form, isRedFlag]); + + // 레드플래그 선택 시 옵션을 YES/NO로 고정 + React.useEffect(() => { + if (isRedFlag) { + setOptions(RED_FLAG_OPTIONS.map((option) => ({ ...option }))); + setShowOptionForm(false); + } else { + setOptions([]); + } + }, [isRedFlag]); // 시트/다이얼로그 열릴 때 부모 후보 로드 (같은 템플릿 내 선택형 질문만) React.useEffect(() => { @@ -283,6 +312,28 @@ export function ComplianceQuestionCreateDialog({ )} /> </div> + + {/* 레드플래그 체크박스 */} + <FormField + control={form.control} + name="isRedFlag" + render={({ field }) => ( + <FormItem className="flex flex-row items-start space-x-3 space-y-0 rounded-md border p-4 bg-red-50"> + <FormControl> + <Checkbox + checked={field.value} + onCheckedChange={field.onChange} + /> + </FormControl> + <div className="space-y-1 leading-none"> + <FormLabel className="text-red-700">레드플래그 질문</FormLabel> + <FormDescription> + 질문 유형 - RADIO || 옵션 - YES/NO + </FormDescription> + </div> + </FormItem> + )} + /> {/* 유효성 검사 에러 메시지 */} {(form.formState.errors.isRequired || form.formState.errors.isConditional) && ( @@ -301,7 +352,7 @@ export function ComplianceQuestionCreateDialog({ <Textarea placeholder="질문 내용을 입력하세요" className="min-h-[100px]" - disabled={!form.watch("isRequired") && !form.watch("isConditional")} + disabled={!isRequired && !isConditional} {...field} /> </FormControl> @@ -316,7 +367,11 @@ export function ComplianceQuestionCreateDialog({ render={({ field }) => ( <FormItem> <FormLabel>질문 유형</FormLabel> - <Select onValueChange={field.onChange} defaultValue={field.value} disabled={!form.watch("isRequired") && !form.watch("isConditional")}> + <Select + onValueChange={field.onChange} + defaultValue={field.value} + disabled={(!isRequired && !isConditional) || isRedFlag} + > <FormControl> <SelectTrigger> <SelectValue placeholder="질문 유형을 선택하세요" /> @@ -330,6 +385,11 @@ export function ComplianceQuestionCreateDialog({ ))} </SelectContent> </Select> + {/* {isRedFlag && ( + <FormDescription className="text-red-600"> + 레드플래그 질문은 RADIO 유형으로 고정됩니다 + </FormDescription> + )} */} <FormMessage /> </FormItem> )} @@ -337,14 +397,14 @@ export function ComplianceQuestionCreateDialog({ {/* 옵션 관리 (선택형 질문일 때만) */} {isSelectionType && ( - <div className={`space-y-3 ${(!form.watch("isRequired") && !form.watch("isConditional")) ? "opacity-50 pointer-events-none" : ""}`}> + <div className={`space-y-3 ${(!isRequired && !isConditional) ? "opacity-50 pointer-events-none" : ""}`}> <div className="flex items-center justify-between"> <div className="text-sm font-medium">옵션 관리</div> <Button type="button" variant="outline" size="sm" - disabled={!form.watch("isRequired") && !form.watch("isConditional")} + disabled={(!isRequired && !isConditional) || isRedFlag} onClick={() => { setNewOptionValue(""); setNewOptionText(""); @@ -358,7 +418,7 @@ export function ComplianceQuestionCreateDialog({ </div> {/* 옵션 추가 폼 */} - {showOptionForm && ( + {showOptionForm && !isRedFlag && ( <div className="space-y-3 p-3 border rounded-lg bg-muted/50"> <div className="grid grid-cols-2 gap-3"> <div> @@ -439,29 +499,37 @@ export function ComplianceQuestionCreateDialog({ <div className="text-sm font-mono">{opt.optionValue}</div> <div className="text-sm flex-1">{opt.optionText}</div> {opt.allowsOtherInput && <Badge variant="secondary">기타 허용</Badge>} - <Button - type="button" - variant="ghost" - size="icon" - onClick={() => { - const newOptions = options.filter((_, i) => i !== index); - setOptions(newOptions); - toast.success("옵션이 제거되었습니다."); - }} - > - <Trash2 className="h-4 w-4" /> - </Button> + {!isRedFlag && ( + <Button + type="button" + variant="ghost" + size="icon" + onClick={() => { + const newOptions = options.filter((_, i) => i !== index); + setOptions(newOptions.map((item, idx) => ({ ...item, displayOrder: idx + 1 }))); + toast.success("옵션이 제거되었습니다."); + }} + > + <Trash2 className="h-4 w-4" /> + </Button> + )} </div> )) )} </div> + + {/* {isRedFlag && ( + <div className="text-xs text-red-600"> + 레드플래그 질문의 라디오 옵션은 YES/NO로 고정됩니다. + </div> + )} */} </div> )} {/* 조건부 질문일 때만 부모 질문과 조건값 표시 */} - {form.watch("isConditional") && ( + {isConditional && ( <div className="space-y-2"> {/* 조건 질문 선택 */} <div> @@ -530,13 +598,13 @@ export function ComplianceQuestionCreateDialog({ type="button" variant="outline" onClick={() => setOpen(false)} - disabled={isLoading} + disabled={isLoading || (!isRequired && !isConditional)} > 취소 </Button> <Button type="submit" - disabled={isLoading || (!form.watch("isRequired") && !form.watch("isConditional"))} + disabled={isLoading || (!isRequired && !isConditional)} > {isLoading ? "추가 중..." : "질문 추가"} </Button> diff --git a/lib/compliance/questions/compliance-questions-draggable-list.tsx b/lib/compliance/questions/compliance-questions-draggable-list.tsx index 6a226b54..3dd82138 100644 --- a/lib/compliance/questions/compliance-questions-draggable-list.tsx +++ b/lib/compliance/questions/compliance-questions-draggable-list.tsx @@ -35,7 +35,12 @@ function SortableQuestionItem({ question, onSuccess }: SortableQuestionItemProps > <GripVertical className="h-3 w-3 text-muted-foreground" /> </SortableDragHandle> - <Badge variant="outline">{question.questionNumber}</Badge> + <Badge + variant={question.isRedFlag ? "destructive" : "outline"} + className={question.isRedFlag ? "bg-red-600 text-white border-red-600" : ""} + > + {question.questionNumber} + </Badge> <span className="font-medium flex-1 leading-tight">{question.questionText}</span> <div className="flex items-center gap-2"> <ComplianceQuestionEditSheet question={question} onSuccess={onSuccess} /> |
