204 lines
6.7 KiB
Dart
204 lines
6.7 KiB
Dart
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
|
|
import '../../core/constants/enums.dart';
|
|
|
|
part 'user_model.freezed.dart';
|
|
part 'user_model.g.dart';
|
|
|
|
@freezed
|
|
class UserModel with _$UserModel {
|
|
const factory UserModel({
|
|
required String id,
|
|
@JsonKey(name: 'employee_id') required String employeeId,
|
|
required String name,
|
|
required String email,
|
|
required String mobile,
|
|
required String role,
|
|
String? department,
|
|
@Default('active') String status,
|
|
@JsonKey(name: 'company_id') String? companyId,
|
|
@JsonKey(name: 'branch_id') String? branchId,
|
|
String? avatarUrl,
|
|
@Default([]) List<String> permissions,
|
|
DateTime? createdAt,
|
|
DateTime? updatedAt,
|
|
}) = _UserModel;
|
|
|
|
factory UserModel.fromJson(Map<String, dynamic> json) => _$UserModelFromJson(json);
|
|
|
|
/// Parses user objects returned by auth endpoints (`/auth/login`, `/auth/me`).
|
|
factory UserModel.fromLoginJson(Map<String, dynamic> json) {
|
|
final roleRaw = json['role_name'] ??
|
|
json['role'] ??
|
|
json['role_slug'] ??
|
|
_firstRoleFromList(json['roles']) ??
|
|
'employee';
|
|
final role = roleRaw is Map
|
|
? (roleRaw['name'] as String? ?? roleRaw['slug'] as String? ?? 'employee')
|
|
: roleRaw.toString();
|
|
final name = json['full_name'] ??
|
|
json['name'] ??
|
|
[
|
|
json['first_name'],
|
|
json['last_name'],
|
|
].whereType<String>().where((s) => s.isNotEmpty).join(' ');
|
|
|
|
return UserModel(
|
|
id: json['id']?.toString() ?? '',
|
|
employeeId: (json['employee_code'] ?? json['employee_id'] ?? '').toString(),
|
|
name: name is String && name.isNotEmpty ? name : (json['email'] as String? ?? ''),
|
|
email: json['email'] as String? ?? '',
|
|
mobile: json['mobile']?.toString() ?? '',
|
|
role: role,
|
|
department: _nestedLabel(json['department_name']) ??
|
|
_nestedLabel(json['department']),
|
|
status: json['status'] as String? ?? 'active',
|
|
companyId: json['company_id']?.toString(),
|
|
branchId: json['branch_id']?.toString(),
|
|
avatarUrl: json['avatar_url'] as String? ?? json['avatarUrl'] as String?,
|
|
permissions: (json['permissions'] as List<dynamic>?)
|
|
?.map((e) => e.toString())
|
|
.toList() ??
|
|
const [],
|
|
createdAt: json['created_at'] != null
|
|
? DateTime.tryParse(json['created_at'].toString())
|
|
: null,
|
|
updatedAt: json['updated_at'] != null
|
|
? DateTime.tryParse(json['updated_at'].toString())
|
|
: null,
|
|
);
|
|
}
|
|
}
|
|
|
|
Object? _firstRoleFromList(Object? roles) {
|
|
if (roles is! List || roles.isEmpty) return null;
|
|
return roles.first;
|
|
}
|
|
|
|
String? _nestedLabel(Object? value) {
|
|
if (value == null) return null;
|
|
if (value is String) return value.isEmpty ? null : value;
|
|
if (value is Map) {
|
|
final name = value['name'];
|
|
if (name is String && name.isNotEmpty) return name;
|
|
final code = value['code'];
|
|
if (code is String && code.isNotEmpty) return code;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
extension UserModelX on UserModel {
|
|
UserRole get userRole => UserRole.fromValue(role);
|
|
EntityStatus get entityStatus => EntityStatus.fromValue(status);
|
|
bool get isActive => status == EntityStatus.active.value;
|
|
}
|
|
|
|
@freezed
|
|
class AuthTokens with _$AuthTokens {
|
|
const factory AuthTokens({
|
|
@JsonKey(name: 'access_token') required String accessToken,
|
|
@JsonKey(name: 'refresh_token') required String refreshToken,
|
|
@JsonKey(name: 'expires_in') int? expiresIn,
|
|
}) = _AuthTokens;
|
|
|
|
factory AuthTokens.fromJson(Map<String, dynamic> json) => _$AuthTokensFromJson(json);
|
|
}
|
|
|
|
@freezed
|
|
class LoginRequest with _$LoginRequest {
|
|
const factory LoginRequest({
|
|
required String email,
|
|
required String password,
|
|
@JsonKey(name: 'company_code') String? companyCode,
|
|
}) = _LoginRequest;
|
|
|
|
factory LoginRequest.fromJson(Map<String, dynamic> json) => _$LoginRequestFromJson(json);
|
|
}
|
|
|
|
@freezed
|
|
class LoginResponse with _$LoginResponse {
|
|
const factory LoginResponse({
|
|
required AuthTokens tokens,
|
|
required UserModel user,
|
|
@JsonKey(name: 'requires_otp') @Default(false) bool requiresOtp,
|
|
}) = _LoginResponse;
|
|
|
|
factory LoginResponse.fromJson(Map<String, dynamic> json) => _$LoginResponseFromJson(json);
|
|
|
|
/// Supports API `data` shapes:
|
|
/// - `{ access_token, refresh_token, user? }`
|
|
/// - `{ tokens: { access_token, refresh_token }, user }`
|
|
factory LoginResponse.fromApiData(Map<String, dynamic> data) {
|
|
final Map<String, dynamic> tokenSource;
|
|
if (data['tokens'] is Map<String, dynamic>) {
|
|
tokenSource = data['tokens'] as Map<String, dynamic>;
|
|
} else {
|
|
tokenSource = data;
|
|
}
|
|
|
|
final accessToken = tokenSource['access_token'] as String? ??
|
|
tokenSource['accessToken'] as String? ??
|
|
tokenSource['token'] as String?;
|
|
final refreshToken = tokenSource['refresh_token'] as String? ??
|
|
tokenSource['refreshToken'] as String? ??
|
|
'';
|
|
|
|
if (accessToken == null || accessToken.isEmpty) {
|
|
throw StateError('Login response missing access_token');
|
|
}
|
|
|
|
final userJson = data['user'];
|
|
final user = userJson is Map<String, dynamic>
|
|
? UserModel.fromLoginJson(userJson)
|
|
: UserModel.fromLoginJson({
|
|
if (data['email'] != null) 'email': data['email'],
|
|
if (data['full_name'] != null) 'full_name': data['full_name'],
|
|
if (data['name'] != null) 'name': data['name'],
|
|
if (data['id'] != null) 'id': data['id'],
|
|
});
|
|
|
|
return LoginResponse(
|
|
tokens: AuthTokens(
|
|
accessToken: accessToken,
|
|
refreshToken: refreshToken,
|
|
expiresIn: (tokenSource['expires_in'] as num?)?.toInt(),
|
|
),
|
|
user: user,
|
|
requiresOtp: data['requires_otp'] as bool? ?? false,
|
|
);
|
|
}
|
|
}
|
|
|
|
@freezed
|
|
class OtpVerifyRequest with _$OtpVerifyRequest {
|
|
const factory OtpVerifyRequest({
|
|
required String email,
|
|
required String otp,
|
|
}) = _OtpVerifyRequest;
|
|
|
|
factory OtpVerifyRequest.fromJson(Map<String, dynamic> json) =>
|
|
_$OtpVerifyRequestFromJson(json);
|
|
}
|
|
|
|
@freezed
|
|
class ChangePasswordRequest with _$ChangePasswordRequest {
|
|
const factory ChangePasswordRequest({
|
|
@JsonKey(name: 'current_password') required String currentPassword,
|
|
@JsonKey(name: 'new_password') required String newPassword,
|
|
@JsonKey(name: 'confirm_password') required String confirmPassword,
|
|
}) = _ChangePasswordRequest;
|
|
|
|
factory ChangePasswordRequest.fromJson(Map<String, dynamic> json) =>
|
|
_$ChangePasswordRequestFromJson(json);
|
|
}
|
|
|
|
@freezed
|
|
class ForgotPasswordRequest with _$ForgotPasswordRequest {
|
|
const factory ForgotPasswordRequest({
|
|
required String email,
|
|
}) = _ForgotPasswordRequest;
|
|
|
|
factory ForgotPasswordRequest.fromJson(Map<String, dynamic> json) =>
|
|
_$ForgotPasswordRequestFromJson(json);
|
|
}
|