import type { UserDto } from '@/lib/types/admin'; /** * AdminUserViewModel * * View Model for admin user management. * Transforms API DTO into UI-ready state with formatting and derived fields. */ export class AdminUserViewModel { id: string; email: string; displayName: string; roles: string[]; status: string; isSystemAdmin: boolean; createdAt: Date; updatedAt: Date; lastLoginAt?: Date; primaryDriverId?: string; // UI-specific derived fields readonly roleBadges: string[]; readonly statusBadge: { label: string; variant: string }; readonly lastLoginFormatted: string; readonly createdAtFormatted: string; readonly canSuspend: boolean; readonly canActivate: boolean; readonly canDelete: boolean; constructor(dto: UserDto) { this.id = dto.id; this.email = dto.email; this.displayName = dto.displayName; this.roles = dto.roles; this.status = dto.status; this.isSystemAdmin = dto.isSystemAdmin; this.createdAt = new Date(dto.createdAt); this.updatedAt = new Date(dto.updatedAt); this.lastLoginAt = dto.lastLoginAt ? new Date(dto.lastLoginAt) : undefined; this.primaryDriverId = dto.primaryDriverId; // Derive role badges this.roleBadges = this.roles.map(role => { switch (role) { case 'owner': return 'Owner'; case 'admin': return 'Admin'; case 'user': return 'User'; default: return role; } }); // Derive status badge this.statusBadge = this.getStatusBadge(); // Format dates this.lastLoginFormatted = this.lastLoginAt ? this.lastLoginAt.toLocaleDateString() : 'Never'; this.createdAtFormatted = this.createdAt.toLocaleDateString(); // Derive action permissions this.canSuspend = this.status === 'active'; this.canActivate = this.status === 'suspended'; this.canDelete = this.status !== 'deleted'; } private getStatusBadge(): { label: string; variant: string } { switch (this.status) { case 'active': return { label: 'Active', variant: 'performance-green' }; case 'suspended': return { label: 'Suspended', variant: 'yellow-500' }; case 'deleted': return { label: 'Deleted', variant: 'racing-red' }; default: return { label: this.status, variant: 'gray-500' }; } } } /** * DashboardStatsViewModel * * View Model for admin dashboard statistics. * Provides formatted statistics and derived metrics for UI. */ export class DashboardStatsViewModel { totalUsers: number; activeUsers: number; suspendedUsers: number; deletedUsers: number; systemAdmins: number; recentLogins: number; newUsersToday: number; userGrowth: { label: string; value: number; color: string; }[]; roleDistribution: { label: string; value: number; color: string; }[]; statusDistribution: { active: number; suspended: number; deleted: number; }; activityTimeline: { date: string; newUsers: number; logins: number; }[]; // UI-specific derived fields readonly activeRate: number; readonly activeRateFormatted: string; readonly adminRatio: string; readonly activityLevel: 'low' | 'medium' | 'high'; constructor(data: { totalUsers: number; activeUsers: number; suspendedUsers: number; deletedUsers: number; systemAdmins: number; recentLogins: number; newUsersToday: number; userGrowth: { label: string; value: number; color: string; }[]; roleDistribution: { label: string; value: number; color: string; }[]; statusDistribution: { active: number; suspended: number; deleted: number; }; activityTimeline: { date: string; newUsers: number; logins: number; }[]; }) { this.totalUsers = data.totalUsers; this.activeUsers = data.activeUsers; this.suspendedUsers = data.suspendedUsers; this.deletedUsers = data.deletedUsers; this.systemAdmins = data.systemAdmins; this.recentLogins = data.recentLogins; this.newUsersToday = data.newUsersToday; this.userGrowth = data.userGrowth; this.roleDistribution = data.roleDistribution; this.statusDistribution = data.statusDistribution; this.activityTimeline = data.activityTimeline; // Derive active rate this.activeRate = this.totalUsers > 0 ? (this.activeUsers / this.totalUsers) * 100 : 0; this.activeRateFormatted = `${Math.round(this.activeRate)}%`; // Derive admin ratio const nonAdmins = Math.max(1, this.totalUsers - this.systemAdmins); this.adminRatio = `1:${Math.floor(nonAdmins / Math.max(1, this.systemAdmins))}`; // Derive activity level const engagementRate = this.totalUsers > 0 ? (this.recentLogins / this.totalUsers) * 100 : 0; if (engagementRate < 20) { this.activityLevel = 'low'; } else if (engagementRate < 50) { this.activityLevel = 'medium'; } else { this.activityLevel = 'high'; } } } /** * UserListViewModel * * View Model for user list with pagination and filtering state. */ export class UserListViewModel { users: AdminUserViewModel[]; total: number; page: number; limit: number; totalPages: number; // UI-specific derived fields readonly hasUsers: boolean; readonly showPagination: boolean; readonly startIndex: number; readonly endIndex: number; constructor(data: { users: UserDto[]; total: number; page: number; limit: number; totalPages: number; }) { this.users = data.users.map(dto => new AdminUserViewModel(dto)); this.total = data.total; this.page = data.page; this.limit = data.limit; this.totalPages = data.totalPages; // Derive UI state this.hasUsers = this.users.length > 0; this.showPagination = this.totalPages > 1; this.startIndex = this.users.length > 0 ? (this.page - 1) * this.limit + 1 : 0; this.endIndex = this.users.length > 0 ? (this.page - 1) * this.limit + this.users.length : 0; } }