add auth, format

This commit is contained in:
Phuoc Nguyen
2025-11-07 11:52:06 +07:00
parent 24a8508fce
commit 3803bd26e0
173 changed files with 8505 additions and 7116 deletions

View File

@@ -0,0 +1,279 @@
/// Authentication State Provider
///
/// Manages authentication state for the Worker application.
/// Handles login, logout, and user session management.
///
/// Uses Riverpod 3.0 with code generation for type-safe state management.
library;
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:worker/features/auth/data/datasources/auth_local_datasource.dart';
import 'package:worker/features/auth/data/models/auth_session_model.dart';
import 'package:worker/features/auth/domain/entities/user.dart';
part 'auth_provider.g.dart';
/// Provide FlutterSecureStorage instance
@riverpod
FlutterSecureStorage secureStorage(Ref ref) {
return const FlutterSecureStorage(
aOptions: AndroidOptions(encryptedSharedPreferences: true),
iOptions: IOSOptions(accessibility: KeychainAccessibility.first_unlock),
);
}
/// Provide AuthLocalDataSource instance
@riverpod
AuthLocalDataSource authLocalDataSource(Ref ref) {
final secureStorage = ref.watch(secureStorageProvider);
return AuthLocalDataSource(secureStorage);
}
/// Authentication state result
///
/// Represents the result of authentication operations.
/// Contains either the authenticated user or null if logged out.
typedef AuthState = AsyncValue<User?>;
/// Authentication Provider
///
/// Main provider for authentication state management.
/// Provides login and logout functionality with async state handling.
///
/// Usage in widgets:
/// ```dart
/// final authState = ref.watch(authProvider);
/// authState.when(
/// data: (user) => user != null ? HomeScreen() : LoginScreen(),
/// loading: () => LoadingIndicator(),
/// error: (error, stack) => ErrorWidget(error),
/// );
/// ```
@riverpod
class Auth extends _$Auth {
/// Get auth local data source
AuthLocalDataSource get _localDataSource =>
ref.read(authLocalDataSourceProvider);
/// Initialize with saved session if available
@override
Future<User?> build() async {
// Check for saved session in secure storage
final session = await _localDataSource.getSession();
if (session != null) {
// User has saved session, create User entity
final now = DateTime.now();
return User(
userId: 'user_saved', // TODO: Get from API
phoneNumber: '', // TODO: Get from saved user data
fullName: session.fullName,
email: '', // TODO: Get from saved user data
role: UserRole.customer,
status: UserStatus.active,
loyaltyTier: LoyaltyTier.gold,
totalPoints: 0,
companyInfo: null,
cccd: null,
attachments: [],
address: null,
avatarUrl: null,
referralCode: null,
referredBy: null,
erpnextCustomerId: null,
createdAt: session.createdAt,
updatedAt: now,
lastLoginAt: now,
);
}
return null;
}
/// Login with phone number and password
///
/// Simulates ERPNext API authentication with mock response.
/// Stores session data (SID, CSRF token) in Hive.
///
/// Parameters:
/// - [phoneNumber]: User's phone number (Vietnamese format)
/// - [password]: User's password
///
/// Returns: Authenticated User object on success
///
/// Throws: Exception on authentication failure
Future<void> login({
required String phoneNumber,
required String password,
}) async {
// Set loading state
state = const AsyncValue.loading();
// Simulate API call delay
state = await AsyncValue.guard(() async {
await Future<void>.delayed(const Duration(seconds: 2));
// Mock validation
if (phoneNumber.isEmpty || password.isEmpty) {
throw Exception('Số điện thoại và mật khẩu không được để trống');
}
if (password.length < 6) {
throw Exception('Mật khẩu phải có ít nhất 6 ký tự');
}
// Simulate API response matching ERPNext format
final mockApiResponse = AuthSessionResponse(
sessionExpired: 1,
message: const LoginMessage(
success: true,
message: 'Login successful',
sid: 'df7fd4e7ef1041aa3422b0ee861315ba8c28d4fe008a7d7e0e7e0e01',
csrfToken: '6b6e37563854e951c36a7af4177956bb15ca469ca4f498b742648d70',
apps: [
AppInfo(
appTitle: 'App nhân viên kinh doanh',
appEndpoint: '/ecommerce/app-sales',
appLogo:
'https://assets.digitalbiz.com.vn/DBIZ_Internal/Logo/logo_app_sales.png',
),
],
),
homePage: '/apps',
fullName: 'Tân Duy Nguyễn',
);
// Save session data to Hive
final sessionData = SessionData.fromAuthResponse(mockApiResponse);
await _localDataSource.saveSession(sessionData);
// Create and return User entity
final now = DateTime.now();
return User(
userId: 'user_${phoneNumber.replaceAll('+84', '')}',
phoneNumber: phoneNumber,
fullName: mockApiResponse.fullName,
email: 'user@eurotile.vn',
role: UserRole.customer,
status: UserStatus.active,
loyaltyTier: LoyaltyTier.gold,
totalPoints: 1500,
companyInfo: const CompanyInfo(
name: 'Công ty TNHH XYZ',
taxId: '0123456789',
businessType: 'Xây dựng',
),
cccd: '001234567890',
attachments: [],
address: '123 Đường ABC, Quận 1, TP.HCM',
avatarUrl: null,
referralCode: 'REF${phoneNumber.replaceAll('+84', '').substring(0, 6)}',
referredBy: null,
erpnextCustomerId: null,
createdAt: now.subtract(const Duration(days: 30)),
updatedAt: now,
lastLoginAt: now,
);
});
}
/// Logout current user
///
/// Clears authentication state and removes saved session from Hive.
Future<void> logout() async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() async {
// Clear saved session from Hive
await _localDataSource.clearSession();
// TODO: Call logout API to invalidate token on server
await Future<void>.delayed(const Duration(milliseconds: 500));
// Return null to indicate logged out
return null;
});
}
/// Get current authenticated user
///
/// Returns the current user if logged in, null otherwise.
User? get currentUser => state.value;
/// Check if user is authenticated
///
/// Returns true if there is a logged-in user.
bool get isAuthenticated => currentUser != null;
/// Check if authentication is in progress
///
/// Returns true during login/logout operations.
bool get isLoading => state.isLoading;
/// Get authentication error if any
///
/// Returns error message or null if no error.
Object? get error => state.error;
}
/// Convenience provider for checking if user is authenticated
///
/// Usage:
/// ```dart
/// final isLoggedIn = ref.watch(isAuthenticatedProvider);
/// if (isLoggedIn) {
/// // Show home screen
/// }
/// ```
@riverpod
bool isAuthenticated(Ref ref) {
final authState = ref.watch(authProvider);
return authState.value != null;
}
/// Convenience provider for getting current user
///
/// Usage:
/// ```dart
/// final user = ref.watch(currentUserProvider);
/// if (user != null) {
/// Text('Welcome ${user.fullName}');
/// }
/// ```
@riverpod
User? currentUser(Ref ref) {
final authState = ref.watch(authProvider);
return authState.value;
}
/// Convenience provider for user's loyalty tier
///
/// Returns the current user's loyalty tier or null if not logged in.
///
/// Usage:
/// ```dart
/// final tier = ref.watch(userLoyaltyTierProvider);
/// if (tier != null) {
/// Text('Tier: ${tier.displayName}');
/// }
/// ```
@riverpod
LoyaltyTier? userLoyaltyTier(Ref ref) {
final user = ref.watch(currentUserProvider);
return user?.loyaltyTier;
}
/// Convenience provider for user's total points
///
/// Returns the current user's total loyalty points or 0 if not logged in.
///
/// Usage:
/// ```dart
/// final points = ref.watch(userTotalPointsProvider);
/// Text('Points: $points');
/// ```
@riverpod
int userTotalPoints(Ref ref) {
final user = ref.watch(currentUserProvider);
return user?.totalPoints ?? 0;
}

