/// User Info Model /// /// Data model for user information from API. /// Handles JSON serialization and conversion to domain entity. library; import 'package:worker/core/database/models/enums.dart'; import 'package:worker/features/account/domain/entities/user_info.dart'; /// User Info Model /// /// Maps API response from get_user_info endpoint to UserInfo entity. class UserInfoModel { const UserInfoModel({ required this.userId, required this.fullName, this.email, this.phoneNumber, required this.role, required this.status, required this.loyaltyTier, this.totalPoints = 0, this.availablePoints = 0, this.expiringPoints = 0, this.avatarUrl, this.companyName, this.taxId, this.address, this.cccd, this.referralCode, this.erpnextCustomerId, this.createdAt, this.updatedAt, }); final String userId; final String fullName; final String? email; final String? phoneNumber; final UserRole role; final UserStatus status; final LoyaltyTier loyaltyTier; final int totalPoints; final int availablePoints; final int expiringPoints; final String? avatarUrl; final String? companyName; final String? taxId; final String? address; final String? cccd; final String? referralCode; final String? erpnextCustomerId; final DateTime? createdAt; final DateTime? updatedAt; // ========================================================================= // JSON SERIALIZATION // ========================================================================= /// Create UserInfoModel from API JSON response /// /// Expected API response structure: /// ```json /// { /// "message": { /// "user_id": "...", /// "full_name": "...", /// "email": "...", /// "phone_number": "...", /// "role": "customer", /// "status": "active", /// "loyalty_tier": "gold", /// "total_points": 1000, /// "available_points": 800, /// "expiring_points": 200, /// "avatar_url": "...", /// "company_name": "...", /// "tax_id": "...", /// "address": "...", /// "cccd": "...", /// "referral_code": "...", /// "erpnext_customer_id": "...", /// "created_at": "...", /// "updated_at": "...", /// "last_login_at": "..." /// } /// } /// ``` factory UserInfoModel.fromJson(Map json) { // API response structure: { "message": { "success": true, "data": {...} } } final message = json['message'] as Map?; final data = message?['data'] as Map? ?? json; return UserInfoModel( // Use email as userId since API doesn't provide user_id userId: data['email'] as String? ?? data['phone'] as String? ?? 'unknown', fullName: data['full_name'] as String? ?? '', email: data['email'] as String?, phoneNumber: data['phone'] as String?, // Default values for fields not in this API role: UserRole.customer, // Default to customer status: UserStatus.active, // Default to active loyaltyTier: LoyaltyTier.bronze, // Default to bronze totalPoints: 0, availablePoints: 0, expiringPoints: 0, avatarUrl: data['avatar'] as String?, companyName: data['company_name'] as String?, taxId: data['tax_code'] as String?, address: data['address'] as String?, cccd: data['id_card_front'] as String?, // Store front ID card referralCode: null, erpnextCustomerId: null, createdAt: _parseDateTime(data['date_of_birth'] as String?), updatedAt: _parseDateTime(data['date_of_birth'] as String?), ); } /// Convert UserInfoModel to JSON Map toJson() { return { 'user_id': userId, 'full_name': fullName, 'email': email, 'phone_number': phoneNumber, 'role': role.name, 'status': status.name, 'loyalty_tier': loyaltyTier.name, 'total_points': totalPoints, 'available_points': availablePoints, 'expiring_points': expiringPoints, 'avatar_url': avatarUrl, 'company_name': companyName, 'tax_id': taxId, 'address': address, 'cccd': cccd, 'referral_code': referralCode, 'erpnext_customer_id': erpnextCustomerId, 'created_at': createdAt?.toIso8601String(), 'updated_at': updatedAt?.toIso8601String(), }; } // ========================================================================= // DOMAIN CONVERSION // ========================================================================= /// Convert to domain entity UserInfo toEntity() { final now = DateTime.now(); return UserInfo( userId: userId, fullName: fullName, email: email, phoneNumber: phoneNumber, role: role, status: status, loyaltyTier: loyaltyTier, totalPoints: totalPoints, availablePoints: availablePoints, expiringPoints: expiringPoints, avatarUrl: avatarUrl, companyName: companyName, taxId: taxId, address: address, cccd: cccd, referralCode: referralCode, erpnextCustomerId: erpnextCustomerId, createdAt: createdAt ?? now, updatedAt: updatedAt ?? now, ); } /// Create model from domain entity factory UserInfoModel.fromEntity(UserInfo entity) { return UserInfoModel( userId: entity.userId, fullName: entity.fullName, email: entity.email, phoneNumber: entity.phoneNumber, role: entity.role, status: entity.status, loyaltyTier: entity.loyaltyTier, totalPoints: entity.totalPoints, availablePoints: entity.availablePoints, expiringPoints: entity.expiringPoints, avatarUrl: entity.avatarUrl, companyName: entity.companyName, taxId: entity.taxId, address: entity.address, cccd: entity.cccd, referralCode: entity.referralCode, erpnextCustomerId: entity.erpnextCustomerId, createdAt: entity.createdAt, updatedAt: entity.updatedAt, ); } // ========================================================================= // HELPER METHODS // ========================================================================= /// Parse user role from string static UserRole _parseUserRole(String? role) { if (role == null) return UserRole.customer; return UserRole.values.firstWhere( (e) => e.name.toLowerCase() == role.toLowerCase(), orElse: () => UserRole.customer, ); } /// Parse user status from string static UserStatus _parseUserStatus(String? status) { if (status == null) return UserStatus.pending; // Handle ERPNext status values final normalizedStatus = status.toLowerCase(); if (normalizedStatus == 'enabled' || normalizedStatus == 'active') { return UserStatus.active; } if (normalizedStatus == 'disabled' || normalizedStatus == 'inactive') { return UserStatus.suspended; } return UserStatus.values.firstWhere( (e) => e.name.toLowerCase() == normalizedStatus, orElse: () => UserStatus.pending, ); } /// Parse loyalty tier from string static LoyaltyTier _parseLoyaltyTier(String? tier) { if (tier == null) return LoyaltyTier.bronze; return LoyaltyTier.values.firstWhere( (e) => e.name.toLowerCase() == tier.toLowerCase(), orElse: () => LoyaltyTier.bronze, ); } /// Parse integer from dynamic value static int _parseInt(dynamic value) { if (value == null) return 0; if (value is int) return value; if (value is double) return value.toInt(); if (value is String) return int.tryParse(value) ?? 0; return 0; } /// Parse DateTime from string static DateTime? _parseDateTime(String? dateString) { if (dateString == null || dateString.isEmpty) return null; try { return DateTime.parse(dateString); } catch (e) { return null; } } // ========================================================================= // EQUALITY // ========================================================================= @override bool operator ==(Object other) { if (identical(this, other)) return true; return other is UserInfoModel && other.userId == userId; } @override int get hashCode => userId.hashCode; @override String toString() { return 'UserInfoModel(userId: $userId, fullName: $fullName, ' 'tier: $loyaltyTier, points: $totalPoints)'; } }