add auth register

This commit is contained in:
Phuoc Nguyen
2025-11-07 15:55:02 +07:00
parent ce7396f729
commit 9e55983d82
16 changed files with 2376 additions and 110 deletions

View File

@@ -0,0 +1,62 @@
/// Cities Provider
///
/// Manages the list of cities/provinces for address selection
library;
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:worker/features/auth/data/datasources/auth_remote_datasource.dart';
import 'package:worker/features/auth/domain/entities/city.dart';
import 'package:worker/features/auth/presentation/providers/session_provider.dart';
part 'cities_provider.g.dart';
/// Cities Provider
///
/// Fetches list of cities from API for registration form.
/// Requires active session (CSRF token and SID).
/// keepAlive: true ensures the cities list persists and doesn't auto-dispose.
@Riverpod(keepAlive: true)
class Cities extends _$Cities {
@override
Future<List<City>> build() async {
// Don't auto-fetch on build, wait for manual call
return [];
}
/// Fetch cities from API
Future<void> fetchCities() async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() async {
final sessionState = ref.read(sessionProvider);
if (!sessionState.hasSession) {
throw Exception('No active session. Please get session first.');
}
final dataSource = ref.read(authRemoteDataSourceProvider);
return await dataSource.getCities(
csrfToken: sessionState.csrfToken!,
sid: sessionState.sid!,
);
});
}
/// Refresh cities list
Future<void> refresh() async {
await fetchCities();
}
}
/// Provider to get a specific city by code
@riverpod
City? cityByCode(Ref ref, String code) {
final citiesAsync = ref.watch(citiesProvider);
return citiesAsync.whenOrNull(
data: (List<City> cities) => cities.firstWhere(
(City city) => city.code == code,
orElse: () => cities.first,
),
);
}

View File

