File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
/**
* 꾸미기 아이템 도메인 타입 — 순수 데이터 표현, 외부 의존 없음.
*
* **백엔드(edupay-backend)에 이 도메인의 API가 전혀 없다** — 조회조차 없다(사용자 확정 사항).
* 그래서 이 타입의 실제 값은 전부 `lib/data/mock/decoration-item-store.ts`가 만들어 내고,
* "백엔드 응답 → 도메인 매핑" 절차 자체가 존재하지 않는다 — admin-member.ts/student-member.ts와
* 달리 `null`이 "백엔드가 아직 주지 않는 항목"을 뜻하지 않는다(이 타입에서 `description`의
* `null`은 순수하게 "값 없음"이다).
*/
/**
* 유형 — 개별아이템 / 셋트아이템. 시안(ADM_ITM_101, ADM_ITM_102_p) 두 곳 모두 "셋트아이템도
* 동일한 항목으로 처리하며 별도 화면이 없다"고 명시한다 — 목록의 유형 탭과 등록/수정 폼의 값이
* 모두 이 타입 하나를 공유한다.
*/
export type DecorationItemType = 'individual' | 'set';
export const DECORATION_ITEM_TYPE_OPTIONS: ReadonlyArray<{
value: DecorationItemType;
label: string;
}> = [
{ value: 'individual', label: '개별아이템' },
{ value: 'set', label: '셋트아이템' },
];
export const DEFAULT_DECORATION_ITEM_TYPE: DecorationItemType = 'individual';
/**
* 카테고리 — **백엔드 코드테이블이 없어 시안 예시값을 그대로 mock 상수로 둔다**(사용자 확정
* 사항. 시안 설명: "추후 확장성을 위해 필요, As is는 단일 카테고리로 운영"). 백엔드에 코드테이블
* API가 생기면 이 상수를 지우고 그 응답으로 대체한다.
*/
export const DECORATION_ITEM_CATEGORIES = ['계절', '축하', '시즌'] as const;
export type DecorationItemCategory = (typeof DECORATION_ITEM_CATEGORIES)[number];
export type DecorationItem = {
/**
* 아이템ID — 등록 시 사용자가 직접 입력하는 유일 식별자(예: `ITEM-HAT-002`). 백엔드가 없어
* 별도 내부 id를 두지 않는다 — 목록 행의 key이자 수정/삭제 Server Action의 입력값이다.
* 등록 후에는 변경할 수 없다(시안 ADM_ITM_103_p).
*/
itemId: string;
itemType: DecorationItemType;
name: string;
category: DecorationItemCategory;
/** 오픈 가능한 포인트. */
points: number;
description: string | null;
/** 사용여부 — 표에서는 O/X로 표기한다(`formatDecorationItemActiveLabel`). */
isActive: boolean;
/**
* 정렬순서 — 목록 기본 정렬 기준(오름차순, 시안 ADM_ITM_101). **유형(개별/셋트)마다 독립된
* 순번 공간이다** — 두 탭이 같은 화면을 재사용할 뿐 사실상 별개 목록이라, 한쪽의 정렬순서가
* 다른 쪽 순번에 영향을 주지 않는다(mock 생성기·Repository가 이 규칙을 지킨다).
*/
sortOrder: number;
/** ISO 8601 문자열 — 수정일시. */
updatedAt: string;
};
/** 사용여부 → 화면 표기(시안: O/X). */
export function formatDecorationItemActiveLabel(isActive: boolean): string {
return isActive ? 'O' : 'X';
}
/** 포인트 → 화면 표기(시안: "100 P" 형태). */
export function formatDecorationItemPoints(points: number): string {
return `${points.toLocaleString('ko-KR')} P`;
}
/**
* ISO 수정일시 → "YYYY-MM-DD HH:mm". mock이 생성하는 값이 항상 `Date#toISOString()` 형식
* (`YYYY-MM-DDTHH:mm:ss.sssZ`)이라 별도 날짜 라이브러리 없이 문자열 절단만으로 충분하다(신규
* 의존성 추가 금지 — CLAUDE.md §4.2).
*/
export function formatDecorationItemUpdatedAt(updatedAt: string): string {
return updatedAt.slice(0, 16).replace('T', ' ');
}
/**
* 썸네일 자리의 플레이스홀더 텍스트 — **의도적으로 이미지가 아니다.** 시안(ADM_ITM_102_p)은
* 썸네일 이미지 업로드 필드를 요구하지만, 사용자가 "썸네일을 따로 등록하지 않고 추후 백엔드가
* 자동생성해주는 형식"이라고 확정해 등록/수정 폼에서 업로드 필드를 제외했다(시안과 다르게 가는
* 지점). 목록의 썸네일 컬럼 자체는 유지하되(백엔드가 자동생성한 이미지를 표시할 자리), 지금은
* 생성해 줄 백엔드가 없어 아이템ID의 중간 세그먼트(예: `ITEM-HAT-002` → `HAT`)를 뽑아 대신
* 보여준다 — 실제 썸네일 API가 생기면 표 컴포넌트의 이 자리만 `<img>`로 바꾸면 된다.
*/
export function getDecorationItemThumbnailLabel(itemId: string): string {
const segments = itemId.split('-');
const middle = segments.length >= 2 ? segments[1] : itemId;
return middle.slice(0, 3).toUpperCase();
}