From a4ceade24d28af0bde985bf750017efc02f053ff Mon Sep 17 00:00:00 2001 From: 0-Zz-ang Date: Thu, 13 Nov 2025 11:36:15 +0900 Subject: (박서영)준법설문조사 RedFlag관련 요청사항 반영 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../compliance-question-create-dialog.tsx | 114 +++++++++--- .../compliance-questions-draggable-list.tsx | 7 +- lib/compliance/red-flag-notifier.ts | 142 ++++++++++++++ lib/compliance/services.ts | 94 +++++++++- .../table/compliance-survey-templates-toolbar.tsx | 4 + lib/compliance/table/red-flag-managers-dialog.tsx | 203 +++++++++++++++++++++ 6 files changed, 539 insertions(+), 25 deletions(-) create mode 100644 lib/compliance/red-flag-notifier.ts create mode 100644 lib/compliance/table/red-flag-managers-dialog.tsx (limited to 'lib/compliance') 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({ )} /> + + {/* 레드플래그 체크박스 */} + ( + + + + +
+ 레드플래그 질문 + + 질문 유형 - RADIO || 옵션 - YES/NO + +
+
+ )} + /> {/* 유효성 검사 에러 메시지 */} {(form.formState.errors.isRequired || form.formState.errors.isConditional) && ( @@ -301,7 +352,7 @@ export function ComplianceQuestionCreateDialog({