export interface ApiResponse { code: number; message: string; data: T | null; } export type Role = 'ADMIN' | 'SECURITY' | 'HOST'; export interface CurrentUser { id: number; username: string; fullName: string; department?: string; email?: string; roles: Role[]; mustChangePassword: boolean; } export interface LoginRequest { username: string; password: string; } export interface ChangePasswordRequest { oldPassword: string; newPassword: string; } export interface Zone { id: number; code: string; name: string; securityLevel: number; } export interface PurposeCode { id: number; code: string; name: string; sortOrder: number; active: boolean; customAllowed: boolean; } export interface Watcher1Settings { name: string; team: string; contact: string; } export interface SmsDiagnosticsResult { provider: string; url: string; ok: boolean; reachable: boolean; httpStatus: number | null; elapsedMs: number; checkedAt: string; error: string | null; } export interface EmailDiagnosticsResult { host: string; port: number; from: string; smtpAuth: boolean; startTls: boolean; usernameConfigured: boolean; passwordConfigured: boolean; ok: boolean; reachable: boolean; elapsedMs: number; checkedAt: string; error: string | null; } export interface Team { id: number; code: string; name: string; active: boolean; defaultRoles: Role[]; } export interface AdminUser { id: number; username: string; fullName: string; department?: string; teamId?: number; teamCode?: string; teamName?: string; email?: string; enabled: boolean; locked: boolean; roles: Role[]; } export type VisitStatus = | 'DRAFT' | 'PENDING' | 'APPROVED' | 'REJECTED' | 'CANCELLED' | 'EXPIRED'; export interface VisitRequestCreate { visitorName: string; company?: string; contact: string; email?: string; vehicleNo?: string; /** 전산실 checkboxes; each selected room yields its own request/QR. */ serverRooms?: string[]; /** Detail room (콤보박스, 기타 자유 입력) — auxiliary, no separate QR. */ roomZone?: string; purpose: string; purposeCode?: string; purposeDetail?: string; /** 작업명 — optional concrete task detail, stored separately from purpose. */ workName?: string; /** 현장감시자2 (담당자 입력). 담당자·감시자1은 서버가 채운다. */ watcher2Name?: string; watcher2Team?: string; watcher2Contact?: string; visitFrom: string; // ISO local datetime visitTo: string; } export interface VisitRequestView { id: number; visitorName: string; company?: string; contact?: string; email?: string; vehicleNo?: string; hostId: number; hostName: string; hostDepartment?: string; zoneName?: string; purpose: string; purposeCode?: string; purposeDetail?: string; workName?: string; controlName?: string; controlTeam?: string; controlContact?: string; watcher1Name?: string; watcher1Team?: string; watcher1Contact?: string; watcher2Name?: string; watcher2Team?: string; watcher2Contact?: string; visitFrom: string; visitTo: string; status: VisitStatus; qrToken?: string; createdAt: string; } export interface PublicPass { visitorName: string; company?: string; zoneName?: string; visitFrom: string; visitTo: string; /** Whether the visitor is currently inside — drives the kiosk 입장/퇴장 button. */ inside: boolean; /** Whether the visit already completed (entered & exited) today — blocks re-entry. */ completedToday: boolean; } export interface ExcelImportResult { totalRows: number; successCount: number; errors: string[]; success: boolean; } export interface AdminExcelImportRow { rowNumber: number; status: 'CREATE' | 'UPDATE' | 'ERROR' | string; key?: string; summary?: string; errors: string[]; warnings: string[]; } export interface AdminExcelImportResult { totalRows: number; createCount: number; updateCount: number; errorCount: number; warningCount: number; applied: boolean; success: boolean; rows: AdminExcelImportRow[]; } export interface InsideVisitor { visitRequestId: number; visitorName: string; company?: string; zoneName?: string; hostName: string; checkInAt: string; } export interface AccessRecord { visitRequestId: number; visitorName: string; company?: string; zoneName?: string; visitFrom: string; visitTo: string; status?: VisitStatus; checkInAt?: string; checkOutAt?: string; inside: boolean; } export interface AccessAction { visitRequestId: number; visitorName: string; direction: 'IN' | 'OUT'; eventAt: string; gateOpened: boolean; message: string; } export interface StatsSummary { todayVisits: number; pending: number; approved: number; currentlyInside: number; total: number; } export interface BlacklistItem { id: number; name: string; company?: string; contact?: string; reason: string; active: boolean; createdByName?: string; createdAt: string; } export interface BlacklistCreate { name: string; company?: string; contact?: string; reason: string; } export interface AuditLog { id: number; at: string; actorId?: number; actorUsername?: string; action: 'APPROVE' | 'REJECT' | 'BLACKLIST_ADD' | 'BLACKLIST_REMOVE' | 'ADMIN_CONFIG_UPDATE'; targetType?: string; targetId?: number; detail?: string; } export type DeliveryStatus = 'SENT' | 'FAILED'; export interface PassDelivery { id: number; visitRequestId: number; channel?: string; recipient?: string; status: DeliveryStatus; attempts: number; lastError?: string; createdAt: string; updatedAt: string; }