@@ -0,0 +1,160 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'cities_provider.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
/// Cities Provider
///
/// Fetches list of cities from API for registration form.
/// Requires active session (CSRF token and SID).
/// keepAlive: true ensures the cities list persists and doesn't auto-dispose.
@ProviderFor(Cities)
const citiesProvider = CitiesProvider._();
/// Cities Provider
///
/// Fetches list of cities from API for registration form.
/// Requires active session (CSRF token and SID).
/// keepAlive: true ensures the cities list persists and doesn't auto-dispose.
final class CitiesProvider extends $AsyncNotifierProvider<Cities, List<City>> {
/// Cities Provider
///
/// Fetches list of cities from API for registration form.
/// Requires active session (CSRF token and SID).
/// keepAlive: true ensures the cities list persists and doesn't auto-dispose.
const CitiesProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'citiesProvider',
isAutoDispose: false,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$citiesHash();
@$internal
@override
Cities create() => Cities();
}
String _$citiesHash() => r'0de4a7d44e576d74ecd875ddad46d6cb52a38bf8';
/// Cities Provider
///
/// Fetches list of cities from API for registration form.
/// Requires active session (CSRF token and SID).
/// keepAlive: true ensures the cities list persists and doesn't auto-dispose.
abstract class _$Cities extends $AsyncNotifier<List<City>> {
FutureOr<List<City>> build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<AsyncValue<List<City>>, List<City>>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<AsyncValue<List<City>>, List<City>>,
AsyncValue<List<City>>,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}
/// Provider to get a specific city by code
@ProviderFor(cityByCode)
const cityByCodeProvider = CityByCodeFamily._();
/// Provider to get a specific city by code
final class CityByCodeProvider extends $FunctionalProvider<City?, City?, City?>
with $Provider<City?> {
/// Provider to get a specific city by code
const CityByCodeProvider._({
required CityByCodeFamily super.from,
required String super.argument,
}) : super(
retry: null,
name: r'cityByCodeProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$cityByCodeHash();
@override
String toString() {
return r'cityByCodeProvider'
''
'($argument)';
}
@$internal
@override
$ProviderElement<City?> $createElement($ProviderPointer pointer) =>
$ProviderElement(pointer);
@override
City? create(Ref ref) {
final argument = this.argument as String;
return cityByCode(ref, argument);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(City? value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<City?>(value),
);
}
@override
bool operator ==(Object other) {
return other is CityByCodeProvider && other.argument == argument;
}
@override
int get hashCode {
return argument.hashCode;
}
}
String _$cityByCodeHash() => r'4e4a9a72526b0a366b8697244f2e7e2aedf7cabe';
/// Provider to get a specific city by code
final class CityByCodeFamily extends $Family
with $FunctionalFamilyOverride<City?, String> {
const CityByCodeFamily._()
: super(
retry: null,
name: r'cityByCodeProvider',
dependencies: null,
$allTransitiveDependencies: null,
isAutoDispose: true,
);
/// Provider to get a specific city by code
CityByCodeProvider call(String code) =>
CityByCodeProvider._(argument: code, from: this);
@override
String toString() => r'cityByCodeProvider';
}

View File

@@ -0,0 +1,65 @@
/// Customer Groups Provider
///
/// Manages the list of customer groups (roles) for user registration
library;
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:worker/features/auth/data/datasources/auth_remote_datasource.dart';
import 'package:worker/features/auth/domain/entities/customer_group.dart';
import 'package:worker/features/auth/presentation/providers/session_provider.dart';
part 'customer_groups_provider.g.dart';
/// Customer Groups Provider
///
/// Fetches list of customer groups (roles) from API for registration form.
/// Requires active session (CSRF token and SID).
/// keepAlive: true ensures the customer groups list persists and doesn't auto-dispose.
@Riverpod(keepAlive: true)
class CustomerGroups extends _$CustomerGroups {
@override
Future<List<CustomerGroup>> build() async {
// Don't auto-fetch on build, wait for manual call
return [];
}
/// Fetch customer groups from API
Future<void> fetchCustomerGroups() async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() async {
final sessionState = ref.read(sessionProvider);
if (!sessionState.hasSession) {
throw Exception('No active session. Please get session first.');
}
final dataSource = ref.read(authRemoteDataSourceProvider);
return await dataSource.getCustomerGroups(
csrfToken: sessionState.csrfToken!,
sid: sessionState.sid!,
);
});
}
/// Refresh customer groups list
Future<void> refresh() async {
await fetchCustomerGroups();
}
}
/// Provider to get a specific customer group by code/value
@riverpod
CustomerGroup? customerGroupByCode(
Ref ref,
String code,
) {
final groupsAsync = ref.watch(customerGroupsProvider);
return groupsAsync.whenOrNull(
data: (List<CustomerGroup> groups) => groups.firstWhere(
(CustomerGroup group) => group.value == code || group.name == code,
orElse: () => groups.first,
),
);
}

View File

