add dropdown
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
import 'package:hive_ce/hive.dart';
|
||||
|
||||
import '../models/user_model.dart';
|
||||
|
||||
/// Abstract interface for users local data source
|
||||
abstract class UsersLocalDataSource {
|
||||
/// Get all users from local storage
|
||||
Future<List<UserModel>> getUsers();
|
||||
|
||||
/// Save users to local storage
|
||||
Future<void> saveUsers(List<UserModel> users);
|
||||
|
||||
/// Clear all users from local storage
|
||||
Future<void> clearUsers();
|
||||
}
|
||||
|
||||
/// Implementation of UsersLocalDataSource using Hive
|
||||
class UsersLocalDataSourceImpl implements UsersLocalDataSource {
|
||||
static const String _boxName = 'users';
|
||||
|
||||
Future<Box<UserModel>> get _box async {
|
||||
if (!Hive.isBoxOpen(_boxName)) {
|
||||
return await Hive.openBox<UserModel>(_boxName);
|
||||
}
|
||||
return Hive.box<UserModel>(_boxName);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<UserModel>> getUsers() async {
|
||||
try {
|
||||
final box = await _box;
|
||||
return box.values.toList();
|
||||
} catch (e) {
|
||||
throw Exception('Failed to get users from local storage: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveUsers(List<UserModel> users) async {
|
||||
try {
|
||||
final box = await _box;
|
||||
await box.clear();
|
||||
|
||||
// Save users with their ID as key
|
||||
for (final user in users) {
|
||||
await box.put(user.id, user);
|
||||
}
|
||||
} catch (e) {
|
||||
throw Exception('Failed to save users to local storage: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> clearUsers() async {
|
||||
try {
|
||||
final box = await _box;
|
||||
await box.clear();
|
||||
} catch (e) {
|
||||
throw Exception('Failed to clear users from local storage: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import '../../../../core/constants/api_endpoints.dart';
|
||||
import '../../../../core/errors/exceptions.dart';
|
||||
import '../../../../core/network/api_client.dart';
|
||||
import '../../../../core/network/api_response.dart';
|
||||
import '../models/user_model.dart';
|
||||
|
||||
/// Abstract interface for users remote data source
|
||||
abstract class UsersRemoteDataSource {
|
||||
/// Fetch all users from the API
|
||||
Future<List<UserModel>> getUsers();
|
||||
}
|
||||
|
||||
/// Implementation of UsersRemoteDataSource using ApiClient
|
||||
class UsersRemoteDataSourceImpl implements UsersRemoteDataSource {
|
||||
final ApiClient apiClient;
|
||||
|
||||
UsersRemoteDataSourceImpl(this.apiClient);
|
||||
|
||||
@override
|
||||
Future<List<UserModel>> getUsers() async {
|
||||
try {
|
||||
// Make API call to get all users
|
||||
final response = await apiClient.get(ApiEndpoints.users);
|
||||
|
||||
// Parse the API response using ApiResponse wrapper
|
||||
final apiResponse = ApiResponse.fromJson(
|
||||
response.data as Map<String, dynamic>,
|
||||
(json) => (json as List)
|
||||
.map((e) => UserModel.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
// Check if the API call was successful
|
||||
if (apiResponse.isSuccess && apiResponse.value != null) {
|
||||
return apiResponse.value!;
|
||||
} else {
|
||||
// Throw exception with error message from API
|
||||
throw ServerException(
|
||||
apiResponse.errors.isNotEmpty
|
||||
? apiResponse.errors.first
|
||||
: 'Failed to get users',
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
// Re-throw ServerException as-is
|
||||
if (e is ServerException) {
|
||||
rethrow;
|
||||
}
|
||||
// Re-throw NetworkException as-is
|
||||
if (e is NetworkException) {
|
||||
rethrow;
|
||||
}
|
||||
// Wrap other exceptions in ServerException
|
||||
throw ServerException('Failed to get users: ${e.toString()}');
|
||||
}
|
||||
}
|
||||
}
|
||||
163
lib/features/users/data/models/user_model.dart
Normal file
163
lib/features/users/data/models/user_model.dart
Normal file
@@ -0,0 +1,163 @@
|
||||
import 'package:hive_ce/hive.dart';
|
||||
|
||||
import '../../domain/entities/user_entity.dart';
|
||||
|
||||
part 'user_model.g.dart';
|
||||
|
||||
@HiveType(typeId: 1)
|
||||
class UserModel extends UserEntity {
|
||||
@HiveField(0)
|
||||
@override
|
||||
final int id;
|
||||
|
||||
@HiveField(1)
|
||||
@override
|
||||
final String firstName;
|
||||
|
||||
@HiveField(2)
|
||||
@override
|
||||
final String name;
|
||||
|
||||
@HiveField(3)
|
||||
@override
|
||||
final String? plateNumber;
|
||||
|
||||
@HiveField(4)
|
||||
@override
|
||||
final String email;
|
||||
|
||||
@HiveField(5)
|
||||
@override
|
||||
final String phone;
|
||||
|
||||
@HiveField(6)
|
||||
@override
|
||||
final bool isParent;
|
||||
|
||||
@HiveField(7)
|
||||
@override
|
||||
final String fullName;
|
||||
|
||||
@HiveField(8)
|
||||
@override
|
||||
final String fullNameEmail;
|
||||
|
||||
@HiveField(9)
|
||||
@override
|
||||
final String? referralCode;
|
||||
|
||||
@HiveField(10)
|
||||
@override
|
||||
final String? avatar;
|
||||
|
||||
@HiveField(11)
|
||||
@override
|
||||
final int departmentId;
|
||||
|
||||
@HiveField(12)
|
||||
@override
|
||||
final bool isWareHouseUser;
|
||||
|
||||
@HiveField(13)
|
||||
@override
|
||||
final int? wareHouseId;
|
||||
|
||||
@HiveField(14)
|
||||
@override
|
||||
final int roleId;
|
||||
|
||||
const UserModel({
|
||||
required this.id,
|
||||
required this.firstName,
|
||||
required this.name,
|
||||
this.plateNumber,
|
||||
required this.email,
|
||||
required this.phone,
|
||||
this.isParent = false,
|
||||
required this.fullName,
|
||||
required this.fullNameEmail,
|
||||
this.referralCode,
|
||||
this.avatar,
|
||||
required this.departmentId,
|
||||
this.isWareHouseUser = false,
|
||||
this.wareHouseId,
|
||||
required this.roleId,
|
||||
}) : super(
|
||||
id: id,
|
||||
firstName: firstName,
|
||||
name: name,
|
||||
plateNumber: plateNumber,
|
||||
email: email,
|
||||
phone: phone,
|
||||
isParent: isParent,
|
||||
fullName: fullName,
|
||||
fullNameEmail: fullNameEmail,
|
||||
referralCode: referralCode,
|
||||
avatar: avatar,
|
||||
departmentId: departmentId,
|
||||
isWareHouseUser: isWareHouseUser,
|
||||
wareHouseId: wareHouseId,
|
||||
roleId: roleId,
|
||||
);
|
||||
|
||||
factory UserModel.fromJson(Map<String, dynamic> json) {
|
||||
return UserModel(
|
||||
id: json['Id'] ?? 0,
|
||||
firstName: json['FirstName'] ?? '',
|
||||
name: json['Name'] ?? '',
|
||||
plateNumber: json['PlateNumber'],
|
||||
email: json['Email'] ?? '',
|
||||
phone: json['Phone'] ?? '',
|
||||
isParent: json['IsParent'] ?? false,
|
||||
fullName: json['FullName'] ?? '',
|
||||
fullNameEmail: json['FullNameEmail'] ?? '',
|
||||
referralCode: json['ReferralCode'],
|
||||
avatar: json['Avatar'],
|
||||
departmentId: json['DepartmentId'] ?? 0,
|
||||
isWareHouseUser: json['IsWareHouseUser'] ?? false,
|
||||
wareHouseId: json['WareHouseId'],
|
||||
roleId: json['RoleId'] ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'Id': id,
|
||||
'FirstName': firstName,
|
||||
'Name': name,
|
||||
'PlateNumber': plateNumber,
|
||||
'Email': email,
|
||||
'Phone': phone,
|
||||
'IsParent': isParent,
|
||||
'FullName': fullName,
|
||||
'FullNameEmail': fullNameEmail,
|
||||
'ReferralCode': referralCode,
|
||||
'Avatar': avatar,
|
||||
'DepartmentId': departmentId,
|
||||
'IsWareHouseUser': isWareHouseUser,
|
||||
'WareHouseId': wareHouseId,
|
||||
'RoleId': roleId,
|
||||
};
|
||||
}
|
||||
|
||||
UserEntity toEntity() {
|
||||
return UserEntity(
|
||||
id: id,
|
||||
firstName: firstName,
|
||||
name: name,
|
||||
plateNumber: plateNumber,
|
||||
email: email,
|
||||
phone: phone,
|
||||
isParent: isParent,
|
||||
fullName: fullName,
|
||||
fullNameEmail: fullNameEmail,
|
||||
referralCode: referralCode,
|
||||
avatar: avatar,
|
||||
departmentId: departmentId,
|
||||
isWareHouseUser: isWareHouseUser,
|
||||
wareHouseId: wareHouseId,
|
||||
roleId: roleId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
83
lib/features/users/data/models/user_model.g.dart
Normal file
83
lib/features/users/data/models/user_model.g.dart
Normal file
@@ -0,0 +1,83 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'user_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class UserModelAdapter extends TypeAdapter<UserModel> {
|
||||
@override
|
||||
final typeId = 1;
|
||||
|
||||
@override
|
||||
UserModel read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return UserModel(
|
||||
id: (fields[0] as num).toInt(),
|
||||
firstName: fields[1] as String,
|
||||
name: fields[2] as String,
|
||||
plateNumber: fields[3] as String?,
|
||||
email: fields[4] as String,
|
||||
phone: fields[5] as String,
|
||||
isParent: fields[6] == null ? false : fields[6] as bool,
|
||||
fullName: fields[7] as String,
|
||||
fullNameEmail: fields[8] as String,
|
||||
referralCode: fields[9] as String?,
|
||||
avatar: fields[10] as String?,
|
||||
departmentId: (fields[11] as num).toInt(),
|
||||
isWareHouseUser: fields[12] == null ? false : fields[12] as bool,
|
||||
wareHouseId: (fields[13] as num?)?.toInt(),
|
||||
roleId: (fields[14] as num).toInt(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, UserModel obj) {
|
||||
writer
|
||||
..writeByte(15)
|
||||
..writeByte(0)
|
||||
..write(obj.id)
|
||||
..writeByte(1)
|
||||
..write(obj.firstName)
|
||||
..writeByte(2)
|
||||
..write(obj.name)
|
||||
..writeByte(3)
|
||||
..write(obj.plateNumber)
|
||||
..writeByte(4)
|
||||
..write(obj.email)
|
||||
..writeByte(5)
|
||||
..write(obj.phone)
|
||||
..writeByte(6)
|
||||
..write(obj.isParent)
|
||||
..writeByte(7)
|
||||
..write(obj.fullName)
|
||||
..writeByte(8)
|
||||
..write(obj.fullNameEmail)
|
||||
..writeByte(9)
|
||||
..write(obj.referralCode)
|
||||
..writeByte(10)
|
||||
..write(obj.avatar)
|
||||
..writeByte(11)
|
||||
..write(obj.departmentId)
|
||||
..writeByte(12)
|
||||
..write(obj.isWareHouseUser)
|
||||
..writeByte(13)
|
||||
..write(obj.wareHouseId)
|
||||
..writeByte(14)
|
||||
..write(obj.roleId);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is UserModelAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
import '../../../../core/errors/exceptions.dart';
|
||||
import '../../../../core/errors/failures.dart';
|
||||
import '../../domain/entities/user_entity.dart';
|
||||
import '../../domain/repositories/users_repository.dart';
|
||||
import '../datasources/users_local_datasource.dart';
|
||||
import '../datasources/users_remote_datasource.dart';
|
||||
|
||||
/// Implementation of UsersRepository
|
||||
class UsersRepositoryImpl implements UsersRepository {
|
||||
final UsersRemoteDataSource remoteDataSource;
|
||||
final UsersLocalDataSource localDataSource;
|
||||
|
||||
UsersRepositoryImpl({
|
||||
required this.remoteDataSource,
|
||||
required this.localDataSource,
|
||||
});
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<UserEntity>>> getUsers() async {
|
||||
try {
|
||||
// Try to get users from local storage first
|
||||
final localUsers = await localDataSource.getUsers();
|
||||
|
||||
if (localUsers.isNotEmpty) {
|
||||
// Return local users if available
|
||||
return Right(localUsers.map((model) => model.toEntity()).toList());
|
||||
}
|
||||
|
||||
// If no local users, fetch from API
|
||||
return await syncUsers();
|
||||
} catch (e) {
|
||||
return Left(CacheFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<UserEntity>>> syncUsers() async {
|
||||
try {
|
||||
// Fetch users from API
|
||||
final users = await remoteDataSource.getUsers();
|
||||
|
||||
// Save to local storage
|
||||
await localDataSource.saveUsers(users);
|
||||
|
||||
// Return as entities
|
||||
return Right(users.map((model) => model.toEntity()).toList());
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(e.message));
|
||||
} on NetworkException catch (e) {
|
||||
return Left(NetworkFailure(e.message));
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> clearUsers() async {
|
||||
try {
|
||||
await localDataSource.clearUsers();
|
||||
return const Right(null);
|
||||
} catch (e) {
|
||||
return Left(CacheFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user