View File

@@ -0,0 +1,500 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'auth_provider.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
/// Provide FlutterSecureStorage instance
@ProviderFor(secureStorage)
const secureStorageProvider = SecureStorageProvider._();
/// Provide FlutterSecureStorage instance
final class SecureStorageProvider
extends
$FunctionalProvider<
FlutterSecureStorage,
FlutterSecureStorage,
FlutterSecureStorage
>
with $Provider<FlutterSecureStorage> {
/// Provide FlutterSecureStorage instance
const SecureStorageProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'secureStorageProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$secureStorageHash();
@$internal
@override
$ProviderElement<FlutterSecureStorage> $createElement(
$ProviderPointer pointer,
) => $ProviderElement(pointer);
@override
FlutterSecureStorage create(Ref ref) {
return secureStorage(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(FlutterSecureStorage value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<FlutterSecureStorage>(value),
);
}
}
String _$secureStorageHash() => r'c3d90388f6d1bb7c95a29ceeda2e56c57deb1ecb';
/// Provide AuthLocalDataSource instance
@ProviderFor(authLocalDataSource)
const authLocalDataSourceProvider = AuthLocalDataSourceProvider._();
/// Provide AuthLocalDataSource instance
final class AuthLocalDataSourceProvider
extends
$FunctionalProvider<
AuthLocalDataSource,
AuthLocalDataSource,
AuthLocalDataSource
>
with $Provider<AuthLocalDataSource> {
/// Provide AuthLocalDataSource instance
const AuthLocalDataSourceProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'authLocalDataSourceProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$authLocalDataSourceHash();
@$internal
@override
$ProviderElement<AuthLocalDataSource> $createElement(
$ProviderPointer pointer,
) => $ProviderElement(pointer);
@override
AuthLocalDataSource create(Ref ref) {
return authLocalDataSource(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(AuthLocalDataSource value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<AuthLocalDataSource>(value),
);
}
}
String _$authLocalDataSourceHash() =>
r'f104de00a8ab431f6736387fb499c2b6e0ab4924';
/// Authentication Provider
///
/// Main provider for authentication state management.
/// Provides login and logout functionality with async state handling.
///
/// Usage in widgets:
/// ```dart
/// final authState = ref.watch(authProvider);
/// authState.when(
/// data: (user) => user != null ? HomeScreen() : LoginScreen(),
/// loading: () => LoadingIndicator(),
/// error: (error, stack) => ErrorWidget(error),
/// );
/// ```
@ProviderFor(Auth)
const authProvider = AuthProvider._();
/// Authentication Provider
///
/// Main provider for authentication state management.
/// Provides login and logout functionality with async state handling.
///
/// Usage in widgets:
/// ```dart
/// final authState = ref.watch(authProvider);
/// authState.when(
/// data: (user) => user != null ? HomeScreen() : LoginScreen(),
/// loading: () => LoadingIndicator(),
/// error: (error, stack) => ErrorWidget(error),
/// );
/// ```
final class AuthProvider extends $AsyncNotifierProvider<Auth, User?> {
/// Authentication Provider
///
/// Main provider for authentication state management.
/// Provides login and logout functionality with async state handling.
///
/// Usage in widgets:
/// ```dart
/// final authState = ref.watch(authProvider);
/// authState.when(
/// data: (user) => user != null ? HomeScreen() : LoginScreen(),
/// loading: () => LoadingIndicator(),
/// error: (error, stack) => ErrorWidget(error),
/// );
/// ```
const AuthProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'authProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$authHash();
@$internal
@override
Auth create() => Auth();
}
String _$authHash() => r'6f410d1abe6c53a6cbfa52fde7ea7a2d22a7f78d';
/// Authentication Provider
///
/// Main provider for authentication state management.
/// Provides login and logout functionality with async state handling.
///
/// Usage in widgets:
/// ```dart
/// final authState = ref.watch(authProvider);
/// authState.when(
/// data: (user) => user != null ? HomeScreen() : LoginScreen(),
/// loading: () => LoadingIndicator(),
/// error: (error, stack) => ErrorWidget(error),
/// );
/// ```
abstract class _$Auth extends $AsyncNotifier<User?> {
FutureOr<User?> build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<AsyncValue<User?>, User?>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<AsyncValue<User?>, User?>,
AsyncValue<User?>,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}
/// Convenience provider for checking if user is authenticated
///
/// Usage:
/// ```dart
/// final isLoggedIn = ref.watch(isAuthenticatedProvider);
/// if (isLoggedIn) {
/// // Show home screen
/// }
/// ```
@ProviderFor(isAuthenticated)
const isAuthenticatedProvider = IsAuthenticatedProvider._();
/// Convenience provider for checking if user is authenticated
///
/// Usage:
/// ```dart
/// final isLoggedIn = ref.watch(isAuthenticatedProvider);
/// if (isLoggedIn) {
/// // Show home screen
/// }
/// ```
final class IsAuthenticatedProvider
extends $FunctionalProvider<bool, bool, bool>
with $Provider<bool> {
/// Convenience provider for checking if user is authenticated
///
/// Usage:
/// ```dart
/// final isLoggedIn = ref.watch(isAuthenticatedProvider);
/// if (isLoggedIn) {
/// // Show home screen
/// }
/// ```
const IsAuthenticatedProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'isAuthenticatedProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$isAuthenticatedHash();
@$internal
@override
$ProviderElement<bool> $createElement($ProviderPointer pointer) =>
$ProviderElement(pointer);
@override
bool create(Ref ref) {
return isAuthenticated(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(bool value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<bool>(value),
);
}
}
String _$isAuthenticatedHash() => r'dc783f052ad2ddb7fa18c58e5dc6d212e6c32a96';
/// Convenience provider for getting current user
///
/// Usage:
/// ```dart
/// final user = ref.watch(currentUserProvider);
/// if (user != null) {
/// Text('Welcome ${user.fullName}');
/// }
/// ```
@ProviderFor(currentUser)
const currentUserProvider = CurrentUserProvider._();
/// Convenience provider for getting current user
///
/// Usage:
/// ```dart
/// final user = ref.watch(currentUserProvider);
/// if (user != null) {
/// Text('Welcome ${user.fullName}');
/// }
/// ```
final class CurrentUserProvider extends $FunctionalProvider<User?, User?, User?>
with $Provider<User?> {
/// Convenience provider for getting current user
///
/// Usage:
/// ```dart
/// final user = ref.watch(currentUserProvider);
/// if (user != null) {
/// Text('Welcome ${user.fullName}');
/// }
/// ```
const CurrentUserProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'currentUserProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$currentUserHash();
@$internal
@override
$ProviderElement<User?> $createElement($ProviderPointer pointer) =>
$ProviderElement(pointer);
@override
User? create(Ref ref) {
return currentUser(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(User? value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<User?>(value),
);
}
}
String _$currentUserHash() => r'f3c1da551f4a4c2bf158782ea37a4749a718128a';
/// Convenience provider for user's loyalty tier
///
/// Returns the current user's loyalty tier or null if not logged in.
///
/// Usage:
/// ```dart
/// final tier = ref.watch(userLoyaltyTierProvider);
/// if (tier != null) {
/// Text('Tier: ${tier.displayName}');
/// }
/// ```
@ProviderFor(userLoyaltyTier)
const userLoyaltyTierProvider = UserLoyaltyTierProvider._();
/// Convenience provider for user's loyalty tier
///
/// Returns the current user's loyalty tier or null if not logged in.
///
/// Usage:
/// ```dart
/// final tier = ref.watch(userLoyaltyTierProvider);
/// if (tier != null) {
/// Text('Tier: ${tier.displayName}');
/// }
/// ```
final class UserLoyaltyTierProvider
extends $FunctionalProvider<LoyaltyTier?, LoyaltyTier?, LoyaltyTier?>
with $Provider<LoyaltyTier?> {
/// Convenience provider for user's loyalty tier
///
/// Returns the current user's loyalty tier or null if not logged in.
///
/// Usage:
/// ```dart
/// final tier = ref.watch(userLoyaltyTierProvider);
/// if (tier != null) {
/// Text('Tier: ${tier.displayName}');
/// }
/// ```
const UserLoyaltyTierProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'userLoyaltyTierProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$userLoyaltyTierHash();
@$internal
@override
$ProviderElement<LoyaltyTier?> $createElement($ProviderPointer pointer) =>
$ProviderElement(pointer);
@override
LoyaltyTier? create(Ref ref) {
return userLoyaltyTier(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(LoyaltyTier? value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<LoyaltyTier?>(value),
);
}
}
String _$userLoyaltyTierHash() => r'f1a157486b8bdd2cf64bc2201207f2ac71ea6a69';
/// Convenience provider for user's total points
///
/// Returns the current user's total loyalty points or 0 if not logged in.
///
/// Usage:
/// ```dart
/// final points = ref.watch(userTotalPointsProvider);
/// Text('Points: $points');
/// ```
@ProviderFor(userTotalPoints)
const userTotalPointsProvider = UserTotalPointsProvider._();
/// Convenience provider for user's total points
///
/// Returns the current user's total loyalty points or 0 if not logged in.
///
/// Usage:
/// ```dart
/// final points = ref.watch(userTotalPointsProvider);
/// Text('Points: $points');
/// ```
final class UserTotalPointsProvider extends $FunctionalProvider<int, int, int>
with $Provider<int> {
/// Convenience provider for user's total points
///
/// Returns the current user's total loyalty points or 0 if not logged in.
///
/// Usage:
/// ```dart
/// final points = ref.watch(userTotalPointsProvider);
/// Text('Points: $points');
/// ```
const UserTotalPointsProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'userTotalPointsProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$userTotalPointsHash();
@$internal
@override
$ProviderElement<int> $createElement($ProviderPointer pointer) =>
$ProviderElement(pointer);
@override
int create(Ref ref) {
return userTotalPoints(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(int value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<int>(value),
);
}
}
String _$userTotalPointsHash() => r'9ccebb48a8641c3c0624b1649303b436e82602bd';

View File

@@ -0,0 +1,112 @@
/// Password Visibility Provider
///
/// Simple state provider for toggling password visibility in login/register forms.
///
/// Uses Riverpod 3.0 with code generation for type-safe state management.
library;
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'password_visibility_provider.g.dart';
/// Password Visibility State Provider
///
/// Manages the visibility state of password input fields.
/// Default state is false (password hidden).
///
/// Usage in login/register pages:
/// ```dart
/// class LoginPage extends ConsumerWidget {
/// @override
/// Widget build(BuildContext context, WidgetRef ref) {
/// final isPasswordVisible = ref.watch(passwordVisibilityProvider);
///
/// return TextField(
/// obscureText: !isPasswordVisible,
/// decoration: InputDecoration(
/// suffixIcon: IconButton(
/// icon: Icon(
/// isPasswordVisible ? Icons.visibility : Icons.visibility_off,
/// ),
/// onPressed: () {
/// ref.read(passwordVisibilityProvider.notifier).toggle();
/// },
/// ),
/// ),
/// );
/// }
/// }
/// ```
@riverpod
class PasswordVisibility extends _$PasswordVisibility {
/// Initialize with password hidden (false)
@override
bool build() => false;
/// Toggle password visibility
///
/// Switches between showing and hiding the password.
void toggle() {
state = !state;
}
/// Show password
///
/// Sets visibility to true (password visible).
void show() {
state = true;
}
/// Hide password
///
/// Sets visibility to false (password hidden).
void hide() {
state = false;
}
}
/// Confirm Password Visibility State Provider
///
/// Separate provider for confirm password field in registration forms.
/// This allows independent control of password and confirm password visibility.
///
/// Usage in registration page:
/// ```dart
/// final isConfirmPasswordVisible = ref.watch(confirmPasswordVisibilityProvider);
///
/// TextField(
/// obscureText: !isConfirmPasswordVisible,
/// decoration: InputDecoration(
/// labelText: 'Xác nhận mật khẩu',
/// suffixIcon: IconButton(
/// icon: Icon(
/// isConfirmPasswordVisible ? Icons.visibility : Icons.visibility_off,
/// ),
/// onPressed: () {
/// ref.read(confirmPasswordVisibilityProvider.notifier).toggle();
/// },
/// ),
/// ),
/// );
/// ```
@riverpod
class ConfirmPasswordVisibility extends _$ConfirmPasswordVisibility {
/// Initialize with password hidden (false)
@override
bool build() => false;
/// Toggle confirm password visibility
void toggle() {
state = !state;
}
/// Show confirm password
void show() {
state = true;
}
/// Hide confirm password
void hide() {
state = false;
}
}

View File

@@ -0,0 +1,329 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'password_visibility_provider.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
/// Password Visibility State Provider
///
/// Manages the visibility state of password input fields.
/// Default state is false (password hidden).
///
/// Usage in login/register pages:
/// ```dart
/// class LoginPage extends ConsumerWidget {
/// @override
/// Widget build(BuildContext context, WidgetRef ref) {
/// final isPasswordVisible = ref.watch(passwordVisibilityProvider);
///
/// return TextField(
/// obscureText: !isPasswordVisible,
/// decoration: InputDecoration(
/// suffixIcon: IconButton(
/// icon: Icon(
/// isPasswordVisible ? Icons.visibility : Icons.visibility_off,
/// ),
/// onPressed: () {
/// ref.read(passwordVisibilityProvider.notifier).toggle();
/// },
/// ),
/// ),
/// );
/// }
/// }
/// ```
@ProviderFor(PasswordVisibility)
const passwordVisibilityProvider = PasswordVisibilityProvider._();
/// Password Visibility State Provider
///
/// Manages the visibility state of password input fields.
/// Default state is false (password hidden).
///
/// Usage in login/register pages:
/// ```dart
/// class LoginPage extends ConsumerWidget {
/// @override
/// Widget build(BuildContext context, WidgetRef ref) {
/// final isPasswordVisible = ref.watch(passwordVisibilityProvider);
///
/// return TextField(
/// obscureText: !isPasswordVisible,
/// decoration: InputDecoration(
/// suffixIcon: IconButton(
/// icon: Icon(
/// isPasswordVisible ? Icons.visibility : Icons.visibility_off,
/// ),
/// onPressed: () {
/// ref.read(passwordVisibilityProvider.notifier).toggle();
/// },
/// ),
/// ),
/// );
/// }
/// }
/// ```
final class PasswordVisibilityProvider
extends $NotifierProvider<PasswordVisibility, bool> {
/// Password Visibility State Provider
///
/// Manages the visibility state of password input fields.
/// Default state is false (password hidden).
///
/// Usage in login/register pages:
/// ```dart
/// class LoginPage extends ConsumerWidget {
/// @override
/// Widget build(BuildContext context, WidgetRef ref) {
/// final isPasswordVisible = ref.watch(passwordVisibilityProvider);
///
/// return TextField(
/// obscureText: !isPasswordVisible,
/// decoration: InputDecoration(
/// suffixIcon: IconButton(
/// icon: Icon(
/// isPasswordVisible ? Icons.visibility : Icons.visibility_off,
/// ),
/// onPressed: () {
/// ref.read(passwordVisibilityProvider.notifier).toggle();
/// },
/// ),
/// ),
/// );
/// }
/// }
/// ```
const PasswordVisibilityProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'passwordVisibilityProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$passwordVisibilityHash();
@$internal
@override
PasswordVisibility create() => PasswordVisibility();
/// {@macro riverpod.override_with_value}
Override overrideWithValue(bool value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<bool>(value),
);
}
}
String _$passwordVisibilityHash() =>
r'25b6fa914e42dd83c8443aecbeb1d608cccd00ab';
/// Password Visibility State Provider
///
/// Manages the visibility state of password input fields.
/// Default state is false (password hidden).
///
/// Usage in login/register pages:
/// ```dart
/// class LoginPage extends ConsumerWidget {
/// @override
/// Widget build(BuildContext context, WidgetRef ref) {
/// final isPasswordVisible = ref.watch(passwordVisibilityProvider);
///
/// return TextField(
/// obscureText: !isPasswordVisible,
/// decoration: InputDecoration(
/// suffixIcon: IconButton(
/// icon: Icon(
/// isPasswordVisible ? Icons.visibility : Icons.visibility_off,
/// ),
/// onPressed: () {
/// ref.read(passwordVisibilityProvider.notifier).toggle();
/// },
/// ),
/// ),
/// );
/// }
/// }
/// ```
abstract class _$PasswordVisibility extends $Notifier<bool> {
bool build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<bool, bool>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<bool, bool>,
bool,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}
/// Confirm Password Visibility State Provider
///
/// Separate provider for confirm password field in registration forms.
/// This allows independent control of password and confirm password visibility.
///
/// Usage in registration page:
/// ```dart
/// final isConfirmPasswordVisible = ref.watch(confirmPasswordVisibilityProvider);
///
/// TextField(
/// obscureText: !isConfirmPasswordVisible,
/// decoration: InputDecoration(
/// labelText: 'Xác nhận mật khẩu',
/// suffixIcon: IconButton(
/// icon: Icon(
/// isConfirmPasswordVisible ? Icons.visibility : Icons.visibility_off,
/// ),
/// onPressed: () {
/// ref.read(confirmPasswordVisibilityProvider.notifier).toggle();
/// },
/// ),
/// ),
/// );
/// ```
@ProviderFor(ConfirmPasswordVisibility)
const confirmPasswordVisibilityProvider = ConfirmPasswordVisibilityProvider._();
/// Confirm Password Visibility State Provider
///
/// Separate provider for confirm password field in registration forms.
/// This allows independent control of password and confirm password visibility.
///
/// Usage in registration page:
/// ```dart
/// final isConfirmPasswordVisible = ref.watch(confirmPasswordVisibilityProvider);
///
/// TextField(
/// obscureText: !isConfirmPasswordVisible,
/// decoration: InputDecoration(
/// labelText: 'Xác nhận mật khẩu',
/// suffixIcon: IconButton(
/// icon: Icon(
/// isConfirmPasswordVisible ? Icons.visibility : Icons.visibility_off,
/// ),
/// onPressed: () {
/// ref.read(confirmPasswordVisibilityProvider.notifier).toggle();
/// },
/// ),
/// ),
/// );
/// ```
final class ConfirmPasswordVisibilityProvider
extends $NotifierProvider<ConfirmPasswordVisibility, bool> {
/// Confirm Password Visibility State Provider
///
/// Separate provider for confirm password field in registration forms.
/// This allows independent control of password and confirm password visibility.
///
/// Usage in registration page:
/// ```dart
/// final isConfirmPasswordVisible = ref.watch(confirmPasswordVisibilityProvider);
///
/// TextField(
/// obscureText: !isConfirmPasswordVisible,
/// decoration: InputDecoration(
/// labelText: 'Xác nhận mật khẩu',
/// suffixIcon: IconButton(
/// icon: Icon(
/// isConfirmPasswordVisible ? Icons.visibility : Icons.visibility_off,
/// ),
/// onPressed: () {
/// ref.read(confirmPasswordVisibilityProvider.notifier).toggle();
/// },
/// ),
/// ),
/// );
/// ```
const ConfirmPasswordVisibilityProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'confirmPasswordVisibilityProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$confirmPasswordVisibilityHash();
@$internal
@override
ConfirmPasswordVisibility create() => ConfirmPasswordVisibility();
/// {@macro riverpod.override_with_value}
Override overrideWithValue(bool value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<bool>(value),
);
}
}
String _$confirmPasswordVisibilityHash() =>
r'8408bba9db1e8deba425f98015a4e2fa76d75eb8';
/// Confirm Password Visibility State Provider
///
/// Separate provider for confirm password field in registration forms.
/// This allows independent control of password and confirm password visibility.
///
/// Usage in registration page:
/// ```dart
/// final isConfirmPasswordVisible = ref.watch(confirmPasswordVisibilityProvider);
///
/// TextField(
/// obscureText: !isConfirmPasswordVisible,
/// decoration: InputDecoration(
/// labelText: 'Xác nhận mật khẩu',
/// suffixIcon: IconButton(
/// icon: Icon(
/// isConfirmPasswordVisible ? Icons.visibility : Icons.visibility_off,
/// ),
/// onPressed: () {
/// ref.read(confirmPasswordVisibilityProvider.notifier).toggle();
/// },
/// ),
/// ),
/// );
/// ```
abstract class _$ConfirmPasswordVisibility extends $Notifier<bool> {
bool build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<bool, bool>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<bool, bool>,
bool,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}

View File

@@ -0,0 +1,305 @@
/// 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<String>? 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<String>? 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<User?> 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<void> 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<void>.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<void> 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;
}

View File

@@ -0,0 +1,251 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'register_provider.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
/// 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),
/// );
/// ```
@ProviderFor(Register)
const registerProvider = RegisterProvider._();
/// 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),
/// );
/// ```
final class RegisterProvider extends $AsyncNotifierProvider<Register, User?> {
/// 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),
/// );
/// ```
const RegisterProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'registerProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$registerHash();
@$internal
@override
Register create() => Register();
}
String _$registerHash() => r'a073b5c5958b74c63a3cddfec7f6f018e14a5088';
/// 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),
/// );
/// ```
abstract class _$Register extends $AsyncNotifier<User?> {
FutureOr<User?> build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<AsyncValue<User?>, User?>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<AsyncValue<User?>, User?>,
AsyncValue<User?>,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}
/// Convenience provider for checking if registration is in progress
///
/// Usage:
/// ```dart
/// final isRegistering = ref.watch(isRegisteringProvider);
/// if (isRegistering) {
/// // Show loading indicator
/// }
/// ```
@ProviderFor(isRegistering)
const isRegisteringProvider = IsRegisteringProvider._();
/// Convenience provider for checking if registration is in progress
///
/// Usage:
/// ```dart
/// final isRegistering = ref.watch(isRegisteringProvider);
/// if (isRegistering) {
/// // Show loading indicator
/// }
/// ```
final class IsRegisteringProvider extends $FunctionalProvider<bool, bool, bool>
with $Provider<bool> {
/// Convenience provider for checking if registration is in progress
///
/// Usage:
/// ```dart
/// final isRegistering = ref.watch(isRegisteringProvider);
/// if (isRegistering) {
/// // Show loading indicator
/// }
/// ```
const IsRegisteringProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'isRegisteringProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$isRegisteringHash();
@$internal
@override
$ProviderElement<bool> $createElement($ProviderPointer pointer) =>
$ProviderElement(pointer);
@override
bool create(Ref ref) {
return isRegistering(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(bool value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<bool>(value),
);
}
}
String _$isRegisteringHash() => r'2108b87b37451de9aaf799f9b8b380924bed2c87';
/// Convenience provider for checking if registration was successful
///
/// Usage:
/// ```dart
/// final success = ref.watch(registrationSuccessProvider);
/// if (success) {
/// // Navigate to pending approval or OTP screen
/// }
/// ```
@ProviderFor(registrationSuccess)
const registrationSuccessProvider = RegistrationSuccessProvider._();
/// Convenience provider for checking if registration was successful
///
/// Usage:
/// ```dart
/// final success = ref.watch(registrationSuccessProvider);
/// if (success) {
/// // Navigate to pending approval or OTP screen
/// }
/// ```
final class RegistrationSuccessProvider
extends $FunctionalProvider<bool, bool, bool>
with $Provider<bool> {
/// Convenience provider for checking if registration was successful
///
/// Usage:
/// ```dart
/// final success = ref.watch(registrationSuccessProvider);
/// if (success) {
/// // Navigate to pending approval or OTP screen
/// }
/// ```
const RegistrationSuccessProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'registrationSuccessProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$registrationSuccessHash();
@$internal
@override
$ProviderElement<bool> $createElement($ProviderPointer pointer) =>
$ProviderElement(pointer);
@override
bool create(Ref ref) {
return registrationSuccess(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(bool value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<bool>(value),
);
}
}
String _$registrationSuccessHash() =>
r'6435b9ca4bf4c287497a39077a5d4558e0515ddc';

View File

@@ -0,0 +1,175 @@
/// Selected Role State Provider
///
/// Manages the selected user role during registration.
/// Simple state provider for role selection in the registration form.
///
/// 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 'selected_role_provider.g.dart';
/// Selected Role Provider
///
/// Manages the currently selected user role in the registration form.
/// Provides methods to select and clear role selection.
///
/// This provider is used to:
/// - Track which role the user has selected
/// - Conditionally show/hide verification fields based on role
/// - Validate required documents for dealer/worker roles
///
/// Usage in widgets:
/// ```dart
/// // Watch the selected role
/// final selectedRole = ref.watch(selectedRoleProvider);
///
/// // Select a role
/// ref.read(selectedRoleProvider.notifier).selectRole(UserRole.customer);
///
/// // Clear selection
/// ref.read(selectedRoleProvider.notifier).clearRole();
///
/// // Show verification section conditionally
/// if (selectedRole == UserRole.customer) {
/// VerificationSection(),
/// }
/// ```
@riverpod
class SelectedRole extends _$SelectedRole {
/// Initialize with no role selected
@override
UserRole? build() {
return null;
}
/// Select a user role
///
/// Updates the state with the newly selected role.
/// This triggers UI updates that depend on role selection.
///
/// Parameters:
/// - [role]: The user role to select
///
/// Example:
/// ```dart
/// // User selects "Đại lý hệ thống" (dealer)
/// ref.read(selectedRoleProvider.notifier).selectRole(UserRole.customer);
/// // This will show verification fields
/// ```
void selectRole(UserRole role) {
state = role;
}
/// Clear the role selection
///
/// Resets the state to null (no role selected).
/// Useful when resetting the form or canceling registration.
///
/// Example:
/// ```dart
/// // User clicks "Cancel" or goes back
/// ref.read(selectedRoleProvider.notifier).clearRole();
/// // This will hide verification fields
/// ```
void clearRole() {
state = null;
}
/// Check if a role is currently selected
///
/// Returns true if any role has been selected, false otherwise.
bool get hasSelection => state != null;
/// Check if verification is required for current role
///
/// Returns true if the selected role requires verification documents
/// (CCCD, certificates, etc.). Currently only customer role requires this.
///
/// This is used to conditionally show the verification section:
/// ```dart
/// if (ref.read(selectedRoleProvider.notifier).requiresVerification) {
/// // Show CCCD input, file uploads, etc.
/// }
/// ```
bool get requiresVerification => state == UserRole.customer;
/// Get the display name for the current role (Vietnamese)
///
/// Returns a user-friendly Vietnamese name for the selected role,
/// or null if no role is selected.
///
/// Example:
/// ```dart
/// final displayName = ref.read(selectedRoleProvider.notifier).displayName;
/// // Returns: "Đại lý hệ thống" for customer role
/// ```
String? get displayName {
if (state == null) return null;
switch (state!) {
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ế';
}
}
}
/// Convenience provider for checking if verification is required
///
/// Returns true if the currently selected role requires verification
/// documents (CCCD, certificates, etc.).
///
/// Usage:
/// ```dart
/// final needsVerification = ref.watch(requiresVerificationProvider);
/// if (needsVerification) {
/// // Show verification section with file uploads
/// }
/// ```
@riverpod
bool requiresVerification(Ref ref) {
final selectedRole = ref.watch(selectedRoleProvider);
return selectedRole == UserRole.customer;
}
/// Convenience provider for getting role display name
///
/// Returns a user-friendly Vietnamese name for the selected role,
/// or null if no role is selected.
///
/// Usage:
/// ```dart
/// final roleName = ref.watch(roleDisplayNameProvider);
/// if (roleName != null) {
/// Text('Bạn đang đăng ký với vai trò: $roleName');
/// }
/// ```
@riverpod
String? roleDisplayName(Ref ref) {
final selectedRole = ref.watch(selectedRoleProvider);
if (selectedRole == null) return null;
switch (selectedRole) {
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ế';
}
}

View File

@@ -0,0 +1,327 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'selected_role_provider.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
/// Selected Role Provider
///
/// Manages the currently selected user role in the registration form.
/// Provides methods to select and clear role selection.
///
/// This provider is used to:
/// - Track which role the user has selected
/// - Conditionally show/hide verification fields based on role
/// - Validate required documents for dealer/worker roles
///
/// Usage in widgets:
/// ```dart
/// // Watch the selected role
/// final selectedRole = ref.watch(selectedRoleProvider);
///
/// // Select a role
/// ref.read(selectedRoleProvider.notifier).selectRole(UserRole.customer);
///
/// // Clear selection
/// ref.read(selectedRoleProvider.notifier).clearRole();
///
/// // Show verification section conditionally
/// if (selectedRole == UserRole.customer) {
/// VerificationSection(),
/// }
/// ```
@ProviderFor(SelectedRole)
const selectedRoleProvider = SelectedRoleProvider._();
/// Selected Role Provider
///
/// Manages the currently selected user role in the registration form.
/// Provides methods to select and clear role selection.
///
/// This provider is used to:
/// - Track which role the user has selected
/// - Conditionally show/hide verification fields based on role
/// - Validate required documents for dealer/worker roles
///
/// Usage in widgets:
/// ```dart
/// // Watch the selected role
/// final selectedRole = ref.watch(selectedRoleProvider);
///
/// // Select a role
/// ref.read(selectedRoleProvider.notifier).selectRole(UserRole.customer);
///
/// // Clear selection
/// ref.read(selectedRoleProvider.notifier).clearRole();
///
/// // Show verification section conditionally
/// if (selectedRole == UserRole.customer) {
/// VerificationSection(),
/// }
/// ```
final class SelectedRoleProvider
extends $NotifierProvider<SelectedRole, UserRole?> {
/// Selected Role Provider
///
/// Manages the currently selected user role in the registration form.
/// Provides methods to select and clear role selection.
///
/// This provider is used to:
/// - Track which role the user has selected
/// - Conditionally show/hide verification fields based on role
/// - Validate required documents for dealer/worker roles
///
/// Usage in widgets:
/// ```dart
/// // Watch the selected role
/// final selectedRole = ref.watch(selectedRoleProvider);
///
/// // Select a role
/// ref.read(selectedRoleProvider.notifier).selectRole(UserRole.customer);
///
/// // Clear selection
/// ref.read(selectedRoleProvider.notifier).clearRole();
///
/// // Show verification section conditionally
/// if (selectedRole == UserRole.customer) {
/// VerificationSection(),
/// }
/// ```
const SelectedRoleProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'selectedRoleProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$selectedRoleHash();
@$internal
@override
SelectedRole create() => SelectedRole();
/// {@macro riverpod.override_with_value}
Override overrideWithValue(UserRole? value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<UserRole?>(value),
);
}
}
String _$selectedRoleHash() => r'098c7fdaec4694d14a48c049556960eb6ed2dc06';
/// Selected Role Provider
///
/// Manages the currently selected user role in the registration form.
/// Provides methods to select and clear role selection.
///
/// This provider is used to:
/// - Track which role the user has selected
/// - Conditionally show/hide verification fields based on role
/// - Validate required documents for dealer/worker roles
///
/// Usage in widgets:
/// ```dart
/// // Watch the selected role
/// final selectedRole = ref.watch(selectedRoleProvider);
///
/// // Select a role
/// ref.read(selectedRoleProvider.notifier).selectRole(UserRole.customer);
///
/// // Clear selection
/// ref.read(selectedRoleProvider.notifier).clearRole();
///
/// // Show verification section conditionally
/// if (selectedRole == UserRole.customer) {
/// VerificationSection(),
/// }
/// ```
abstract class _$SelectedRole extends $Notifier<UserRole?> {
UserRole? build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<UserRole?, UserRole?>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<UserRole?, UserRole?>,
UserRole?,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}
/// Convenience provider for checking if verification is required
///
/// Returns true if the currently selected role requires verification
/// documents (CCCD, certificates, etc.).
///
/// Usage:
/// ```dart
/// final needsVerification = ref.watch(requiresVerificationProvider);
/// if (needsVerification) {
/// // Show verification section with file uploads
/// }
/// ```
@ProviderFor(requiresVerification)
const requiresVerificationProvider = RequiresVerificationProvider._();
/// Convenience provider for checking if verification is required
///
/// Returns true if the currently selected role requires verification
/// documents (CCCD, certificates, etc.).
///
/// Usage:
/// ```dart
/// final needsVerification = ref.watch(requiresVerificationProvider);
/// if (needsVerification) {
/// // Show verification section with file uploads
/// }
/// ```
final class RequiresVerificationProvider
extends $FunctionalProvider<bool, bool, bool>
with $Provider<bool> {
/// Convenience provider for checking if verification is required
///
/// Returns true if the currently selected role requires verification
/// documents (CCCD, certificates, etc.).
///
/// Usage:
/// ```dart
/// final needsVerification = ref.watch(requiresVerificationProvider);
/// if (needsVerification) {
/// // Show verification section with file uploads
/// }
/// ```
const RequiresVerificationProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'requiresVerificationProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$requiresVerificationHash();
@$internal
@override
$ProviderElement<bool> $createElement($ProviderPointer pointer) =>
$ProviderElement(pointer);
@override
bool create(Ref ref) {
return requiresVerification(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(bool value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<bool>(value),
);
}
}
String _$requiresVerificationHash() =>
r'400b4242bca2defd14e46361d2b77dd94a4e3e5e';
/// Convenience provider for getting role display name
///
/// Returns a user-friendly Vietnamese name for the selected role,
/// or null if no role is selected.
///
/// Usage:
/// ```dart
/// final roleName = ref.watch(roleDisplayNameProvider);
/// if (roleName != null) {
/// Text('Bạn đang đăng ký với vai trò: $roleName');
/// }
/// ```
@ProviderFor(roleDisplayName)
const roleDisplayNameProvider = RoleDisplayNameProvider._();
/// Convenience provider for getting role display name
///
/// Returns a user-friendly Vietnamese name for the selected role,
/// or null if no role is selected.
///
/// Usage:
/// ```dart
/// final roleName = ref.watch(roleDisplayNameProvider);
/// if (roleName != null) {
/// Text('Bạn đang đăng ký với vai trò: $roleName');
/// }
/// ```
final class RoleDisplayNameProvider
extends $FunctionalProvider<String?, String?, String?>
with $Provider<String?> {
/// Convenience provider for getting role display name
///
/// Returns a user-friendly Vietnamese name for the selected role,
/// or null if no role is selected.
///
/// Usage:
/// ```dart
/// final roleName = ref.watch(roleDisplayNameProvider);
/// if (roleName != null) {
/// Text('Bạn đang đăng ký với vai trò: $roleName');
/// }
/// ```
const RoleDisplayNameProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'roleDisplayNameProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$roleDisplayNameHash();
@$internal
@override
$ProviderElement<String?> $createElement($ProviderPointer pointer) =>
$ProviderElement(pointer);
@override
String? create(Ref ref) {
return roleDisplayName(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(String? value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<String?>(value),
);
}
}
String _$roleDisplayNameHash() => r'6cb4bfd9e76fb2f3ed52d4a249e5a2477bc6f39e';