/** * 학생 회원 도메인 타입 — 순수 데이터 표현, 외부 의존 없음. * * 데이터 출처는 백엔드(edupay-backend)의 `GET /api/v1/mngr/user/pagination`이며, * `lib/data/repositories/student-member-repository.ts`가 응답을 이 타입으로 매핑한다. * * **`null`의 의미는 "백엔드가 아직 주지 않는 항목"이다.** 현재 응답이 담고 있는 값은 * 식별자·이름 계열(`userId`/`loginId`/`userNm`) 넷뿐이라 나머지 항목은 전부 `null`로 채워지고 * 화면에서 `-`로 표시된다(화면 컬럼은 유지 — 백엔드가 필드를 추가하면 Repository의 매핑만 * 늘리면 그대로 채워진다). 값이 "비어 있다"와 "제공되지 않는다"를 굳이 구분하지 않는 이유는, * 조회 전용 화면에서 둘 다 사용자에게는 `-`로 같은 의미이기 때문이다. * * 조회 전용 화면(등록/수정/삭제 없음)의 데이터라 모든 필드를 읽기 전용으로 취급한다 * (수정 가능 여부는 UI 계층의 책임이지 타입 자체의 제약은 아니다). */ export type StudentMember = { /** 내부 식별자 — 백엔드 `userId`. 목록 행의 key이자 향후 단건 조회의 입력값이다. */ id: string; /** 화면에 노출되는 회원코드. 백엔드에 전용 필드가 없어 현재는 `userId`를 그대로 쓴다. */ memberCode: string; /** 백엔드 `userNm`. */ name: string; /** 백엔드 `loginId`. */ loginId: string; phoneNumber: string | null; email: string | null; /** 회원 역할 — 본 화면(학생 회원 목록)에서는 항상 '학생'이다. */ role: string; schoolName: string | null; grade: number | null; classNumber: number | null; studentNumber: number | null; guardianName: string | null; guardianPhoneNumber: string | null; /** ISO 형식(YYYY-MM-DD) 문자열. */ joinedAt: string | null; /** ISO 형식(YYYY-MM-DD) 문자열. */ birthDate: string | null; /** 사용여부. 백엔드가 값도 변경 API도 제공하지 않아 현재는 항상 null이다. */ isActive: boolean | null; }; /** 값이 없는 항목의 화면 표기. 표·조회 팝업이 같은 문자를 쓰도록 여기 한 곳에 둔다. */ export const EMPTY_FIELD_PLACEHOLDER = '-'; /** 값이 없으면 `-`, 있으면 문자열로 표기한다. */ export function formatOptionalValue( value: string | number | null ): string { return value === null ? EMPTY_FIELD_PLACEHOLDER : String(value); } /** * "학년/반/번호" 합성 표기. 표와 조회 팝업이 같은 규칙을 쓰도록 한 곳에 둔다. * * **있는 값만 이어 붙인다.** 백엔드 `TB_COM_USER`에는 GRADE·CLS_NO만 있고 학생 번호 컬럼이 * 아예 없어(코드베이스 전체 VO가 그 둘만 쓴다) 번호는 영영 오지 않는다. 셋이 다 있어야 * 표기하던 종전 규칙으로는 실제로 오는 학년·반까지 함께 묻혔다. * * 하나도 없으면 `-`다. */ export function formatGradeClassNumber(member: StudentMember): string { const parts = [ member.grade === null ? null : `${member.grade}학년`, member.classNumber === null ? null : `${member.classNumber}반`, member.studentNumber === null ? null : `${member.studentNumber}번`, ].filter((part): part is string => part !== null); return parts.length > 0 ? parts.join(' ') : EMPTY_FIELD_PLACEHOLDER; }