update address

This commit is contained in:
Phuoc Nguyen
2025-11-18 17:04:00 +07:00
parent a5eb95fa64
commit 0dda402246
33 changed files with 4250 additions and 232 deletions

View File

@@ -0,0 +1,159 @@
/// Address Repository Implementation
///
/// Implements address repository with online-only API calls.
library;
import 'package:worker/features/account/data/datasources/address_remote_datasource.dart';
import 'package:worker/features/account/data/models/address_model.dart';
import 'package:worker/features/account/domain/entities/address.dart';
import 'package:worker/features/account/domain/repositories/address_repository.dart';
/// Address Repository Implementation
///
/// Online-only implementation - all operations go directly to API.
/// No local caching or offline support.
class AddressRepositoryImpl implements AddressRepository {
final AddressRemoteDataSource _remoteDataSource;
AddressRepositoryImpl({
required AddressRemoteDataSource remoteDataSource,
}) : _remoteDataSource = remoteDataSource;
@override
Future<List<Address>> getAddresses({bool? isDefault}) async {
_debugPrint('Getting addresses...');
try {
final addressModels = await _remoteDataSource.getAddresses(
isDefault: isDefault,
);
final addresses = addressModels.map((model) => model.toEntity()).toList();
_debugPrint('Retrieved ${addresses.length} addresses');
return addresses;
} catch (e) {
_debugPrint('Error getting addresses: $e');
rethrow;
}
}
@override
Future<Address> createAddress(Address address) async {
_debugPrint('Creating address: ${address.addressTitle}');
try {
// Create model with empty name (API will generate)
final addressModel = AddressModel.fromEntity(address).copyWith(
name: '', // Empty name indicates creation
);
final savedModel = await _remoteDataSource.saveAddress(addressModel);
_debugPrint('Address created: ${savedModel.name}');
return savedModel.toEntity();
} catch (e) {
_debugPrint('Error creating address: $e');
rethrow;
}
}
@override
Future<Address> updateAddress(Address address) async {
_debugPrint('Updating address: ${address.name}');
try {
final addressModel = AddressModel.fromEntity(address);
final savedModel = await _remoteDataSource.saveAddress(addressModel);
_debugPrint('Address updated: ${savedModel.name}');
return savedModel.toEntity();
} catch (e) {
_debugPrint('Error updating address: $e');
rethrow;
}
}
@override
Future<void> deleteAddress(String name) async {
_debugPrint('Deleting address: $name');
try {
await _remoteDataSource.deleteAddress(name);
_debugPrint('Address deleted: $name');
} catch (e) {
_debugPrint('Error deleting address: $e');
rethrow;
}
}
@override
Future<void> setDefaultAddress(String name) async {
_debugPrint('Setting default address: $name');
try {
// Get all addresses
final addresses = await getAddresses();
// Find the address to set as default
final targetAddress = addresses.firstWhere(
(addr) => addr.name == name,
orElse: () => throw Exception('Address not found: $name'),
);
// Update the target address to be default
await updateAddress(targetAddress.copyWith(isDefault: true));
// Update other addresses to not be default
for (final addr in addresses) {
if (addr.name != name && addr.isDefault) {
await updateAddress(addr.copyWith(isDefault: false));
}
}
_debugPrint('Default address set: $name');
} catch (e) {
_debugPrint('Error setting default address: $e');
rethrow;
}
}
/// Debug print helper
void _debugPrint(String message) {
// ignore: avoid_print
print('[AddressRepository] $message');
}
}
/// Extension to create a copy with modifications (since AddressModel is not freezed)
extension _AddressModelCopyWith on AddressModel {
AddressModel copyWith({
String? name,
String? addressTitle,
String? addressLine1,
String? phone,
String? email,
String? fax,
String? taxCode,
String? cityCode,
String? wardCode,
bool? isDefault,
String? cityName,
String? wardName,
}) {
return AddressModel(
name: name ?? this.name,
addressTitle: addressTitle ?? this.addressTitle,
addressLine1: addressLine1 ?? this.addressLine1,
phone: phone ?? this.phone,
email: email ?? this.email,
fax: fax ?? this.fax,
taxCode: taxCode ?? this.taxCode,
cityCode: cityCode ?? this.cityCode,
wardCode: wardCode ?? this.wardCode,
isDefault: isDefault ?? this.isDefault,
cityName: cityName ?? this.cityName,
wardName: wardName ?? this.wardName,
);
}
}

View File

@@ -0,0 +1,96 @@
/// Location Repository Implementation
///
/// Implements location repository with offline-first strategy.
library;
import 'package:worker/features/account/data/datasources/location_local_datasource.dart';
import 'package:worker/features/account/data/datasources/location_remote_datasource.dart';
import 'package:worker/features/account/domain/entities/city.dart';
import 'package:worker/features/account/domain/entities/ward.dart';
import 'package:worker/features/account/domain/repositories/location_repository.dart';
/// Location Repository Implementation
///
/// Offline-first implementation:
/// - Cities: Cache in Hive, fetch from API if cache is empty or force refresh
/// - Wards: Cache per city, fetch from API if not cached or force refresh
class LocationRepositoryImpl implements LocationRepository {
final LocationRemoteDataSource _remoteDataSource;
final LocationLocalDataSource _localDataSource;
LocationRepositoryImpl({
required LocationRemoteDataSource remoteDataSource,
required LocationLocalDataSource localDataSource,
}) : _remoteDataSource = remoteDataSource,
_localDataSource = localDataSource;
@override
Future<List<City>> getCities({bool forceRefresh = false}) async {
try {
// Check cache first (offline-first)
if (!forceRefresh && _localDataSource.hasCities()) {
final cachedCities = _localDataSource.getCities();
if (cachedCities.isNotEmpty) {
return cachedCities.map((model) => model.toEntity()).toList();
}
}
// Fetch from API
final cityModels = await _remoteDataSource.getCities();
// Save to cache
await _localDataSource.saveCities(cityModels);
return cityModels.map((model) => model.toEntity()).toList();
} catch (e) {
// Fallback to cache on error
if (!forceRefresh) {
final cachedCities = _localDataSource.getCities();
if (cachedCities.isNotEmpty) {
return cachedCities.map((model) => model.toEntity()).toList();
}
}
rethrow;
}
}
@override
Future<List<Ward>> getWards(
String cityCode, {
bool forceRefresh = false,
}) async {
try {
// Check cache first (offline-first)
if (!forceRefresh && _localDataSource.hasWards(cityCode)) {
final cachedWards = _localDataSource.getWards(cityCode);
if (cachedWards.isNotEmpty) {
return cachedWards.map((model) => model.toEntity()).toList();
}
}
// Fetch from API
final wardModels = await _remoteDataSource.getWards(cityCode);
// Save to cache
await _localDataSource.saveWards(cityCode, wardModels);
return wardModels.map((model) => model.toEntity()).toList();
} catch (e) {
// Fallback to cache on error
if (!forceRefresh) {
final cachedWards = _localDataSource.getWards(cityCode);
if (cachedWards.isNotEmpty) {
return cachedWards.map((model) => model.toEntity()).toList();
}
}
rethrow;
}
}
@override
Future<void> clearCache() async {
await _localDataSource.clearAll();
}
}