/** * 관리자 회원 도메인 타입 — 순수 데이터 표현, 외부 의존 없음. * * 데이터 출처는 백엔드(edupay-backend)의 `GET /api/v1/mngr/admin/pagination`이며, * `lib/data/repositories/admin-member-repository.ts`가 응답을 이 타입으로 매핑한다. * * 값이 없는 항목은 `null`로 채워지고 화면에서 `-`로 표시된다. */ /** 역할 코드 — 백엔드 `roleId`. 코드 체계를 백엔드가 확정하지 않아 문자열로 다룬다. */ export type AdminRoleCode = string; export type AdminMember = { /** 내부 식별자 — 백엔드 `userId`. 목록 행의 key이자 수정/삭제의 입력값이다. */ id: string; /** 백엔드 `userNm`. */ name: string; /** 백엔드 `loginId`. 등록 후에는 변경할 수 없다(시안 ADM_ADM_103_p ①). */ loginId: string; /** 백엔드 목록 응답에 없다 — mock으로 등록한 행에만 값이 있다. */ phoneNumber: string | null; /** 백엔드 목록 응답에 없다 — mock으로 등록한 행에만 값이 있다. */ email: string | null; /** 백엔드 `roleId`. 표기용 이름은 `formatAdminRoleLabel`이 만든다. */ roleCode: AdminRoleCode; /** ISO 형식(YYYY-MM-DD) 문자열. 백엔드 목록 응답에 없다(정렬에만 쓰이고 select되지 않는다). */ createdAt: string | null; /** 백엔드 `acctLockYn` — 계정 잠김 여부. 시안의 "잠김여부"는 이 값의 반대(활성/비활성)다. */ isLocked: boolean | null; /** 백엔드 `useYn` — 사용 여부. */ isActive: boolean | null; /** 백엔드 `loginFailCnt` — 로그인 연속 실패 횟수. 잠김 사유 판단용이라 목록에는 노출하지 않는다. */ loginFailCount: number | null; }; /** 값이 없는 항목의 화면 표기. 표·팝업이 같은 문자를 쓰도록 여기 한 곳에 둔다. */ export const EMPTY_FIELD_PLACEHOLDER = '-'; /** 값이 없으면 `-`, 있으면 문자열로 표기한다. */ export function formatOptionalValue(value: string | number | null): string { return value === null ? EMPTY_FIELD_PLACEHOLDER : String(value); } /** * 역할 코드 → 화면 표기 이름. * * **백엔드에 역할 코드 목록을 주는 API도, 코드 상수도 없다** — `TB_ADM_USER.ADM_ROLE_CD`를 * 그대로 내려줄 뿐이고 코드값의 정의는 어디에도 없다(백엔드 저장소 전체 검색으로 확인). * 시안(ADM_ADM_101)이 요구하는 표기는 "최고관리자/일반관리자" 둘이고, 실제로 관측된 코드는 * 로그인 토큰의 `roleId` 클레임에서 본 `ROLE_SYSTEM` 하나뿐이다(`lib/domain/admin-user.ts`). * * 그래서 아래 표는 **잠정 매핑**이며, 표에 없는 코드는 임의의 이름을 지어내지 않고 코드를 그대로 * 노출한다 — 잘못된 역할 이름을 보여주는 것보다 낯선 코드를 보여주는 편이 낫다(권한 화면이라 * 표기 오류의 대가가 크다). 시안의 "역할 선택" 목록은 원래 [시스템관리 > 역할관리]가 등록한 * 역할을 가져와야 하는데 그 화면도 API도 아직 없어, 지금은 이 표가 곧 선택지다. * * 백엔드가 역할 코드 API를 내면 이 표를 지우고 그 응답으로 대체한다. */ const ADMIN_ROLE_LABEL_BY_CODE: Record = { ROLE_SYSTEM: '최고관리자', ROLE_ADMIN: '일반관리자', }; export const ADMIN_ROLE_OPTIONS: ReadonlyArray<{ value: AdminRoleCode; label: string; }> = Object.entries(ADMIN_ROLE_LABEL_BY_CODE).map(([value, label]) => ({ value, label, })); /** 시안의 "역할 선택" 기본값 — 잠정 매핑의 첫 항목(최고관리자). */ export const DEFAULT_ADMIN_ROLE_CODE: AdminRoleCode = 'ROLE_SYSTEM'; /** 역할 코드를 화면 표기로 바꾼다. 모르는 코드는 코드 그대로 노출한다(위 주석 참조). */ export function formatAdminRoleLabel(roleCode: AdminRoleCode): string { return ADMIN_ROLE_LABEL_BY_CODE[roleCode] ?? roleCode; } /** * 시안(ADM_ADM_103_p ③)의 "잠김여부" 표기. 항목은 활성/비활성이고, **잠기지 않은 계정이 활성**이다 * — 백엔드 필드가 `acctLockYn`(잠김 여부)이라 의미가 뒤집혀 있어 이 변환을 한 곳에 모아 둔다. */ export const ADMIN_LOCK_STATUS_OPTIONS: ReadonlyArray<{ value: string; label: string; }> = [ { value: 'false', label: '활성' }, { value: 'true', label: '비활성' }, ]; export function formatAdminLockStatusLabel(isLocked: boolean | null): string { if (isLocked === null) { return EMPTY_FIELD_PLACEHOLDER; } return isLocked ? '비활성' : '활성'; }