@@ -0,0 +1,164 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'customer_groups_provider.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
/// Customer Groups Provider
///
/// Fetches list of customer groups (roles) from API for registration form.
/// Requires active session (CSRF token and SID).
/// keepAlive: true ensures the customer groups list persists and doesn't auto-dispose.
@ProviderFor(CustomerGroups)
const customerGroupsProvider = CustomerGroupsProvider._();
/// Customer Groups Provider
///
/// Fetches list of customer groups (roles) from API for registration form.
/// Requires active session (CSRF token and SID).
/// keepAlive: true ensures the customer groups list persists and doesn't auto-dispose.
final class CustomerGroupsProvider
extends $AsyncNotifierProvider<CustomerGroups, List<CustomerGroup>> {
/// Customer Groups Provider
///
/// Fetches list of customer groups (roles) from API for registration form.
/// Requires active session (CSRF token and SID).
/// keepAlive: true ensures the customer groups list persists and doesn't auto-dispose.
const CustomerGroupsProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'customerGroupsProvider',
isAutoDispose: false,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$customerGroupsHash();
@$internal
@override
CustomerGroups create() => CustomerGroups();
}
String _$customerGroupsHash() => r'df9107ef844e3cd320804af8d5dcf2fee2462208';
/// Customer Groups Provider
///
/// Fetches list of customer groups (roles) from API for registration form.
/// Requires active session (CSRF token and SID).
/// keepAlive: true ensures the customer groups list persists and doesn't auto-dispose.
abstract class _$CustomerGroups extends $AsyncNotifier<List<CustomerGroup>> {
FutureOr<List<CustomerGroup>> build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref =
this.ref as $Ref<AsyncValue<List<CustomerGroup>>, List<CustomerGroup>>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<AsyncValue<List<CustomerGroup>>, List<CustomerGroup>>,
AsyncValue<List<CustomerGroup>>,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}
/// Provider to get a specific customer group by code/value
@ProviderFor(customerGroupByCode)
const customerGroupByCodeProvider = CustomerGroupByCodeFamily._();
/// Provider to get a specific customer group by code/value
final class CustomerGroupByCodeProvider
extends $FunctionalProvider<CustomerGroup?, CustomerGroup?, CustomerGroup?>
with $Provider<CustomerGroup?> {
/// Provider to get a specific customer group by code/value
const CustomerGroupByCodeProvider._({
required CustomerGroupByCodeFamily super.from,
required String super.argument,
}) : super(
retry: null,
name: r'customerGroupByCodeProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$customerGroupByCodeHash();
@override
String toString() {
return r'customerGroupByCodeProvider'
''
'($argument)';
}
@$internal
@override
$ProviderElement<CustomerGroup?> $createElement($ProviderPointer pointer) =>
$ProviderElement(pointer);
@override
CustomerGroup? create(Ref ref) {
final argument = this.argument as String;
return customerGroupByCode(ref, argument);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(CustomerGroup? value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<CustomerGroup?>(value),
);
}
@override
bool operator ==(Object other) {
return other is CustomerGroupByCodeProvider && other.argument == argument;
}
@override
int get hashCode {
return argument.hashCode;
}
}
String _$customerGroupByCodeHash() =>
r'0ddf96a19d7c20c9fe3ddfd2af80ac49bfe76512';
/// Provider to get a specific customer group by code/value
final class CustomerGroupByCodeFamily extends $Family
with $FunctionalFamilyOverride<CustomerGroup?, String> {
const CustomerGroupByCodeFamily._()
: super(
retry: null,
name: r'customerGroupByCodeProvider',
dependencies: null,
$allTransitiveDependencies: null,
isAutoDispose: true,
);
/// Provider to get a specific customer group by code/value
CustomerGroupByCodeProvider call(String code) =>
CustomerGroupByCodeProvider._(argument: code, from: this);
@override
String toString() => r'customerGroupByCodeProvider';
}

View File

@@ -0,0 +1,107 @@
/// Session Provider
///
/// Manages authentication session (SID and CSRF token)
library;
import 'package:curl_logger_dio_interceptor/curl_logger_dio_interceptor.dart';
import 'package:dio/dio.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:worker/features/auth/data/datasources/auth_remote_datasource.dart';
import 'package:worker/features/auth/data/models/auth_session_model.dart';
part 'session_provider.g.dart';
/// Provider for Dio instance
@Riverpod(keepAlive: true)
Dio dio(Ref ref) {
final dio = Dio(
BaseOptions(
baseUrl: 'https://land.dbiz.com',
connectTimeout: const Duration(seconds: 30),
receiveTimeout: const Duration(seconds: 30),
),
);
// Add curl logger interceptor for debugging
dio.interceptors.add(CurlLoggerDioInterceptor(printOnSuccess: true));
return dio;
}
/// Provider for AuthRemoteDataSource
@Riverpod(keepAlive: true)
AuthRemoteDataSource authRemoteDataSource(Ref ref) {
final dio = ref.watch(dioProvider);
return AuthRemoteDataSource(dio);
}
/// Session State
class SessionState {
final String? sid;
final String? csrfToken;
final bool isLoading;
final String? error;
const SessionState({
this.sid,
this.csrfToken,
this.isLoading = false,
this.error,
});
SessionState copyWith({
String? sid,
String? csrfToken,
bool? isLoading,
String? error,
}) {
return SessionState(
sid: sid ?? this.sid,
csrfToken: csrfToken ?? this.csrfToken,
isLoading: isLoading ?? this.isLoading,
error: error ?? this.error,
);
}
bool get hasSession => sid != null && csrfToken != null;
}
/// Session Provider
///
/// Manages the authentication session including SID and CSRF token.
/// This should be called before making any authenticated requests.
/// keepAlive: true ensures the session persists across the app lifecycle.
@Riverpod(keepAlive: true)
class Session extends _$Session {
@override
SessionState build() {
return const SessionState();
}
/// Get session from API
Future<void> getSession() async {
state = state.copyWith(isLoading: true, error: null);
try {
final dataSource = ref.read(authRemoteDataSourceProvider);
final response = await dataSource.getSession();
state = SessionState(
sid: response.message.data.sid,
csrfToken: response.message.data.csrfToken,
isLoading: false,
);
} catch (e) {
state = SessionState(
isLoading: false,
error: e.toString(),
);
rethrow;
}
}
/// Clear session
void clearSession() {
state = const SessionState();
}
}

View File

@@ -0,0 +1,181 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'session_provider.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
/// Provider for Dio instance
@ProviderFor(dio)
const dioProvider = DioProvider._();
/// Provider for Dio instance
final class DioProvider extends $FunctionalProvider<Dio, Dio, Dio>
with $Provider<Dio> {
/// Provider for Dio instance
const DioProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'dioProvider',
isAutoDispose: false,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$dioHash();
@$internal
@override
$ProviderElement<Dio> $createElement($ProviderPointer pointer) =>
$ProviderElement(pointer);
@override
Dio create(Ref ref) {
return dio(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(Dio value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<Dio>(value),
);
}
}
String _$dioHash() => r'2bc10725a1b646cfaabd88c722e5101c06837c75';
/// Provider for AuthRemoteDataSource
@ProviderFor(authRemoteDataSource)
const authRemoteDataSourceProvider = AuthRemoteDataSourceProvider._();
/// Provider for AuthRemoteDataSource
final class AuthRemoteDataSourceProvider
extends
$FunctionalProvider<
AuthRemoteDataSource,
AuthRemoteDataSource,
AuthRemoteDataSource
>
with $Provider<AuthRemoteDataSource> {
/// Provider for AuthRemoteDataSource
const AuthRemoteDataSourceProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'authRemoteDataSourceProvider',
isAutoDispose: false,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$authRemoteDataSourceHash();
@$internal
@override
$ProviderElement<AuthRemoteDataSource> $createElement(
$ProviderPointer pointer,
) => $ProviderElement(pointer);
@override
AuthRemoteDataSource create(Ref ref) {
return authRemoteDataSource(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(AuthRemoteDataSource value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<AuthRemoteDataSource>(value),
);
}
}
String _$authRemoteDataSourceHash() =>
r'6a18a0d3fee86c512b6eec01b8e8fda53a307a4c';
/// Session Provider
///
/// Manages the authentication session including SID and CSRF token.
/// This should be called before making any authenticated requests.
/// keepAlive: true ensures the session persists across the app lifecycle.
@ProviderFor(Session)
const sessionProvider = SessionProvider._();
/// Session Provider
///
/// Manages the authentication session including SID and CSRF token.
/// This should be called before making any authenticated requests.
/// keepAlive: true ensures the session persists across the app lifecycle.
final class SessionProvider extends $NotifierProvider<Session, SessionState> {
/// Session Provider
///
/// Manages the authentication session including SID and CSRF token.
/// This should be called before making any authenticated requests.
/// keepAlive: true ensures the session persists across the app lifecycle.
const SessionProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'sessionProvider',
isAutoDispose: false,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$sessionHash();
@$internal
@override
Session create() => Session();
/// {@macro riverpod.override_with_value}
Override overrideWithValue(SessionState value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<SessionState>(value),
);
}
}
String _$sessionHash() => r'9c755f010681d87ab3898c4daaa920501104df46';
/// Session Provider
///
/// Manages the authentication session including SID and CSRF token.
/// This should be called before making any authenticated requests.
/// keepAlive: true ensures the session persists across the app lifecycle.
abstract class _$Session extends $Notifier<SessionState> {
SessionState build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<SessionState, SessionState>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<SessionState, SessionState>,
SessionState,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}