/** * 콘텐츠관리 도메인 — 금융쇼츠·금융OX퀴즈·4컷금융만화가 공유하는 값과 표기 규칙. * * 게시판(`bbs`)과 무관하다. 세 종류는 백엔드 API가 각각 따로 있고 저장되는 테이블도 * 쇼츠·만화(`TB_COM_CNTNTS`)와 퀴즈(`TB_COM_QUIZ`)로 갈린다. */ import { EMPTY_FIELD_PLACEHOLDER } from '@/lib/domain/common-code'; export const CONTENT_KINDS = ['shorts', 'quiz', 'comic'] as const; export type ContentKind = (typeof CONTENT_KINDS)[number]; export const CONTENT_KIND_LABEL: Record = { shorts: '금융쇼츠', quiz: '금융OX퀴즈', comic: '4컷금융만화', }; // TODO: `TB_COM_CNTNTS.CNTNTS_TYPE` — 만화 'T'만 백엔드 매퍼에서 확인했다. 쇼츠는 미확인 잠정값. export const CONTENT_TYPE_CODE: Record<'shorts' | 'comic', string> = { shorts: 'V', comic: 'T', }; export type SchoolLevelOption = { value: string; label: string; /** 학년 칩 개수. 0이면 학년을 고르지 않는다(전체연령). */ gradeCount: number; }; // TODO: 학교급 코드값이 백엔드에 없다(검색 파라미터만 있고 컬럼·SQL이 없음). 잠정값이다. export const SCHOOL_LEVEL_OPTIONS: readonly SchoolLevelOption[] = [ { value: 'ELEM', label: '초등학교', gradeCount: 6 }, { value: 'MIDD', label: '중학교', gradeCount: 3 }, { value: 'HIGH', label: '고등학교', gradeCount: 3 }, { value: 'ALL', label: '전체연령', gradeCount: 0 }, ]; export const DEFAULT_SCHOOL_LEVEL = SCHOOL_LEVEL_OPTIONS[0].value; export function findSchoolLevel(value: string): SchoolLevelOption | undefined { return SCHOOL_LEVEL_OPTIONS.find((option) => option.value === value); } export function gradeOptionsOf(schoolLevel: string): readonly string[] { const count = findSchoolLevel(schoolLevel)?.gradeCount ?? 0; return Array.from({ length: count }, (_, index) => String(index + 1)); } /** 난이도 — 백엔드 `MngrQuizVo.quizLev` 주석(1:하, 2:중, 3:상)에서 확인한 실제 코드값. */ export const QUIZ_LEVEL_OPTIONS: ReadonlyArray<{ value: string; label: string }> = [ { value: '3', label: '상' }, { value: '2', label: '중' }, { value: '1', label: '하' }, ]; export const QUIZ_ANSWER_OPTIONS = ['O', 'X'] as const; export type QuizAnswer = (typeof QUIZ_ANSWER_OPTIONS)[number]; /** 정답여부에 함께 노출할 문구. 기타를 고르면 직접 입력한다. */ export const ANSWER_MESSAGE_ETC = 'ETC'; // TODO: 기획이 "추후 정의"로 남긴 기본 3종이다. 확정되면 이 표만 교체한다. export const ANSWER_MESSAGE_OPTIONS: ReadonlyArray<{ value: string; label: string }> = [ { value: 'MSG1', label: '사용자에게 노출할 문구1' }, { value: 'MSG2', label: '사용자에게 노출할 문구2' }, { value: 'MSG3', label: '사용자에게 노출할 문구3' }, { value: ANSWER_MESSAGE_ETC, label: '기타' }, ]; export type ContentKeyword = { keyword: string; description: string; }; /** 등록 폼이 처음 보여 주는 키워드 줄 수 — 기획 "기본 2줄 제공". */ export const DEFAULT_KEYWORD_ROWS = 2; /** 4컷금융만화의 최소 컷 수 — 기획 "기본 4컷 제공, 4컷 이상 구성도 가능". */ export const MIN_COMIC_CUTS = 4; export const THUMBNAIL_HELP_TEXT = '권장 사이즈 : 480x270px'; /** 쇼츠·만화 한 건. 백엔드 `MngrCntntsToonVo`(= `CmmCntntsVo`)를 화면 언어로 옮긴 것. */ export type ContentItem = { cntntsSn: string; /** 만화의 「콘텐츠명」. 쇼츠 목록에는 열이 없다. */ name: string; /** 쇼츠의 「제목」. */ title: string; subject: string; message: string; schoolLevel: string; grades: string[]; keywords: ContentKeyword[]; /** 만화의 컷 이미지·쇼츠의 영상이 묶인 첨부 ID. */ attachmentId: string | null; thumbnailFileId: string | null; isVisible: boolean; modifiedAt: string | null; }; /** 금융OX퀴즈 한 건. 백엔드 `MngrQuizVo`. */ export type QuizItem = { quizSn: string; /** 백엔드 `quizTitle` — 목록의 「키워드」 열이다(제목이 아니다). */ keywordText: string; level: string; question: string; explanation: string; answer: string; points: number | null; isVisible: boolean; modifiedAt: string | null; }; export function formatSchoolLevelLabel(value: string): string { return findSchoolLevel(value)?.label ?? (value || EMPTY_FIELD_PLACEHOLDER); } export function formatGradesLabel(grades: readonly string[]): string { return grades.length > 0 ? grades.join(',') : EMPTY_FIELD_PLACEHOLDER; } export function formatKeywordsLabel(keywords: readonly ContentKeyword[]): string { const names = keywords.map((item) => item.keyword).filter(Boolean); return names.length > 0 ? names.join(', ') : EMPTY_FIELD_PLACEHOLDER; } export function formatQuizLevelLabel(value: string): string { return QUIZ_LEVEL_OPTIONS.find((option) => option.value === value)?.label ?? value; } /** 목록의 「비공개」 열 — 기획 라벨 그대로다(값은 공개/비공개). */ export function formatVisibilityLabel(isVisible: boolean): string { return isVisible ? '공개' : '비공개'; } export function formatContentText(value: string | null | undefined): string { return value === null || value === undefined || value === '' ? EMPTY_FIELD_PLACEHOLDER : value; }