- (visitor) route group, visitor lib, tickets wallet - CongestionPill + LiveNoticeFeed components - login/tickets screens, i18n 4 locales, wordmark assets, splash Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
223 lines
6.1 KiB
TypeScript
223 lines
6.1 KiB
TypeScript
/*
|
|
* 백엔드 API 계약 타입 (단일 출처: _workspace/01_backend_contracts.md · 04_backend_public_auth.md)
|
|
* ★ 백엔드 응답 shape과 정확히 일치해야 한다. 불일치 발견 시 _workspace/에 기록·보고.
|
|
*/
|
|
|
|
// ── 0-1 응답 봉투 ──
|
|
export interface ApiError {
|
|
code: ApiErrorCode;
|
|
message: string;
|
|
}
|
|
export interface ApiResponse<T> {
|
|
success: boolean;
|
|
data: T | null;
|
|
error: ApiError | null;
|
|
}
|
|
export interface PageResponse<T> {
|
|
items: T[];
|
|
page: number;
|
|
size: number;
|
|
total: number;
|
|
}
|
|
|
|
// ── 0-2 오류 코드 ──
|
|
export type ApiErrorCode =
|
|
| 'VALIDATION'
|
|
| 'UNAUTHORIZED'
|
|
| 'FORBIDDEN'
|
|
| 'NOT_FOUND'
|
|
| 'CONFLICT'
|
|
| 'EMAIL_TAKEN'
|
|
| 'COMPLIANCE_BLOCKED'
|
|
| 'RENDER_QUOTA_EXCEEDED'
|
|
| 'NOT_REGISTERED_COMPANY'
|
|
| 'NOT_IMPLEMENTED'
|
|
| 'ACCOUNT_LOCKED'
|
|
| 'OTP_REQUIRED'
|
|
| 'OTP_INVALID'
|
|
| 'INTERNAL';
|
|
|
|
// ── 0-5 역할 ──
|
|
export type EventRole = 'ORGANIZER' | 'EXHIBITOR' | 'CONTRACTOR' | 'HALL_MANAGER';
|
|
|
|
// ── 2. 인증·워크스페이스 (SCR-01) ──
|
|
export interface WorkspaceDto {
|
|
eventId: string;
|
|
eventName: string;
|
|
startDate: string;
|
|
endDate: string;
|
|
hallLabel: string;
|
|
myRole: EventRole;
|
|
dday: number;
|
|
}
|
|
export interface AuthUser {
|
|
userId: string;
|
|
displayName: string;
|
|
hallManager: boolean;
|
|
// 전역 역할(app_user.role_code — 'ADMIN' | 'MANAGER' | …). 공유 백엔드 /api/auth/login 이
|
|
// 웹과 동일하게 반환한다(웹 LoginResponse.user 와 동일 shape). 관람객 등 미지정 계정은 null/undefined.
|
|
// 역할별 랜딩 판정(roleTrack.resolveLandingTrack)이 admin/ops 우선순위 결정에 사용.
|
|
roleCode?: string | null;
|
|
}
|
|
export interface LoginResponse {
|
|
accessToken: string;
|
|
expiresInSeconds: number;
|
|
user: AuthUser;
|
|
workspaces: WorkspaceDto[];
|
|
}
|
|
|
|
// ── 공개 인증 (04_backend_public_auth.md) ──
|
|
export interface RegisterRequest {
|
|
email: string;
|
|
displayName: string;
|
|
password: string;
|
|
companyName?: string;
|
|
inviteCode?: string;
|
|
}
|
|
export interface RegisterResponse {
|
|
userId: string;
|
|
email: string;
|
|
displayName: string;
|
|
joinedEvent: string | null;
|
|
}
|
|
export interface MessageResponse {
|
|
message: string;
|
|
}
|
|
|
|
// ── 6. M5 나노바나나 RenderJob (SCR-06/12) ──
|
|
export type RenderJobStatus = 'QUEUED' | 'RUNNING' | 'DONE' | 'FAILED';
|
|
export interface RenderJobDto {
|
|
jobId: string;
|
|
boothId: string;
|
|
shotPreset: string; // S1..S7
|
|
status: RenderJobStatus;
|
|
imageUrl: string | null;
|
|
schemaHash: string | null;
|
|
modelVersion: string | null;
|
|
/** AI 생성 이미지 고지 — 항상 true (제거 불가). */
|
|
watermarkRequired: boolean;
|
|
watermarkText: string;
|
|
notice?: string;
|
|
createdAt?: string;
|
|
}
|
|
|
|
// ── 헬스체크 ──
|
|
export interface HealthDto {
|
|
status: string;
|
|
service: string;
|
|
time: string;
|
|
}
|
|
|
|
// ── 내정보(SCR-M 프로필) ──
|
|
// GET /api/auth/me → KintexPrincipal(백엔드 record). 이메일·부서·전화는 현 계약 미포함(백엔드 인계 대상).
|
|
export interface MePrincipal {
|
|
userId: string;
|
|
displayName: string;
|
|
eventRoles: Record<string, EventRole>;
|
|
hallManager: boolean;
|
|
roleCode: string | null;
|
|
tenantId: string | null;
|
|
}
|
|
|
|
// GET /api/auth/otp/status → { otpEnabled, verifyMethod }.
|
|
export interface OtpStatusDto {
|
|
otpEnabled: boolean;
|
|
verifyMethod: string;
|
|
}
|
|
|
|
/** 아바타 업로드 응답(제안 계약 — 백엔드 미구현 시 degrade). */
|
|
export interface AvatarUploadResponse {
|
|
photoUrl: string;
|
|
}
|
|
|
|
// ── B2C 관람객: 혼잡·주차 (parking_congestion_contract.md) ──
|
|
/** 혼잡/점유 수준. 여유/보통/혼잡 3단계. */
|
|
export type CongestionLevel = 'FREE' | 'NORMAL' | 'BUSY';
|
|
|
|
/** 주차장 현황 — GET /api/public/parking/lots (공개). */
|
|
export interface ParkingLotStatusDto {
|
|
lotId: string;
|
|
code: string;
|
|
name: string;
|
|
exhibitionCenter: number | null; // 1|2|null(공용)
|
|
totalCapacity: number;
|
|
occupied: number;
|
|
available: number;
|
|
occupancyPercent: number;
|
|
congestionLevel: CongestionLevel;
|
|
congestionLabel: string; // 여유|보통|혼잡
|
|
hourlyRate: number;
|
|
dailyMax: number | null;
|
|
passPrice: number; // 사전 주차권(1일권) 가격
|
|
note?: string | null;
|
|
updatedAt: string;
|
|
}
|
|
|
|
/** 사전 주차권 구매 요청 — POST /api/parking/passes (인증). */
|
|
export interface PurchasePassRequest {
|
|
lotId: string;
|
|
useDate: string; // YYYY-MM-DD, 오늘 이후
|
|
eventId?: string;
|
|
vehiclePlate?: string; // 마스킹 후 폐기·미저장
|
|
payMethod?: 'card' | 'easy' | 'bank';
|
|
}
|
|
|
|
/** 주차권 — POST/GET 응답 공통 shape. */
|
|
export interface ParkingPassDto {
|
|
passNo: string;
|
|
status: 'PAID' | 'CANCELLED' | 'USED';
|
|
lotId: string;
|
|
lotName: string;
|
|
eventId: string | null;
|
|
useDate: string;
|
|
vehiclePlateMasked: string | null; // 원문 미저장(마스킹만)
|
|
amount: number;
|
|
payMethod: string | null;
|
|
payApprovalNo: string | null;
|
|
issuedAt: string;
|
|
}
|
|
|
|
/** 내 주차권 — GET /api/parking/passes/me. */
|
|
export interface MyPassesDto {
|
|
passes: ParkingPassDto[];
|
|
}
|
|
|
|
/** 혼잡 영역 — 게이트/주차/인기 공간 공통. occupancyPercent는 계측 부재 시 null. */
|
|
export interface CongestionAreaDto {
|
|
id: string;
|
|
label: string;
|
|
level: CongestionLevel;
|
|
levelLabel: string;
|
|
occupancyPercent: number | null;
|
|
}
|
|
|
|
/** 혼잡 요약 — GET /api/public/congestion?eventId= (공개). */
|
|
export interface CongestionOverviewDto {
|
|
eventId: string;
|
|
overallLevel: CongestionLevel;
|
|
overallLabel: string;
|
|
onSiteCount: number;
|
|
entryGates: CongestionAreaDto[];
|
|
parking: CongestionAreaDto[];
|
|
popularSessions: CongestionAreaDto[];
|
|
updatedAt: string;
|
|
}
|
|
|
|
// ── B2C 관람객: 라이브 공지 (cms_backlog_contract.md) ──
|
|
/** 공지 카테고리 — 배지 색상 매핑(URGENT=red·PROGRAM=amber·INFO=blue·GENERAL=slate). */
|
|
export type LiveNoticeCategory = 'URGENT' | 'PROGRAM' | 'GENERAL' | 'INFO';
|
|
|
|
/** 라이브 공지 — GET /api/public/events/{eventId}/live-notices (공개). */
|
|
export interface LiveNoticeDto {
|
|
id: string;
|
|
eventId: string;
|
|
category: LiveNoticeCategory;
|
|
title: string;
|
|
body?: string | null;
|
|
pinned: boolean;
|
|
status: string; // published|archived
|
|
authorName?: string | null;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|