/// Registration State Provider /// /// Manages registration state for the Worker application. /// Handles user registration with role-based validation and verification. /// /// Uses Riverpod 3.0 with code generation for type-safe state management. library; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:worker/features/auth/domain/entities/user.dart'; part 'register_provider.g.dart'; /// Registration Form Data /// /// Contains all data needed for user registration. /// Optional fields are used based on selected role. class RegistrationData { /// Required: Full name of the user final String fullName; /// Required: Phone number (Vietnamese format) final String phoneNumber; /// Required: Email address final String email; /// Required: Password (minimum 6 characters) final String password; /// Required: User role final UserRole role; /// Optional: CCCD/ID card number (required for dealer/worker roles) final String? cccd; /// Optional: Tax code (personal or company) final String? taxCode; /// Optional: Company/store name final String? companyName; /// Required: Province/city final String? city; /// Optional: Attachment file paths (ID card, certificate, license) final List? attachments; const RegistrationData({ required this.fullName, required this.phoneNumber, required this.email, required this.password, required this.role, this.cccd, this.taxCode, this.companyName, this.city, this.attachments, }); /// Copy with method for immutability RegistrationData copyWith({ String? fullName, String? phoneNumber, String? email, String? password, UserRole? role, String? cccd, String? taxCode, String? companyName, String? city, List? attachments, }) { return RegistrationData( fullName: fullName ?? this.fullName, phoneNumber: phoneNumber ?? this.phoneNumber, email: email ?? this.email, password: password ?? this.password, role: role ?? this.role, cccd: cccd ?? this.cccd, taxCode: taxCode ?? this.taxCode, companyName: companyName ?? this.companyName, city: city ?? this.city, attachments: attachments ?? this.attachments, ); } } /// Registration State Provider /// /// Main provider for user registration state management. /// Handles registration process with role-based validation. /// /// Usage in widgets: /// ```dart /// final registerState = ref.watch(registerProvider); /// registerState.when( /// data: (user) => SuccessScreen(user), /// loading: () => LoadingIndicator(), /// error: (error, stack) => ErrorWidget(error), /// ); /// ``` @riverpod class Register extends _$Register { /// Initialize with no registration result @override Future build() async { // No initial registration return null; } /// Register a new user /// /// Performs user registration with role-based validation. /// For dealer/worker roles, requires additional verification documents. /// /// Parameters: /// - [data]: Registration form data containing all required fields /// /// Returns: Newly created User object on success /// /// Throws: Exception on validation failure or registration error /// /// Error messages (Vietnamese): /// - "Vui lòng điền đầy đủ thông tin bắt buộc" /// - "Số điện thoại không hợp lệ" /// - "Email không hợp lệ" /// - "Mật khẩu phải có ít nhất 6 ký tự" /// - "Vui lòng nhập số CCCD/CMND" (for dealer/worker) /// - "Vui lòng tải lên ảnh CCCD/CMND" (for dealer/worker) /// - "Vui lòng tải lên ảnh chứng chỉ hành nghề hoặc GPKD" (for dealer/worker) /// - "Số điện thoại đã được đăng ký" /// - "Email đã được đăng ký" Future register(RegistrationData data) async { // Set loading state state = const AsyncValue.loading(); // Perform registration with error handling state = await AsyncValue.guard(() async { // Validate required fields if (data.fullName.isEmpty || data.phoneNumber.isEmpty || data.email.isEmpty || data.password.isEmpty || data.city == null || data.city!.isEmpty) { throw Exception('Vui lòng điền đầy đủ thông tin bắt buộc'); } // Validate phone number (Vietnamese format: 10 digits starting with 0) final phoneRegex = RegExp(r'^0[0-9]{9}$'); if (!phoneRegex.hasMatch(data.phoneNumber)) { throw Exception('Số điện thoại không hợp lệ'); } // Validate email format final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); if (!emailRegex.hasMatch(data.email)) { throw Exception('Email không hợp lệ'); } // Validate password length if (data.password.length < 6) { throw Exception('Mật khẩu phải có ít nhất 6 ký tự'); } // Role-based validation for dealer/worker (requires verification) if (data.role == UserRole.customer) { // For dealer/worker roles, CCCD and attachments are required if (data.cccd == null || data.cccd!.isEmpty) { throw Exception('Vui lòng nhập số CCCD/CMND'); } // Validate CCCD format (9 or 12 digits) final cccdRegex = RegExp(r'^[0-9]{9}$|^[0-9]{12}$'); if (!cccdRegex.hasMatch(data.cccd!)) { throw Exception('Số CCCD/CMND không hợp lệ (phải có 9 hoặc 12 số)'); } // Validate attachments if (data.attachments == null || data.attachments!.isEmpty) { throw Exception('Vui lòng tải lên ảnh CCCD/CMND'); } if (data.attachments!.length < 2) { throw Exception('Vui lòng tải lên ảnh chứng chỉ hành nghề hoặc GPKD'); } } // Simulate API call delay (2 seconds) await Future.delayed(const Duration(seconds: 2)); // TODO: In production, call the registration API here // final response = await ref.read(authRepositoryProvider).register(data); // Mock: Simulate registration success final now = DateTime.now(); // Determine initial status based on role // Dealer/Worker require admin approval (pending status) // Other roles are immediately active final initialStatus = data.role == UserRole.customer ? UserStatus.pending : UserStatus.active; // Create new user entity final newUser = User( userId: 'user_${DateTime.now().millisecondsSinceEpoch}', phoneNumber: data.phoneNumber, fullName: data.fullName, email: data.email, role: data.role, status: initialStatus, loyaltyTier: LoyaltyTier.gold, // Default tier for new users totalPoints: 0, // New users start with 0 points companyInfo: data.companyName != null || data.taxCode != null ? CompanyInfo( name: data.companyName, taxId: data.taxCode, businessType: _getBusinessType(data.role), ) : null, cccd: data.cccd, attachments: data.attachments ?? [], address: data.city, avatarUrl: null, referralCode: 'REF${data.phoneNumber.substring(0, 6)}', referredBy: null, erpnextCustomerId: null, createdAt: now, updatedAt: now, lastLoginAt: null, // Not logged in yet ); return newUser; }); } /// Reset registration state /// /// Clears the registration result. Useful when navigating away /// from success screen or starting a new registration. Future reset() async { state = const AsyncValue.data(null); } /// Get business type based on user role String _getBusinessType(UserRole role) { switch (role) { case UserRole.customer: return 'Đại lý/Thầu thợ/Kiến trúc sư'; case UserRole.sales: return 'Nhân viên kinh doanh'; case UserRole.admin: return 'Quản trị viên'; case UserRole.accountant: return 'Kế toán'; case UserRole.designer: return 'Thiết kế'; } } /// Check if registration is in progress bool get isLoading => state.isLoading; /// Get registration error if any Object? get error => state.error; /// Get registered user if successful User? get registeredUser => state.value; /// Check if registration was successful bool get isSuccess => state.hasValue && state.value != null; } /// Convenience provider for checking if registration is in progress /// /// Usage: /// ```dart /// final isRegistering = ref.watch(isRegisteringProvider); /// if (isRegistering) { /// // Show loading indicator /// } /// ``` @riverpod bool isRegistering(Ref ref) { final registerState = ref.watch(registerProvider); return registerState.isLoading; } /// Convenience provider for checking if registration was successful /// /// Usage: /// ```dart /// final success = ref.watch(registrationSuccessProvider); /// if (success) { /// // Navigate to pending approval or OTP screen /// } /// ``` @riverpod bool registrationSuccess(Ref ref) { final registerState = ref.watch(registerProvider); return registerState.hasValue && registerState.value != null; }