feat: API 통합 2차 작업 완료
- 자동 로그인 구현: 앱 시작 시 토큰 확인 후 적절한 화면으로 라우팅 - AuthInterceptor 개선: AuthService와 통합하여 토큰 관리 일원화 - 로그아웃 기능 개선: AuthService를 사용한 API 로그아웃 처리 - 대시보드 API 연동: MockDataService에서 실제 API로 완전 전환 - Dashboard DTO 모델 생성 (OverviewStats, RecentActivity 등) - DashboardRemoteDataSource 및 DashboardService 구현 - OverviewController를 ChangeNotifier 패턴으로 개선 - OverviewScreenRedesign에 Provider 패턴 적용 - API 통합 진행 상황 문서 업데이트
This commit is contained in:
132
lib/data/datasources/remote/dashboard_remote_datasource.dart
Normal file
132
lib/data/datasources/remote/dashboard_remote_datasource.dart
Normal file
@@ -0,0 +1,132 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:superport/core/constants/api_endpoints.dart';
|
||||
import 'package:superport/core/errors/exceptions.dart';
|
||||
import 'package:superport/core/errors/failures.dart';
|
||||
import 'package:superport/data/datasources/remote/api_client.dart';
|
||||
import 'package:superport/data/models/dashboard/equipment_status_distribution.dart';
|
||||
import 'package:superport/data/models/dashboard/expiring_license.dart';
|
||||
import 'package:superport/data/models/dashboard/overview_stats.dart';
|
||||
import 'package:superport/data/models/dashboard/recent_activity.dart';
|
||||
|
||||
abstract class DashboardRemoteDataSource {
|
||||
Future<Either<Failure, OverviewStats>> getOverviewStats();
|
||||
Future<Either<Failure, List<RecentActivity>>> getRecentActivities();
|
||||
Future<Either<Failure, EquipmentStatusDistribution>> getEquipmentStatusDistribution();
|
||||
Future<Either<Failure, List<ExpiringLicense>>> getExpiringLicenses({int days = 30});
|
||||
}
|
||||
|
||||
@LazySingleton(as: DashboardRemoteDataSource)
|
||||
class DashboardRemoteDataSourceImpl implements DashboardRemoteDataSource {
|
||||
final ApiClient _apiClient;
|
||||
|
||||
DashboardRemoteDataSourceImpl(this._apiClient);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, OverviewStats>> getOverviewStats() async {
|
||||
try {
|
||||
final response = await _apiClient.get('/overview/stats');
|
||||
|
||||
if (response.data != null) {
|
||||
final stats = OverviewStats.fromJson(response.data);
|
||||
return Right(stats);
|
||||
} else {
|
||||
return Left(ServerFailure('응답 데이터가 없습니다'));
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
return Left(_handleDioError(e));
|
||||
} catch (e) {
|
||||
return Left(ServerFailure('통계 데이터를 가져오는 중 오류가 발생했습니다'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<RecentActivity>>> getRecentActivities() async {
|
||||
try {
|
||||
final response = await _apiClient.get('/overview/recent-activities');
|
||||
|
||||
if (response.data != null && response.data is List) {
|
||||
final activities = (response.data as List)
|
||||
.map((json) => RecentActivity.fromJson(json))
|
||||
.toList();
|
||||
return Right(activities);
|
||||
} else {
|
||||
return Left(ServerFailure('응답 데이터가 올바르지 않습니다'));
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
return Left(_handleDioError(e));
|
||||
} catch (e) {
|
||||
return Left(ServerFailure('최근 활동을 가져오는 중 오류가 발생했습니다'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, EquipmentStatusDistribution>> getEquipmentStatusDistribution() async {
|
||||
try {
|
||||
final response = await _apiClient.get('/equipment/status-distribution');
|
||||
|
||||
if (response.data != null) {
|
||||
final distribution = EquipmentStatusDistribution.fromJson(response.data);
|
||||
return Right(distribution);
|
||||
} else {
|
||||
return Left(ServerFailure('응답 데이터가 없습니다'));
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
return Left(_handleDioError(e));
|
||||
} catch (e) {
|
||||
return Left(ServerFailure('장비 상태 분포를 가져오는 중 오류가 발생했습니다'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<ExpiringLicense>>> getExpiringLicenses({int days = 30}) async {
|
||||
try {
|
||||
final response = await _apiClient.get(
|
||||
'/licenses/expiring-soon',
|
||||
queryParameters: {'days': days},
|
||||
);
|
||||
|
||||
if (response.data != null && response.data is List) {
|
||||
final licenses = (response.data as List)
|
||||
.map((json) => ExpiringLicense.fromJson(json))
|
||||
.toList();
|
||||
return Right(licenses);
|
||||
} else {
|
||||
return Left(ServerFailure('응답 데이터가 올바르지 않습니다'));
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
return Left(_handleDioError(e));
|
||||
} catch (e) {
|
||||
return Left(ServerFailure('만료 예정 라이선스를 가져오는 중 오류가 발생했습니다'));
|
||||
}
|
||||
}
|
||||
|
||||
Failure _handleDioError(DioException error) {
|
||||
switch (error.type) {
|
||||
case DioExceptionType.connectionTimeout:
|
||||
case DioExceptionType.sendTimeout:
|
||||
case DioExceptionType.receiveTimeout:
|
||||
return NetworkFailure('네트워크 연결 시간이 초과되었습니다');
|
||||
case DioExceptionType.connectionError:
|
||||
return NetworkFailure('서버에 연결할 수 없습니다');
|
||||
case DioExceptionType.badResponse:
|
||||
final statusCode = error.response?.statusCode ?? 0;
|
||||
final message = error.response?.data?['message'] ?? '서버 오류가 발생했습니다';
|
||||
|
||||
if (statusCode == 401) {
|
||||
return AuthFailure('인증이 만료되었습니다');
|
||||
} else if (statusCode == 403) {
|
||||
return AuthFailure('접근 권한이 없습니다');
|
||||
} else if (statusCode >= 400 && statusCode < 500) {
|
||||
return ServerFailure(message);
|
||||
} else {
|
||||
return ServerFailure('서버 오류가 발생했습니다 ($statusCode)');
|
||||
}
|
||||
case DioExceptionType.cancel:
|
||||
return ServerFailure('요청이 취소되었습니다');
|
||||
default:
|
||||
return ServerFailure('알 수 없는 오류가 발생했습니다');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,15 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import '../../../../core/constants/app_constants.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import '../../../../core/constants/api_endpoints.dart';
|
||||
import '../../../../services/auth_service.dart';
|
||||
|
||||
/// 인증 인터셉터
|
||||
class AuthInterceptor extends Interceptor {
|
||||
final _storage = const FlutterSecureStorage();
|
||||
late final AuthService _authService;
|
||||
|
||||
AuthInterceptor() {
|
||||
_authService = GetIt.instance<AuthService>();
|
||||
}
|
||||
|
||||
@override
|
||||
void onRequest(
|
||||
@@ -19,7 +23,7 @@ class AuthInterceptor extends Interceptor {
|
||||
}
|
||||
|
||||
// 저장된 액세스 토큰 가져오기
|
||||
final accessToken = await _storage.read(key: AppConstants.accessTokenKey);
|
||||
final accessToken = await _authService.getAccessToken();
|
||||
|
||||
if (accessToken != null) {
|
||||
options.headers['Authorization'] = 'Bearer $accessToken';
|
||||
@@ -36,12 +40,17 @@ class AuthInterceptor extends Interceptor {
|
||||
// 401 Unauthorized 에러 처리
|
||||
if (err.response?.statusCode == 401) {
|
||||
// 토큰 갱신 시도
|
||||
final refreshSuccess = await _refreshToken();
|
||||
final refreshResult = await _authService.refreshToken();
|
||||
|
||||
final refreshSuccess = refreshResult.fold(
|
||||
(failure) => false,
|
||||
(tokenResponse) => true,
|
||||
);
|
||||
|
||||
if (refreshSuccess) {
|
||||
// 새로운 토큰으로 원래 요청 재시도
|
||||
try {
|
||||
final newAccessToken = await _storage.read(key: AppConstants.accessTokenKey);
|
||||
final newAccessToken = await _authService.getAccessToken();
|
||||
|
||||
if (newAccessToken != null) {
|
||||
err.requestOptions.headers['Authorization'] = 'Bearer $newAccessToken';
|
||||
@@ -58,60 +67,13 @@ class AuthInterceptor extends Interceptor {
|
||||
}
|
||||
|
||||
// 토큰 갱신 실패 시 로그인 화면으로 이동
|
||||
await _clearTokens();
|
||||
await _authService.clearSession();
|
||||
// TODO: Navigate to login screen
|
||||
}
|
||||
|
||||
handler.next(err);
|
||||
}
|
||||
|
||||
/// 토큰 갱신
|
||||
Future<bool> _refreshToken() async {
|
||||
try {
|
||||
final refreshToken = await _storage.read(key: AppConstants.refreshTokenKey);
|
||||
|
||||
if (refreshToken == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final dio = Dio();
|
||||
final response = await dio.post(
|
||||
'${dio.options.baseUrl}${ApiEndpoints.refresh}',
|
||||
data: {
|
||||
'refresh_token': refreshToken,
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200 && response.data != null) {
|
||||
final data = response.data;
|
||||
|
||||
// 새로운 토큰 저장
|
||||
await _storage.write(
|
||||
key: AppConstants.accessTokenKey,
|
||||
value: data['access_token'],
|
||||
);
|
||||
|
||||
if (data['refresh_token'] != null) {
|
||||
await _storage.write(
|
||||
key: AppConstants.refreshTokenKey,
|
||||
value: data['refresh_token'],
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// 토큰 삭제
|
||||
Future<void> _clearTokens() async {
|
||||
await _storage.delete(key: AppConstants.accessTokenKey);
|
||||
await _storage.delete(key: AppConstants.refreshTokenKey);
|
||||
}
|
||||
|
||||
/// 인증 관련 엔드포인트 확인
|
||||
bool _isAuthEndpoint(String path) {
|
||||
|
||||
17
lib/data/models/dashboard/equipment_status_distribution.dart
Normal file
17
lib/data/models/dashboard/equipment_status_distribution.dart
Normal file
@@ -0,0 +1,17 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'equipment_status_distribution.freezed.dart';
|
||||
part 'equipment_status_distribution.g.dart';
|
||||
|
||||
@freezed
|
||||
class EquipmentStatusDistribution with _$EquipmentStatusDistribution {
|
||||
const factory EquipmentStatusDistribution({
|
||||
required int available,
|
||||
@JsonKey(name: 'in_use') required int inUse,
|
||||
required int maintenance,
|
||||
required int disposed,
|
||||
}) = _EquipmentStatusDistribution;
|
||||
|
||||
factory EquipmentStatusDistribution.fromJson(Map<String, dynamic> json) =>
|
||||
_$EquipmentStatusDistributionFromJson(json);
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'equipment_status_distribution.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
EquipmentStatusDistribution _$EquipmentStatusDistributionFromJson(
|
||||
Map<String, dynamic> json) {
|
||||
return _EquipmentStatusDistribution.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$EquipmentStatusDistribution {
|
||||
int get available => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'in_use')
|
||||
int get inUse => throw _privateConstructorUsedError;
|
||||
int get maintenance => throw _privateConstructorUsedError;
|
||||
int get disposed => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this EquipmentStatusDistribution to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of EquipmentStatusDistribution
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$EquipmentStatusDistributionCopyWith<EquipmentStatusDistribution>
|
||||
get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $EquipmentStatusDistributionCopyWith<$Res> {
|
||||
factory $EquipmentStatusDistributionCopyWith(
|
||||
EquipmentStatusDistribution value,
|
||||
$Res Function(EquipmentStatusDistribution) then) =
|
||||
_$EquipmentStatusDistributionCopyWithImpl<$Res,
|
||||
EquipmentStatusDistribution>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{int available,
|
||||
@JsonKey(name: 'in_use') int inUse,
|
||||
int maintenance,
|
||||
int disposed});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$EquipmentStatusDistributionCopyWithImpl<$Res,
|
||||
$Val extends EquipmentStatusDistribution>
|
||||
implements $EquipmentStatusDistributionCopyWith<$Res> {
|
||||
_$EquipmentStatusDistributionCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of EquipmentStatusDistribution
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? available = null,
|
||||
Object? inUse = null,
|
||||
Object? maintenance = null,
|
||||
Object? disposed = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
available: null == available
|
||||
? _value.available
|
||||
: available // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
inUse: null == inUse
|
||||
? _value.inUse
|
||||
: inUse // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
maintenance: null == maintenance
|
||||
? _value.maintenance
|
||||
: maintenance // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
disposed: null == disposed
|
||||
? _value.disposed
|
||||
: disposed // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$EquipmentStatusDistributionImplCopyWith<$Res>
|
||||
implements $EquipmentStatusDistributionCopyWith<$Res> {
|
||||
factory _$$EquipmentStatusDistributionImplCopyWith(
|
||||
_$EquipmentStatusDistributionImpl value,
|
||||
$Res Function(_$EquipmentStatusDistributionImpl) then) =
|
||||
__$$EquipmentStatusDistributionImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{int available,
|
||||
@JsonKey(name: 'in_use') int inUse,
|
||||
int maintenance,
|
||||
int disposed});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$EquipmentStatusDistributionImplCopyWithImpl<$Res>
|
||||
extends _$EquipmentStatusDistributionCopyWithImpl<$Res,
|
||||
_$EquipmentStatusDistributionImpl>
|
||||
implements _$$EquipmentStatusDistributionImplCopyWith<$Res> {
|
||||
__$$EquipmentStatusDistributionImplCopyWithImpl(
|
||||
_$EquipmentStatusDistributionImpl _value,
|
||||
$Res Function(_$EquipmentStatusDistributionImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of EquipmentStatusDistribution
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? available = null,
|
||||
Object? inUse = null,
|
||||
Object? maintenance = null,
|
||||
Object? disposed = null,
|
||||
}) {
|
||||
return _then(_$EquipmentStatusDistributionImpl(
|
||||
available: null == available
|
||||
? _value.available
|
||||
: available // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
inUse: null == inUse
|
||||
? _value.inUse
|
||||
: inUse // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
maintenance: null == maintenance
|
||||
? _value.maintenance
|
||||
: maintenance // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
disposed: null == disposed
|
||||
? _value.disposed
|
||||
: disposed // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$EquipmentStatusDistributionImpl
|
||||
implements _EquipmentStatusDistribution {
|
||||
const _$EquipmentStatusDistributionImpl(
|
||||
{required this.available,
|
||||
@JsonKey(name: 'in_use') required this.inUse,
|
||||
required this.maintenance,
|
||||
required this.disposed});
|
||||
|
||||
factory _$EquipmentStatusDistributionImpl.fromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$$EquipmentStatusDistributionImplFromJson(json);
|
||||
|
||||
@override
|
||||
final int available;
|
||||
@override
|
||||
@JsonKey(name: 'in_use')
|
||||
final int inUse;
|
||||
@override
|
||||
final int maintenance;
|
||||
@override
|
||||
final int disposed;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'EquipmentStatusDistribution(available: $available, inUse: $inUse, maintenance: $maintenance, disposed: $disposed)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$EquipmentStatusDistributionImpl &&
|
||||
(identical(other.available, available) ||
|
||||
other.available == available) &&
|
||||
(identical(other.inUse, inUse) || other.inUse == inUse) &&
|
||||
(identical(other.maintenance, maintenance) ||
|
||||
other.maintenance == maintenance) &&
|
||||
(identical(other.disposed, disposed) ||
|
||||
other.disposed == disposed));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, available, inUse, maintenance, disposed);
|
||||
|
||||
/// Create a copy of EquipmentStatusDistribution
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$EquipmentStatusDistributionImplCopyWith<_$EquipmentStatusDistributionImpl>
|
||||
get copyWith => __$$EquipmentStatusDistributionImplCopyWithImpl<
|
||||
_$EquipmentStatusDistributionImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$EquipmentStatusDistributionImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _EquipmentStatusDistribution
|
||||
implements EquipmentStatusDistribution {
|
||||
const factory _EquipmentStatusDistribution(
|
||||
{required final int available,
|
||||
@JsonKey(name: 'in_use') required final int inUse,
|
||||
required final int maintenance,
|
||||
required final int disposed}) = _$EquipmentStatusDistributionImpl;
|
||||
|
||||
factory _EquipmentStatusDistribution.fromJson(Map<String, dynamic> json) =
|
||||
_$EquipmentStatusDistributionImpl.fromJson;
|
||||
|
||||
@override
|
||||
int get available;
|
||||
@override
|
||||
@JsonKey(name: 'in_use')
|
||||
int get inUse;
|
||||
@override
|
||||
int get maintenance;
|
||||
@override
|
||||
int get disposed;
|
||||
|
||||
/// Create a copy of EquipmentStatusDistribution
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$EquipmentStatusDistributionImplCopyWith<_$EquipmentStatusDistributionImpl>
|
||||
get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'equipment_status_distribution.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$EquipmentStatusDistributionImpl _$$EquipmentStatusDistributionImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$EquipmentStatusDistributionImpl(
|
||||
available: (json['available'] as num).toInt(),
|
||||
inUse: (json['in_use'] as num).toInt(),
|
||||
maintenance: (json['maintenance'] as num).toInt(),
|
||||
disposed: (json['disposed'] as num).toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$EquipmentStatusDistributionImplToJson(
|
||||
_$EquipmentStatusDistributionImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'available': instance.available,
|
||||
'in_use': instance.inUse,
|
||||
'maintenance': instance.maintenance,
|
||||
'disposed': instance.disposed,
|
||||
};
|
||||
19
lib/data/models/dashboard/expiring_license.dart
Normal file
19
lib/data/models/dashboard/expiring_license.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'expiring_license.freezed.dart';
|
||||
part 'expiring_license.g.dart';
|
||||
|
||||
@freezed
|
||||
class ExpiringLicense with _$ExpiringLicense {
|
||||
const factory ExpiringLicense({
|
||||
required int id,
|
||||
@JsonKey(name: 'license_name') required String licenseName,
|
||||
@JsonKey(name: 'company_name') required String companyName,
|
||||
@JsonKey(name: 'expiry_date') required DateTime expiryDate,
|
||||
@JsonKey(name: 'days_remaining') required int daysRemaining,
|
||||
@JsonKey(name: 'license_type') required String licenseType,
|
||||
}) = _ExpiringLicense;
|
||||
|
||||
factory ExpiringLicense.fromJson(Map<String, dynamic> json) =>
|
||||
_$ExpiringLicenseFromJson(json);
|
||||
}
|
||||
291
lib/data/models/dashboard/expiring_license.freezed.dart
Normal file
291
lib/data/models/dashboard/expiring_license.freezed.dart
Normal file
@@ -0,0 +1,291 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'expiring_license.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
ExpiringLicense _$ExpiringLicenseFromJson(Map<String, dynamic> json) {
|
||||
return _ExpiringLicense.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$ExpiringLicense {
|
||||
int get id => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'license_name')
|
||||
String get licenseName => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'company_name')
|
||||
String get companyName => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'expiry_date')
|
||||
DateTime get expiryDate => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'days_remaining')
|
||||
int get daysRemaining => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'license_type')
|
||||
String get licenseType => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this ExpiringLicense to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of ExpiringLicense
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$ExpiringLicenseCopyWith<ExpiringLicense> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $ExpiringLicenseCopyWith<$Res> {
|
||||
factory $ExpiringLicenseCopyWith(
|
||||
ExpiringLicense value, $Res Function(ExpiringLicense) then) =
|
||||
_$ExpiringLicenseCopyWithImpl<$Res, ExpiringLicense>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{int id,
|
||||
@JsonKey(name: 'license_name') String licenseName,
|
||||
@JsonKey(name: 'company_name') String companyName,
|
||||
@JsonKey(name: 'expiry_date') DateTime expiryDate,
|
||||
@JsonKey(name: 'days_remaining') int daysRemaining,
|
||||
@JsonKey(name: 'license_type') String licenseType});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$ExpiringLicenseCopyWithImpl<$Res, $Val extends ExpiringLicense>
|
||||
implements $ExpiringLicenseCopyWith<$Res> {
|
||||
_$ExpiringLicenseCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of ExpiringLicense
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = null,
|
||||
Object? licenseName = null,
|
||||
Object? companyName = null,
|
||||
Object? expiryDate = null,
|
||||
Object? daysRemaining = null,
|
||||
Object? licenseType = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
licenseName: null == licenseName
|
||||
? _value.licenseName
|
||||
: licenseName // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
companyName: null == companyName
|
||||
? _value.companyName
|
||||
: companyName // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
expiryDate: null == expiryDate
|
||||
? _value.expiryDate
|
||||
: expiryDate // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,
|
||||
daysRemaining: null == daysRemaining
|
||||
? _value.daysRemaining
|
||||
: daysRemaining // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
licenseType: null == licenseType
|
||||
? _value.licenseType
|
||||
: licenseType // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$ExpiringLicenseImplCopyWith<$Res>
|
||||
implements $ExpiringLicenseCopyWith<$Res> {
|
||||
factory _$$ExpiringLicenseImplCopyWith(_$ExpiringLicenseImpl value,
|
||||
$Res Function(_$ExpiringLicenseImpl) then) =
|
||||
__$$ExpiringLicenseImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{int id,
|
||||
@JsonKey(name: 'license_name') String licenseName,
|
||||
@JsonKey(name: 'company_name') String companyName,
|
||||
@JsonKey(name: 'expiry_date') DateTime expiryDate,
|
||||
@JsonKey(name: 'days_remaining') int daysRemaining,
|
||||
@JsonKey(name: 'license_type') String licenseType});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$ExpiringLicenseImplCopyWithImpl<$Res>
|
||||
extends _$ExpiringLicenseCopyWithImpl<$Res, _$ExpiringLicenseImpl>
|
||||
implements _$$ExpiringLicenseImplCopyWith<$Res> {
|
||||
__$$ExpiringLicenseImplCopyWithImpl(
|
||||
_$ExpiringLicenseImpl _value, $Res Function(_$ExpiringLicenseImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of ExpiringLicense
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = null,
|
||||
Object? licenseName = null,
|
||||
Object? companyName = null,
|
||||
Object? expiryDate = null,
|
||||
Object? daysRemaining = null,
|
||||
Object? licenseType = null,
|
||||
}) {
|
||||
return _then(_$ExpiringLicenseImpl(
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
licenseName: null == licenseName
|
||||
? _value.licenseName
|
||||
: licenseName // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
companyName: null == companyName
|
||||
? _value.companyName
|
||||
: companyName // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
expiryDate: null == expiryDate
|
||||
? _value.expiryDate
|
||||
: expiryDate // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,
|
||||
daysRemaining: null == daysRemaining
|
||||
? _value.daysRemaining
|
||||
: daysRemaining // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
licenseType: null == licenseType
|
||||
? _value.licenseType
|
||||
: licenseType // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$ExpiringLicenseImpl implements _ExpiringLicense {
|
||||
const _$ExpiringLicenseImpl(
|
||||
{required this.id,
|
||||
@JsonKey(name: 'license_name') required this.licenseName,
|
||||
@JsonKey(name: 'company_name') required this.companyName,
|
||||
@JsonKey(name: 'expiry_date') required this.expiryDate,
|
||||
@JsonKey(name: 'days_remaining') required this.daysRemaining,
|
||||
@JsonKey(name: 'license_type') required this.licenseType});
|
||||
|
||||
factory _$ExpiringLicenseImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$ExpiringLicenseImplFromJson(json);
|
||||
|
||||
@override
|
||||
final int id;
|
||||
@override
|
||||
@JsonKey(name: 'license_name')
|
||||
final String licenseName;
|
||||
@override
|
||||
@JsonKey(name: 'company_name')
|
||||
final String companyName;
|
||||
@override
|
||||
@JsonKey(name: 'expiry_date')
|
||||
final DateTime expiryDate;
|
||||
@override
|
||||
@JsonKey(name: 'days_remaining')
|
||||
final int daysRemaining;
|
||||
@override
|
||||
@JsonKey(name: 'license_type')
|
||||
final String licenseType;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ExpiringLicense(id: $id, licenseName: $licenseName, companyName: $companyName, expiryDate: $expiryDate, daysRemaining: $daysRemaining, licenseType: $licenseType)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$ExpiringLicenseImpl &&
|
||||
(identical(other.id, id) || other.id == id) &&
|
||||
(identical(other.licenseName, licenseName) ||
|
||||
other.licenseName == licenseName) &&
|
||||
(identical(other.companyName, companyName) ||
|
||||
other.companyName == companyName) &&
|
||||
(identical(other.expiryDate, expiryDate) ||
|
||||
other.expiryDate == expiryDate) &&
|
||||
(identical(other.daysRemaining, daysRemaining) ||
|
||||
other.daysRemaining == daysRemaining) &&
|
||||
(identical(other.licenseType, licenseType) ||
|
||||
other.licenseType == licenseType));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, id, licenseName, companyName,
|
||||
expiryDate, daysRemaining, licenseType);
|
||||
|
||||
/// Create a copy of ExpiringLicense
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$ExpiringLicenseImplCopyWith<_$ExpiringLicenseImpl> get copyWith =>
|
||||
__$$ExpiringLicenseImplCopyWithImpl<_$ExpiringLicenseImpl>(
|
||||
this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$ExpiringLicenseImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _ExpiringLicense implements ExpiringLicense {
|
||||
const factory _ExpiringLicense(
|
||||
{required final int id,
|
||||
@JsonKey(name: 'license_name') required final String licenseName,
|
||||
@JsonKey(name: 'company_name') required final String companyName,
|
||||
@JsonKey(name: 'expiry_date') required final DateTime expiryDate,
|
||||
@JsonKey(name: 'days_remaining') required final int daysRemaining,
|
||||
@JsonKey(name: 'license_type') required final String licenseType}) =
|
||||
_$ExpiringLicenseImpl;
|
||||
|
||||
factory _ExpiringLicense.fromJson(Map<String, dynamic> json) =
|
||||
_$ExpiringLicenseImpl.fromJson;
|
||||
|
||||
@override
|
||||
int get id;
|
||||
@override
|
||||
@JsonKey(name: 'license_name')
|
||||
String get licenseName;
|
||||
@override
|
||||
@JsonKey(name: 'company_name')
|
||||
String get companyName;
|
||||
@override
|
||||
@JsonKey(name: 'expiry_date')
|
||||
DateTime get expiryDate;
|
||||
@override
|
||||
@JsonKey(name: 'days_remaining')
|
||||
int get daysRemaining;
|
||||
@override
|
||||
@JsonKey(name: 'license_type')
|
||||
String get licenseType;
|
||||
|
||||
/// Create a copy of ExpiringLicense
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$ExpiringLicenseImplCopyWith<_$ExpiringLicenseImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
29
lib/data/models/dashboard/expiring_license.g.dart
Normal file
29
lib/data/models/dashboard/expiring_license.g.dart
Normal file
@@ -0,0 +1,29 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'expiring_license.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$ExpiringLicenseImpl _$$ExpiringLicenseImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$ExpiringLicenseImpl(
|
||||
id: (json['id'] as num).toInt(),
|
||||
licenseName: json['license_name'] as String,
|
||||
companyName: json['company_name'] as String,
|
||||
expiryDate: DateTime.parse(json['expiry_date'] as String),
|
||||
daysRemaining: (json['days_remaining'] as num).toInt(),
|
||||
licenseType: json['license_type'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ExpiringLicenseImplToJson(
|
||||
_$ExpiringLicenseImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'license_name': instance.licenseName,
|
||||
'company_name': instance.companyName,
|
||||
'expiry_date': instance.expiryDate.toIso8601String(),
|
||||
'days_remaining': instance.daysRemaining,
|
||||
'license_type': instance.licenseType,
|
||||
};
|
||||
23
lib/data/models/dashboard/overview_stats.dart
Normal file
23
lib/data/models/dashboard/overview_stats.dart
Normal file
@@ -0,0 +1,23 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'overview_stats.freezed.dart';
|
||||
part 'overview_stats.g.dart';
|
||||
|
||||
@freezed
|
||||
class OverviewStats with _$OverviewStats {
|
||||
const factory OverviewStats({
|
||||
@JsonKey(name: 'total_equipment') required int totalEquipment,
|
||||
@JsonKey(name: 'available_equipment') required int availableEquipment,
|
||||
@JsonKey(name: 'in_use_equipment') required int inUseEquipment,
|
||||
@JsonKey(name: 'maintenance_equipment') required int maintenanceEquipment,
|
||||
@JsonKey(name: 'total_companies') required int totalCompanies,
|
||||
@JsonKey(name: 'total_users') required int totalUsers,
|
||||
@JsonKey(name: 'active_licenses') required int activeLicenses,
|
||||
@JsonKey(name: 'expiring_licenses') required int expiringLicenses,
|
||||
@JsonKey(name: 'total_rentals') required int totalRentals,
|
||||
@JsonKey(name: 'active_rentals') required int activeRentals,
|
||||
}) = _OverviewStats;
|
||||
|
||||
factory OverviewStats.fromJson(Map<String, dynamic> json) =>
|
||||
_$OverviewStatsFromJson(json);
|
||||
}
|
||||
403
lib/data/models/dashboard/overview_stats.freezed.dart
Normal file
403
lib/data/models/dashboard/overview_stats.freezed.dart
Normal file
@@ -0,0 +1,403 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'overview_stats.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
OverviewStats _$OverviewStatsFromJson(Map<String, dynamic> json) {
|
||||
return _OverviewStats.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$OverviewStats {
|
||||
@JsonKey(name: 'total_equipment')
|
||||
int get totalEquipment => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'available_equipment')
|
||||
int get availableEquipment => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'in_use_equipment')
|
||||
int get inUseEquipment => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'maintenance_equipment')
|
||||
int get maintenanceEquipment => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'total_companies')
|
||||
int get totalCompanies => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'total_users')
|
||||
int get totalUsers => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'active_licenses')
|
||||
int get activeLicenses => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'expiring_licenses')
|
||||
int get expiringLicenses => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'total_rentals')
|
||||
int get totalRentals => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'active_rentals')
|
||||
int get activeRentals => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this OverviewStats to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of OverviewStats
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$OverviewStatsCopyWith<OverviewStats> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $OverviewStatsCopyWith<$Res> {
|
||||
factory $OverviewStatsCopyWith(
|
||||
OverviewStats value, $Res Function(OverviewStats) then) =
|
||||
_$OverviewStatsCopyWithImpl<$Res, OverviewStats>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{@JsonKey(name: 'total_equipment') int totalEquipment,
|
||||
@JsonKey(name: 'available_equipment') int availableEquipment,
|
||||
@JsonKey(name: 'in_use_equipment') int inUseEquipment,
|
||||
@JsonKey(name: 'maintenance_equipment') int maintenanceEquipment,
|
||||
@JsonKey(name: 'total_companies') int totalCompanies,
|
||||
@JsonKey(name: 'total_users') int totalUsers,
|
||||
@JsonKey(name: 'active_licenses') int activeLicenses,
|
||||
@JsonKey(name: 'expiring_licenses') int expiringLicenses,
|
||||
@JsonKey(name: 'total_rentals') int totalRentals,
|
||||
@JsonKey(name: 'active_rentals') int activeRentals});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$OverviewStatsCopyWithImpl<$Res, $Val extends OverviewStats>
|
||||
implements $OverviewStatsCopyWith<$Res> {
|
||||
_$OverviewStatsCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of OverviewStats
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? totalEquipment = null,
|
||||
Object? availableEquipment = null,
|
||||
Object? inUseEquipment = null,
|
||||
Object? maintenanceEquipment = null,
|
||||
Object? totalCompanies = null,
|
||||
Object? totalUsers = null,
|
||||
Object? activeLicenses = null,
|
||||
Object? expiringLicenses = null,
|
||||
Object? totalRentals = null,
|
||||
Object? activeRentals = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
totalEquipment: null == totalEquipment
|
||||
? _value.totalEquipment
|
||||
: totalEquipment // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
availableEquipment: null == availableEquipment
|
||||
? _value.availableEquipment
|
||||
: availableEquipment // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
inUseEquipment: null == inUseEquipment
|
||||
? _value.inUseEquipment
|
||||
: inUseEquipment // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
maintenanceEquipment: null == maintenanceEquipment
|
||||
? _value.maintenanceEquipment
|
||||
: maintenanceEquipment // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
totalCompanies: null == totalCompanies
|
||||
? _value.totalCompanies
|
||||
: totalCompanies // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
totalUsers: null == totalUsers
|
||||
? _value.totalUsers
|
||||
: totalUsers // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
activeLicenses: null == activeLicenses
|
||||
? _value.activeLicenses
|
||||
: activeLicenses // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
expiringLicenses: null == expiringLicenses
|
||||
? _value.expiringLicenses
|
||||
: expiringLicenses // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
totalRentals: null == totalRentals
|
||||
? _value.totalRentals
|
||||
: totalRentals // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
activeRentals: null == activeRentals
|
||||
? _value.activeRentals
|
||||
: activeRentals // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$OverviewStatsImplCopyWith<$Res>
|
||||
implements $OverviewStatsCopyWith<$Res> {
|
||||
factory _$$OverviewStatsImplCopyWith(
|
||||
_$OverviewStatsImpl value, $Res Function(_$OverviewStatsImpl) then) =
|
||||
__$$OverviewStatsImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{@JsonKey(name: 'total_equipment') int totalEquipment,
|
||||
@JsonKey(name: 'available_equipment') int availableEquipment,
|
||||
@JsonKey(name: 'in_use_equipment') int inUseEquipment,
|
||||
@JsonKey(name: 'maintenance_equipment') int maintenanceEquipment,
|
||||
@JsonKey(name: 'total_companies') int totalCompanies,
|
||||
@JsonKey(name: 'total_users') int totalUsers,
|
||||
@JsonKey(name: 'active_licenses') int activeLicenses,
|
||||
@JsonKey(name: 'expiring_licenses') int expiringLicenses,
|
||||
@JsonKey(name: 'total_rentals') int totalRentals,
|
||||
@JsonKey(name: 'active_rentals') int activeRentals});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$OverviewStatsImplCopyWithImpl<$Res>
|
||||
extends _$OverviewStatsCopyWithImpl<$Res, _$OverviewStatsImpl>
|
||||
implements _$$OverviewStatsImplCopyWith<$Res> {
|
||||
__$$OverviewStatsImplCopyWithImpl(
|
||||
_$OverviewStatsImpl _value, $Res Function(_$OverviewStatsImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of OverviewStats
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? totalEquipment = null,
|
||||
Object? availableEquipment = null,
|
||||
Object? inUseEquipment = null,
|
||||
Object? maintenanceEquipment = null,
|
||||
Object? totalCompanies = null,
|
||||
Object? totalUsers = null,
|
||||
Object? activeLicenses = null,
|
||||
Object? expiringLicenses = null,
|
||||
Object? totalRentals = null,
|
||||
Object? activeRentals = null,
|
||||
}) {
|
||||
return _then(_$OverviewStatsImpl(
|
||||
totalEquipment: null == totalEquipment
|
||||
? _value.totalEquipment
|
||||
: totalEquipment // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
availableEquipment: null == availableEquipment
|
||||
? _value.availableEquipment
|
||||
: availableEquipment // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
inUseEquipment: null == inUseEquipment
|
||||
? _value.inUseEquipment
|
||||
: inUseEquipment // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
maintenanceEquipment: null == maintenanceEquipment
|
||||
? _value.maintenanceEquipment
|
||||
: maintenanceEquipment // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
totalCompanies: null == totalCompanies
|
||||
? _value.totalCompanies
|
||||
: totalCompanies // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
totalUsers: null == totalUsers
|
||||
? _value.totalUsers
|
||||
: totalUsers // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
activeLicenses: null == activeLicenses
|
||||
? _value.activeLicenses
|
||||
: activeLicenses // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
expiringLicenses: null == expiringLicenses
|
||||
? _value.expiringLicenses
|
||||
: expiringLicenses // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
totalRentals: null == totalRentals
|
||||
? _value.totalRentals
|
||||
: totalRentals // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
activeRentals: null == activeRentals
|
||||
? _value.activeRentals
|
||||
: activeRentals // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$OverviewStatsImpl implements _OverviewStats {
|
||||
const _$OverviewStatsImpl(
|
||||
{@JsonKey(name: 'total_equipment') required this.totalEquipment,
|
||||
@JsonKey(name: 'available_equipment') required this.availableEquipment,
|
||||
@JsonKey(name: 'in_use_equipment') required this.inUseEquipment,
|
||||
@JsonKey(name: 'maintenance_equipment')
|
||||
required this.maintenanceEquipment,
|
||||
@JsonKey(name: 'total_companies') required this.totalCompanies,
|
||||
@JsonKey(name: 'total_users') required this.totalUsers,
|
||||
@JsonKey(name: 'active_licenses') required this.activeLicenses,
|
||||
@JsonKey(name: 'expiring_licenses') required this.expiringLicenses,
|
||||
@JsonKey(name: 'total_rentals') required this.totalRentals,
|
||||
@JsonKey(name: 'active_rentals') required this.activeRentals});
|
||||
|
||||
factory _$OverviewStatsImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$OverviewStatsImplFromJson(json);
|
||||
|
||||
@override
|
||||
@JsonKey(name: 'total_equipment')
|
||||
final int totalEquipment;
|
||||
@override
|
||||
@JsonKey(name: 'available_equipment')
|
||||
final int availableEquipment;
|
||||
@override
|
||||
@JsonKey(name: 'in_use_equipment')
|
||||
final int inUseEquipment;
|
||||
@override
|
||||
@JsonKey(name: 'maintenance_equipment')
|
||||
final int maintenanceEquipment;
|
||||
@override
|
||||
@JsonKey(name: 'total_companies')
|
||||
final int totalCompanies;
|
||||
@override
|
||||
@JsonKey(name: 'total_users')
|
||||
final int totalUsers;
|
||||
@override
|
||||
@JsonKey(name: 'active_licenses')
|
||||
final int activeLicenses;
|
||||
@override
|
||||
@JsonKey(name: 'expiring_licenses')
|
||||
final int expiringLicenses;
|
||||
@override
|
||||
@JsonKey(name: 'total_rentals')
|
||||
final int totalRentals;
|
||||
@override
|
||||
@JsonKey(name: 'active_rentals')
|
||||
final int activeRentals;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'OverviewStats(totalEquipment: $totalEquipment, availableEquipment: $availableEquipment, inUseEquipment: $inUseEquipment, maintenanceEquipment: $maintenanceEquipment, totalCompanies: $totalCompanies, totalUsers: $totalUsers, activeLicenses: $activeLicenses, expiringLicenses: $expiringLicenses, totalRentals: $totalRentals, activeRentals: $activeRentals)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$OverviewStatsImpl &&
|
||||
(identical(other.totalEquipment, totalEquipment) ||
|
||||
other.totalEquipment == totalEquipment) &&
|
||||
(identical(other.availableEquipment, availableEquipment) ||
|
||||
other.availableEquipment == availableEquipment) &&
|
||||
(identical(other.inUseEquipment, inUseEquipment) ||
|
||||
other.inUseEquipment == inUseEquipment) &&
|
||||
(identical(other.maintenanceEquipment, maintenanceEquipment) ||
|
||||
other.maintenanceEquipment == maintenanceEquipment) &&
|
||||
(identical(other.totalCompanies, totalCompanies) ||
|
||||
other.totalCompanies == totalCompanies) &&
|
||||
(identical(other.totalUsers, totalUsers) ||
|
||||
other.totalUsers == totalUsers) &&
|
||||
(identical(other.activeLicenses, activeLicenses) ||
|
||||
other.activeLicenses == activeLicenses) &&
|
||||
(identical(other.expiringLicenses, expiringLicenses) ||
|
||||
other.expiringLicenses == expiringLicenses) &&
|
||||
(identical(other.totalRentals, totalRentals) ||
|
||||
other.totalRentals == totalRentals) &&
|
||||
(identical(other.activeRentals, activeRentals) ||
|
||||
other.activeRentals == activeRentals));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
totalEquipment,
|
||||
availableEquipment,
|
||||
inUseEquipment,
|
||||
maintenanceEquipment,
|
||||
totalCompanies,
|
||||
totalUsers,
|
||||
activeLicenses,
|
||||
expiringLicenses,
|
||||
totalRentals,
|
||||
activeRentals);
|
||||
|
||||
/// Create a copy of OverviewStats
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$OverviewStatsImplCopyWith<_$OverviewStatsImpl> get copyWith =>
|
||||
__$$OverviewStatsImplCopyWithImpl<_$OverviewStatsImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$OverviewStatsImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _OverviewStats implements OverviewStats {
|
||||
const factory _OverviewStats(
|
||||
{@JsonKey(name: 'total_equipment') required final int totalEquipment,
|
||||
@JsonKey(name: 'available_equipment')
|
||||
required final int availableEquipment,
|
||||
@JsonKey(name: 'in_use_equipment') required final int inUseEquipment,
|
||||
@JsonKey(name: 'maintenance_equipment')
|
||||
required final int maintenanceEquipment,
|
||||
@JsonKey(name: 'total_companies') required final int totalCompanies,
|
||||
@JsonKey(name: 'total_users') required final int totalUsers,
|
||||
@JsonKey(name: 'active_licenses') required final int activeLicenses,
|
||||
@JsonKey(name: 'expiring_licenses') required final int expiringLicenses,
|
||||
@JsonKey(name: 'total_rentals') required final int totalRentals,
|
||||
@JsonKey(name: 'active_rentals')
|
||||
required final int activeRentals}) = _$OverviewStatsImpl;
|
||||
|
||||
factory _OverviewStats.fromJson(Map<String, dynamic> json) =
|
||||
_$OverviewStatsImpl.fromJson;
|
||||
|
||||
@override
|
||||
@JsonKey(name: 'total_equipment')
|
||||
int get totalEquipment;
|
||||
@override
|
||||
@JsonKey(name: 'available_equipment')
|
||||
int get availableEquipment;
|
||||
@override
|
||||
@JsonKey(name: 'in_use_equipment')
|
||||
int get inUseEquipment;
|
||||
@override
|
||||
@JsonKey(name: 'maintenance_equipment')
|
||||
int get maintenanceEquipment;
|
||||
@override
|
||||
@JsonKey(name: 'total_companies')
|
||||
int get totalCompanies;
|
||||
@override
|
||||
@JsonKey(name: 'total_users')
|
||||
int get totalUsers;
|
||||
@override
|
||||
@JsonKey(name: 'active_licenses')
|
||||
int get activeLicenses;
|
||||
@override
|
||||
@JsonKey(name: 'expiring_licenses')
|
||||
int get expiringLicenses;
|
||||
@override
|
||||
@JsonKey(name: 'total_rentals')
|
||||
int get totalRentals;
|
||||
@override
|
||||
@JsonKey(name: 'active_rentals')
|
||||
int get activeRentals;
|
||||
|
||||
/// Create a copy of OverviewStats
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$OverviewStatsImplCopyWith<_$OverviewStatsImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
35
lib/data/models/dashboard/overview_stats.g.dart
Normal file
35
lib/data/models/dashboard/overview_stats.g.dart
Normal file
@@ -0,0 +1,35 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'overview_stats.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$OverviewStatsImpl _$$OverviewStatsImplFromJson(Map<String, dynamic> json) =>
|
||||
_$OverviewStatsImpl(
|
||||
totalEquipment: (json['total_equipment'] as num).toInt(),
|
||||
availableEquipment: (json['available_equipment'] as num).toInt(),
|
||||
inUseEquipment: (json['in_use_equipment'] as num).toInt(),
|
||||
maintenanceEquipment: (json['maintenance_equipment'] as num).toInt(),
|
||||
totalCompanies: (json['total_companies'] as num).toInt(),
|
||||
totalUsers: (json['total_users'] as num).toInt(),
|
||||
activeLicenses: (json['active_licenses'] as num).toInt(),
|
||||
expiringLicenses: (json['expiring_licenses'] as num).toInt(),
|
||||
totalRentals: (json['total_rentals'] as num).toInt(),
|
||||
activeRentals: (json['active_rentals'] as num).toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$OverviewStatsImplToJson(_$OverviewStatsImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'total_equipment': instance.totalEquipment,
|
||||
'available_equipment': instance.availableEquipment,
|
||||
'in_use_equipment': instance.inUseEquipment,
|
||||
'maintenance_equipment': instance.maintenanceEquipment,
|
||||
'total_companies': instance.totalCompanies,
|
||||
'total_users': instance.totalUsers,
|
||||
'active_licenses': instance.activeLicenses,
|
||||
'expiring_licenses': instance.expiringLicenses,
|
||||
'total_rentals': instance.totalRentals,
|
||||
'active_rentals': instance.activeRentals,
|
||||
};
|
||||
19
lib/data/models/dashboard/recent_activity.dart
Normal file
19
lib/data/models/dashboard/recent_activity.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'recent_activity.freezed.dart';
|
||||
part 'recent_activity.g.dart';
|
||||
|
||||
@freezed
|
||||
class RecentActivity with _$RecentActivity {
|
||||
const factory RecentActivity({
|
||||
required int id,
|
||||
@JsonKey(name: 'activity_type') required String activityType,
|
||||
required String description,
|
||||
@JsonKey(name: 'user_name') required String userName,
|
||||
@JsonKey(name: 'created_at') required DateTime createdAt,
|
||||
Map<String, dynamic>? metadata,
|
||||
}) = _RecentActivity;
|
||||
|
||||
factory RecentActivity.fromJson(Map<String, dynamic> json) =>
|
||||
_$RecentActivityFromJson(json);
|
||||
}
|
||||
291
lib/data/models/dashboard/recent_activity.freezed.dart
Normal file
291
lib/data/models/dashboard/recent_activity.freezed.dart
Normal file
@@ -0,0 +1,291 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'recent_activity.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
RecentActivity _$RecentActivityFromJson(Map<String, dynamic> json) {
|
||||
return _RecentActivity.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$RecentActivity {
|
||||
int get id => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'activity_type')
|
||||
String get activityType => throw _privateConstructorUsedError;
|
||||
String get description => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'user_name')
|
||||
String get userName => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'created_at')
|
||||
DateTime get createdAt => throw _privateConstructorUsedError;
|
||||
Map<String, dynamic>? get metadata => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this RecentActivity to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of RecentActivity
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$RecentActivityCopyWith<RecentActivity> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $RecentActivityCopyWith<$Res> {
|
||||
factory $RecentActivityCopyWith(
|
||||
RecentActivity value, $Res Function(RecentActivity) then) =
|
||||
_$RecentActivityCopyWithImpl<$Res, RecentActivity>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{int id,
|
||||
@JsonKey(name: 'activity_type') String activityType,
|
||||
String description,
|
||||
@JsonKey(name: 'user_name') String userName,
|
||||
@JsonKey(name: 'created_at') DateTime createdAt,
|
||||
Map<String, dynamic>? metadata});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$RecentActivityCopyWithImpl<$Res, $Val extends RecentActivity>
|
||||
implements $RecentActivityCopyWith<$Res> {
|
||||
_$RecentActivityCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of RecentActivity
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = null,
|
||||
Object? activityType = null,
|
||||
Object? description = null,
|
||||
Object? userName = null,
|
||||
Object? createdAt = null,
|
||||
Object? metadata = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
activityType: null == activityType
|
||||
? _value.activityType
|
||||
: activityType // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
description: null == description
|
||||
? _value.description
|
||||
: description // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
userName: null == userName
|
||||
? _value.userName
|
||||
: userName // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
createdAt: null == createdAt
|
||||
? _value.createdAt
|
||||
: createdAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,
|
||||
metadata: freezed == metadata
|
||||
? _value.metadata
|
||||
: metadata // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>?,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$RecentActivityImplCopyWith<$Res>
|
||||
implements $RecentActivityCopyWith<$Res> {
|
||||
factory _$$RecentActivityImplCopyWith(_$RecentActivityImpl value,
|
||||
$Res Function(_$RecentActivityImpl) then) =
|
||||
__$$RecentActivityImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{int id,
|
||||
@JsonKey(name: 'activity_type') String activityType,
|
||||
String description,
|
||||
@JsonKey(name: 'user_name') String userName,
|
||||
@JsonKey(name: 'created_at') DateTime createdAt,
|
||||
Map<String, dynamic>? metadata});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$RecentActivityImplCopyWithImpl<$Res>
|
||||
extends _$RecentActivityCopyWithImpl<$Res, _$RecentActivityImpl>
|
||||
implements _$$RecentActivityImplCopyWith<$Res> {
|
||||
__$$RecentActivityImplCopyWithImpl(
|
||||
_$RecentActivityImpl _value, $Res Function(_$RecentActivityImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of RecentActivity
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = null,
|
||||
Object? activityType = null,
|
||||
Object? description = null,
|
||||
Object? userName = null,
|
||||
Object? createdAt = null,
|
||||
Object? metadata = freezed,
|
||||
}) {
|
||||
return _then(_$RecentActivityImpl(
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
activityType: null == activityType
|
||||
? _value.activityType
|
||||
: activityType // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
description: null == description
|
||||
? _value.description
|
||||
: description // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
userName: null == userName
|
||||
? _value.userName
|
||||
: userName // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
createdAt: null == createdAt
|
||||
? _value.createdAt
|
||||
: createdAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,
|
||||
metadata: freezed == metadata
|
||||
? _value._metadata
|
||||
: metadata // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$RecentActivityImpl implements _RecentActivity {
|
||||
const _$RecentActivityImpl(
|
||||
{required this.id,
|
||||
@JsonKey(name: 'activity_type') required this.activityType,
|
||||
required this.description,
|
||||
@JsonKey(name: 'user_name') required this.userName,
|
||||
@JsonKey(name: 'created_at') required this.createdAt,
|
||||
final Map<String, dynamic>? metadata})
|
||||
: _metadata = metadata;
|
||||
|
||||
factory _$RecentActivityImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$RecentActivityImplFromJson(json);
|
||||
|
||||
@override
|
||||
final int id;
|
||||
@override
|
||||
@JsonKey(name: 'activity_type')
|
||||
final String activityType;
|
||||
@override
|
||||
final String description;
|
||||
@override
|
||||
@JsonKey(name: 'user_name')
|
||||
final String userName;
|
||||
@override
|
||||
@JsonKey(name: 'created_at')
|
||||
final DateTime createdAt;
|
||||
final Map<String, dynamic>? _metadata;
|
||||
@override
|
||||
Map<String, dynamic>? get metadata {
|
||||
final value = _metadata;
|
||||
if (value == null) return null;
|
||||
if (_metadata is EqualUnmodifiableMapView) return _metadata;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableMapView(value);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'RecentActivity(id: $id, activityType: $activityType, description: $description, userName: $userName, createdAt: $createdAt, metadata: $metadata)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$RecentActivityImpl &&
|
||||
(identical(other.id, id) || other.id == id) &&
|
||||
(identical(other.activityType, activityType) ||
|
||||
other.activityType == activityType) &&
|
||||
(identical(other.description, description) ||
|
||||
other.description == description) &&
|
||||
(identical(other.userName, userName) ||
|
||||
other.userName == userName) &&
|
||||
(identical(other.createdAt, createdAt) ||
|
||||
other.createdAt == createdAt) &&
|
||||
const DeepCollectionEquality().equals(other._metadata, _metadata));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, id, activityType, description,
|
||||
userName, createdAt, const DeepCollectionEquality().hash(_metadata));
|
||||
|
||||
/// Create a copy of RecentActivity
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$RecentActivityImplCopyWith<_$RecentActivityImpl> get copyWith =>
|
||||
__$$RecentActivityImplCopyWithImpl<_$RecentActivityImpl>(
|
||||
this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$RecentActivityImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _RecentActivity implements RecentActivity {
|
||||
const factory _RecentActivity(
|
||||
{required final int id,
|
||||
@JsonKey(name: 'activity_type') required final String activityType,
|
||||
required final String description,
|
||||
@JsonKey(name: 'user_name') required final String userName,
|
||||
@JsonKey(name: 'created_at') required final DateTime createdAt,
|
||||
final Map<String, dynamic>? metadata}) = _$RecentActivityImpl;
|
||||
|
||||
factory _RecentActivity.fromJson(Map<String, dynamic> json) =
|
||||
_$RecentActivityImpl.fromJson;
|
||||
|
||||
@override
|
||||
int get id;
|
||||
@override
|
||||
@JsonKey(name: 'activity_type')
|
||||
String get activityType;
|
||||
@override
|
||||
String get description;
|
||||
@override
|
||||
@JsonKey(name: 'user_name')
|
||||
String get userName;
|
||||
@override
|
||||
@JsonKey(name: 'created_at')
|
||||
DateTime get createdAt;
|
||||
@override
|
||||
Map<String, dynamic>? get metadata;
|
||||
|
||||
/// Create a copy of RecentActivity
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$RecentActivityImplCopyWith<_$RecentActivityImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
28
lib/data/models/dashboard/recent_activity.g.dart
Normal file
28
lib/data/models/dashboard/recent_activity.g.dart
Normal file
@@ -0,0 +1,28 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'recent_activity.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$RecentActivityImpl _$$RecentActivityImplFromJson(Map<String, dynamic> json) =>
|
||||
_$RecentActivityImpl(
|
||||
id: (json['id'] as num).toInt(),
|
||||
activityType: json['activity_type'] as String,
|
||||
description: json['description'] as String,
|
||||
userName: json['user_name'] as String,
|
||||
createdAt: DateTime.parse(json['created_at'] as String),
|
||||
metadata: json['metadata'] as Map<String, dynamic>?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$RecentActivityImplToJson(
|
||||
_$RecentActivityImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'activity_type': instance.activityType,
|
||||
'description': instance.description,
|
||||
'user_name': instance.userName,
|
||||
'created_at': instance.createdAt.toIso8601String(),
|
||||
'metadata': instance.metadata,
|
||||
};
|
||||
@@ -4,7 +4,9 @@ import 'package:get_it/get_it.dart';
|
||||
import '../core/config/environment.dart';
|
||||
import '../data/datasources/remote/api_client.dart';
|
||||
import '../data/datasources/remote/auth_remote_datasource.dart';
|
||||
import '../data/datasources/remote/dashboard_remote_datasource.dart';
|
||||
import '../services/auth_service.dart';
|
||||
import '../services/dashboard_service.dart';
|
||||
|
||||
/// GetIt 인스턴스
|
||||
final getIt = GetIt.instance;
|
||||
@@ -25,11 +27,17 @@ Future<void> setupDependencies() async {
|
||||
getIt.registerLazySingleton<AuthRemoteDataSource>(
|
||||
() => AuthRemoteDataSourceImpl(getIt()),
|
||||
);
|
||||
getIt.registerLazySingleton<DashboardRemoteDataSource>(
|
||||
() => DashboardRemoteDataSourceImpl(getIt()),
|
||||
);
|
||||
|
||||
// 서비스
|
||||
getIt.registerLazySingleton<AuthService>(
|
||||
() => AuthServiceImpl(getIt(), getIt()),
|
||||
);
|
||||
getIt.registerLazySingleton<DashboardService>(
|
||||
() => DashboardServiceImpl(getIt()),
|
||||
);
|
||||
|
||||
// 리포지토리
|
||||
// TODO: Repositories will be registered here
|
||||
|
||||
68
lib/domain/repositories/equipment_repository.dart
Normal file
68
lib/domain/repositories/equipment_repository.dart
Normal file
@@ -0,0 +1,68 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import '../../core/errors/failures.dart';
|
||||
import '../../models/equipment_unified_model.dart';
|
||||
|
||||
/// 장비 관리 Repository 인터페이스
|
||||
abstract class EquipmentRepository {
|
||||
/// 장비 입고 목록 조회
|
||||
Future<Either<Failure, List<EquipmentIn>>> getEquipmentIns({
|
||||
int? page,
|
||||
int? limit,
|
||||
String? search,
|
||||
String? sortBy,
|
||||
String? sortOrder,
|
||||
});
|
||||
|
||||
/// 장비 입고 상세 조회
|
||||
Future<Either<Failure, EquipmentIn>> getEquipmentInById(int id);
|
||||
|
||||
/// 장비 입고 생성
|
||||
Future<Either<Failure, EquipmentIn>> createEquipmentIn(EquipmentIn equipmentIn);
|
||||
|
||||
/// 장비 입고 수정
|
||||
Future<Either<Failure, EquipmentIn>> updateEquipmentIn(int id, EquipmentIn equipmentIn);
|
||||
|
||||
/// 장비 입고 삭제
|
||||
Future<Either<Failure, void>> deleteEquipmentIn(int id);
|
||||
|
||||
/// 장비 출고 목록 조회
|
||||
Future<Either<Failure, List<EquipmentOut>>> getEquipmentOuts({
|
||||
int? page,
|
||||
int? limit,
|
||||
String? search,
|
||||
String? sortBy,
|
||||
String? sortOrder,
|
||||
});
|
||||
|
||||
/// 장비 출고 상세 조회
|
||||
Future<Either<Failure, EquipmentOut>> getEquipmentOutById(int id);
|
||||
|
||||
/// 장비 출고 생성
|
||||
Future<Either<Failure, EquipmentOut>> createEquipmentOut(EquipmentOut equipmentOut);
|
||||
|
||||
/// 장비 출고 수정
|
||||
Future<Either<Failure, EquipmentOut>> updateEquipmentOut(int id, EquipmentOut equipmentOut);
|
||||
|
||||
/// 장비 출고 삭제
|
||||
Future<Either<Failure, void>> deleteEquipmentOut(int id);
|
||||
|
||||
/// 장비 일괄 출고
|
||||
Future<Either<Failure, List<EquipmentOut>>> createBatchEquipmentOut(List<EquipmentOut> equipmentOuts);
|
||||
|
||||
/// 제조사 목록 조회
|
||||
Future<Either<Failure, List<String>>> getManufacturers();
|
||||
|
||||
/// 장비명 목록 조회
|
||||
Future<Either<Failure, List<String>>> getEquipmentNames();
|
||||
|
||||
/// 장비 이력 조회
|
||||
Future<Either<Failure, List<dynamic>>> getEquipmentHistory(int equipmentId);
|
||||
|
||||
/// 장비 검색
|
||||
Future<Either<Failure, List<Equipment>>> searchEquipment({
|
||||
String? manufacturer,
|
||||
String? name,
|
||||
String? category,
|
||||
String? serialNumber,
|
||||
});
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:superport/models/equipment_unified_model.dart';
|
||||
import 'package:superport/screens/common/app_layout_redesign.dart';
|
||||
import 'package:superport/screens/common/theme_shadcn.dart';
|
||||
@@ -8,6 +9,7 @@ import 'package:superport/screens/equipment/equipment_out_form.dart';
|
||||
import 'package:superport/screens/license/license_form.dart'; // MaintenanceFormScreen으로 사용
|
||||
import 'package:superport/screens/user/user_form.dart';
|
||||
import 'package:superport/screens/warehouse_location/warehouse_location_form.dart';
|
||||
import 'package:superport/services/auth_service.dart';
|
||||
import 'package:superport/utils/constants.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:superport/screens/login/login_screen.dart';
|
||||
@@ -29,6 +31,8 @@ class SuperportApp extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final authService = GetIt.instance<AuthService>();
|
||||
|
||||
return MaterialApp(
|
||||
title: 'supERPort',
|
||||
theme: ShadcnTheme.lightTheme,
|
||||
@@ -39,7 +43,26 @@ class SuperportApp extends StatelessWidget {
|
||||
],
|
||||
supportedLocales: const [Locale('ko', 'KR'), Locale('en', 'US')],
|
||||
locale: const Locale('ko', 'KR'),
|
||||
initialRoute: '/login',
|
||||
home: FutureBuilder<bool>(
|
||||
future: authService.isLoggedIn(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Scaffold(
|
||||
body: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (snapshot.hasData && snapshot.data!) {
|
||||
// 토큰이 유효하면 홈 화면으로
|
||||
return AppLayoutRedesign(initialRoute: Routes.home);
|
||||
} else {
|
||||
// 토큰이 없거나 유효하지 않으면 로그인 화면으로
|
||||
return const LoginScreen();
|
||||
}
|
||||
},
|
||||
),
|
||||
onGenerateRoute: (settings) {
|
||||
// 로그인 라우트 처리
|
||||
if (settings.name == '/login') {
|
||||
@@ -182,6 +205,7 @@ class SuperportApp extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
},
|
||||
navigatorKey: GlobalKey<NavigatorState>(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:superport/screens/common/theme_shadcn.dart';
|
||||
import 'package:superport/screens/common/components/shadcn_components.dart';
|
||||
import 'package:superport/screens/overview/overview_screen_redesign.dart';
|
||||
@@ -7,6 +8,7 @@ import 'package:superport/screens/company/company_list_redesign.dart';
|
||||
import 'package:superport/screens/user/user_list_redesign.dart';
|
||||
import 'package:superport/screens/license/license_list_redesign.dart';
|
||||
import 'package:superport/screens/warehouse_location/warehouse_location_list_redesign.dart';
|
||||
import 'package:superport/services/auth_service.dart';
|
||||
import 'package:superport/utils/constants.dart';
|
||||
|
||||
/// Microsoft Dynamics 365 스타일의 메인 레이아웃
|
||||
@@ -430,9 +432,43 @@ class _AppLayoutRedesignState extends State<AppLayoutRedesign>
|
||||
// 로그아웃 버튼
|
||||
ShadcnButton(
|
||||
text: '로그아웃',
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
Navigator.of(context).pushReplacementNamed('/login');
|
||||
onPressed: () async {
|
||||
// 로딩 다이얼로그 표시
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) => Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
);
|
||||
|
||||
try {
|
||||
// AuthService를 사용하여 로그아웃
|
||||
final authService = GetIt.instance<AuthService>();
|
||||
await authService.logout();
|
||||
|
||||
// 로딩 다이얼로그와 현재 모달 닫기
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop(); // 로딩 다이얼로그
|
||||
Navigator.of(context).pop(); // 프로필 메뉴
|
||||
// 로그인 화면으로 이동
|
||||
Navigator.of(context).pushNamedAndRemoveUntil(
|
||||
'/login',
|
||||
(route) => false,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
// 에러 처리
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop(); // 로딩 다이얼로그 닫기
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('로그아웃 중 오류가 발생했습니다.'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
variant: ShadcnButtonVariant.destructive,
|
||||
fullWidth: true,
|
||||
|
||||
@@ -22,7 +22,11 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
|
||||
// 로그인 성공 시 콜백 (예: overview로 이동)
|
||||
void _onLoginSuccess() {
|
||||
Navigator.of(context).pushReplacementNamed('/home');
|
||||
// 로그인 성공 시 모든 이전 라우트를 제거하고 홈으로 이동
|
||||
Navigator.of(context).pushNamedAndRemoveUntil(
|
||||
'/home',
|
||||
(route) => false,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -27,8 +27,6 @@ class _LoginViewRedesignState extends State<LoginViewRedesign>
|
||||
late AnimationController _slideController;
|
||||
late Animation<Offset> _slideAnimation;
|
||||
|
||||
bool _rememberMe = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -80,36 +78,37 @@ class _LoginViewRedesignState extends State<LoginViewRedesign>
|
||||
child: Consumer<LoginController>(
|
||||
builder: (context, controller, _) {
|
||||
return Scaffold(
|
||||
backgroundColor: ShadcnTheme.background,
|
||||
body: SafeArea(
|
||||
child: Center(
|
||||
child: SingleChildScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(ShadcnTheme.spacing6),
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 400),
|
||||
child: FadeTransition(
|
||||
opacity: _fadeAnimation,
|
||||
child: SlideTransition(
|
||||
position: _slideAnimation,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
_buildHeader(),
|
||||
const SizedBox(height: ShadcnTheme.spacing12),
|
||||
_buildLoginCard(),
|
||||
const SizedBox(height: ShadcnTheme.spacing8),
|
||||
_buildFooter(),
|
||||
],
|
||||
backgroundColor: ShadcnTheme.background,
|
||||
body: SafeArea(
|
||||
child: Center(
|
||||
child: SingleChildScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(ShadcnTheme.spacing6),
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 400),
|
||||
child: FadeTransition(
|
||||
opacity: _fadeAnimation,
|
||||
child: SlideTransition(
|
||||
position: _slideAnimation,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
_buildHeader(),
|
||||
const SizedBox(height: ShadcnTheme.spacing12),
|
||||
_buildLoginCard(),
|
||||
const SizedBox(height: ShadcnTheme.spacing8),
|
||||
_buildFooter(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -234,7 +233,7 @@ class _LoginViewRedesignState extends State<LoginViewRedesign>
|
||||
margin: const EdgeInsets.only(bottom: ShadcnTheme.spacing4),
|
||||
decoration: BoxDecoration(
|
||||
color: ShadcnTheme.destructive.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(ShadcnTheme.borderRadius),
|
||||
borderRadius: BorderRadius.circular(ShadcnTheme.radiusMd),
|
||||
border: Border.all(
|
||||
color: ShadcnTheme.destructive.withOpacity(0.3),
|
||||
),
|
||||
|
||||
@@ -1,60 +1,177 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:superport/services/mock_data_service.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:superport/data/models/dashboard/equipment_status_distribution.dart';
|
||||
import 'package:superport/data/models/dashboard/expiring_license.dart';
|
||||
import 'package:superport/data/models/dashboard/overview_stats.dart';
|
||||
import 'package:superport/data/models/dashboard/recent_activity.dart';
|
||||
import 'package:superport/services/dashboard_service.dart';
|
||||
import 'package:superport/screens/common/theme_tailwind.dart';
|
||||
|
||||
// 대시보드(Overview) 화면의 상태 및 비즈니스 로직을 담당하는 컨트롤러
|
||||
class OverviewController {
|
||||
final MockDataService dataService;
|
||||
// 대시보드(Overview) 화면의 상태 및 비즈니스 로직을 담답하는 컨트롤러
|
||||
class OverviewController extends ChangeNotifier {
|
||||
final DashboardService _dashboardService = GetIt.instance<DashboardService>();
|
||||
|
||||
int totalCompanies = 0;
|
||||
int totalUsers = 0;
|
||||
int totalEquipmentIn = 0;
|
||||
int totalEquipmentOut = 0;
|
||||
int totalLicenses = 0;
|
||||
|
||||
// 최근 활동 데이터
|
||||
List<Map<String, dynamic>> recentActivities = [];
|
||||
|
||||
OverviewController({required this.dataService});
|
||||
|
||||
// 데이터 로드 및 통계 계산
|
||||
void loadData() {
|
||||
totalCompanies = dataService.getAllCompanies().length;
|
||||
totalUsers = dataService.getAllUsers().length;
|
||||
// 실제 서비스에서는 아래 메서드 구현 필요
|
||||
totalEquipmentIn = 32; // 임시 데이터
|
||||
totalEquipmentOut = 18; // 임시 데이터
|
||||
totalLicenses = dataService.getAllLicenses().length;
|
||||
_loadRecentActivities();
|
||||
// 상태 데이터
|
||||
OverviewStats? _overviewStats;
|
||||
List<RecentActivity> _recentActivities = [];
|
||||
EquipmentStatusDistribution? _equipmentStatus;
|
||||
List<ExpiringLicense> _expiringLicenses = [];
|
||||
|
||||
// 로딩 상태
|
||||
bool _isLoadingStats = false;
|
||||
bool _isLoadingActivities = false;
|
||||
bool _isLoadingEquipmentStatus = false;
|
||||
bool _isLoadingLicenses = false;
|
||||
|
||||
// 에러 상태
|
||||
String? _statsError;
|
||||
String? _activitiesError;
|
||||
String? _equipmentStatusError;
|
||||
String? _licensesError;
|
||||
|
||||
// Getters
|
||||
OverviewStats? get overviewStats => _overviewStats;
|
||||
List<RecentActivity> get recentActivities => _recentActivities;
|
||||
EquipmentStatusDistribution? get equipmentStatus => _equipmentStatus;
|
||||
List<ExpiringLicense> get expiringLicenses => _expiringLicenses;
|
||||
|
||||
bool get isLoading => _isLoadingStats || _isLoadingActivities ||
|
||||
_isLoadingEquipmentStatus || _isLoadingLicenses;
|
||||
|
||||
String? get error {
|
||||
return _statsError ?? _activitiesError ??
|
||||
_equipmentStatusError ?? _licensesError;
|
||||
}
|
||||
|
||||
// 최근 활동 데이터 로드 (임시 데이터)
|
||||
void _loadRecentActivities() {
|
||||
recentActivities = [
|
||||
{
|
||||
'type': '장비 입고',
|
||||
'title': '라우터 입고 처리 완료',
|
||||
'time': '30분 전',
|
||||
'user': '홍길동',
|
||||
'icon': Icons.input,
|
||||
'color': AppThemeTailwind.success,
|
||||
OverviewController();
|
||||
|
||||
// 데이터 로드
|
||||
Future<void> loadData() async {
|
||||
await Future.wait([
|
||||
_loadOverviewStats(),
|
||||
_loadRecentActivities(),
|
||||
_loadEquipmentStatus(),
|
||||
_loadExpiringLicenses(),
|
||||
]);
|
||||
}
|
||||
|
||||
// 개별 데이터 로드 메서드
|
||||
Future<void> _loadOverviewStats() async {
|
||||
_isLoadingStats = true;
|
||||
_statsError = null;
|
||||
notifyListeners();
|
||||
|
||||
final result = await _dashboardService.getOverviewStats();
|
||||
|
||||
result.fold(
|
||||
(failure) {
|
||||
_statsError = failure.message;
|
||||
},
|
||||
{
|
||||
'type': '사용자 추가',
|
||||
'title': '새 관리자 등록',
|
||||
'time': '1시간 전',
|
||||
'user': '김철수',
|
||||
'icon': Icons.person_add,
|
||||
'color': AppThemeTailwind.primary,
|
||||
(stats) {
|
||||
_overviewStats = stats;
|
||||
},
|
||||
{
|
||||
'type': '장비 출고',
|
||||
'title': '모니터 5대 출고 처리',
|
||||
'time': '2시간 전',
|
||||
'user': '이영희',
|
||||
'icon': Icons.output,
|
||||
'color': AppThemeTailwind.warning,
|
||||
);
|
||||
|
||||
_isLoadingStats = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> _loadRecentActivities() async {
|
||||
_isLoadingActivities = true;
|
||||
_activitiesError = null;
|
||||
notifyListeners();
|
||||
|
||||
final result = await _dashboardService.getRecentActivities();
|
||||
|
||||
result.fold(
|
||||
(failure) {
|
||||
_activitiesError = failure.message;
|
||||
},
|
||||
];
|
||||
(activities) {
|
||||
_recentActivities = activities;
|
||||
},
|
||||
);
|
||||
|
||||
_isLoadingActivities = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> _loadEquipmentStatus() async {
|
||||
_isLoadingEquipmentStatus = true;
|
||||
_equipmentStatusError = null;
|
||||
notifyListeners();
|
||||
|
||||
final result = await _dashboardService.getEquipmentStatusDistribution();
|
||||
|
||||
result.fold(
|
||||
(failure) {
|
||||
_equipmentStatusError = failure.message;
|
||||
},
|
||||
(status) {
|
||||
_equipmentStatus = status;
|
||||
},
|
||||
);
|
||||
|
||||
_isLoadingEquipmentStatus = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> _loadExpiringLicenses() async {
|
||||
_isLoadingLicenses = true;
|
||||
_licensesError = null;
|
||||
notifyListeners();
|
||||
|
||||
final result = await _dashboardService.getExpiringLicenses(days: 30);
|
||||
|
||||
result.fold(
|
||||
(failure) {
|
||||
_licensesError = failure.message;
|
||||
},
|
||||
(licenses) {
|
||||
_expiringLicenses = licenses;
|
||||
},
|
||||
);
|
||||
|
||||
_isLoadingLicenses = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// 활동 타입별 아이콘과 색상 가져오기
|
||||
IconData getActivityIcon(String activityType) {
|
||||
switch (activityType.toLowerCase()) {
|
||||
case 'equipment_in':
|
||||
case '장비 입고':
|
||||
return Icons.input;
|
||||
case 'equipment_out':
|
||||
case '장비 출고':
|
||||
return Icons.output;
|
||||
case 'user_create':
|
||||
case '사용자 추가':
|
||||
return Icons.person_add;
|
||||
case 'license_create':
|
||||
case '라이선스 등록':
|
||||
return Icons.vpn_key;
|
||||
default:
|
||||
return Icons.notifications;
|
||||
}
|
||||
}
|
||||
|
||||
Color getActivityColor(String activityType) {
|
||||
switch (activityType.toLowerCase()) {
|
||||
case 'equipment_in':
|
||||
case '장비 입고':
|
||||
return AppThemeTailwind.success;
|
||||
case 'equipment_out':
|
||||
case '장비 출고':
|
||||
return AppThemeTailwind.warning;
|
||||
case 'user_create':
|
||||
case '사용자 추가':
|
||||
return AppThemeTailwind.primary;
|
||||
case 'license_create':
|
||||
case '라이선스 등록':
|
||||
return AppThemeTailwind.info;
|
||||
default:
|
||||
return AppThemeTailwind.muted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,41 +14,45 @@ class OverviewScreenRedesign extends StatefulWidget {
|
||||
|
||||
class _OverviewScreenRedesignState extends State<OverviewScreenRedesign> {
|
||||
late final OverviewController _controller;
|
||||
bool _isLoading = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = OverviewController(dataService: MockDataService());
|
||||
_controller = OverviewController();
|
||||
_loadData();
|
||||
}
|
||||
|
||||
Future<void> _loadData() async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
await _controller.loadDashboardData();
|
||||
}
|
||||
|
||||
await Future.delayed(const Duration(milliseconds: 800));
|
||||
_controller.loadData();
|
||||
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_isLoading) {
|
||||
return _buildLoadingState();
|
||||
}
|
||||
return ChangeNotifierProvider.value(
|
||||
value: _controller,
|
||||
child: Consumer<OverviewController>(
|
||||
builder: (context, controller, child) {
|
||||
if (controller.isLoading) {
|
||||
return _buildLoadingState();
|
||||
}
|
||||
|
||||
return Container(
|
||||
color: ShadcnTheme.background,
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (controller.error != null) {
|
||||
return _buildErrorState(controller.error!);
|
||||
}
|
||||
|
||||
return Container(
|
||||
color: ShadcnTheme.background,
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 환영 섹션
|
||||
ShadcnCard(
|
||||
padding: const EdgeInsets.all(32),
|
||||
@@ -164,6 +168,9 @@ class _OverviewScreenRedesignState extends State<OverviewScreenRedesign> {
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -183,6 +190,34 @@ class _OverviewScreenRedesignState extends State<OverviewScreenRedesign> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildErrorState(String error) {
|
||||
return Container(
|
||||
color: ShadcnTheme.background,
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.error_outline,
|
||||
size: 48,
|
||||
color: ShadcnTheme.error,
|
||||
),
|
||||
const SizedBox(height: ShadcnTheme.spacing4),
|
||||
Text('오류가 발생했습니다', style: ShadcnTheme.headingH4),
|
||||
const SizedBox(height: ShadcnTheme.spacing2),
|
||||
Text(error, style: ShadcnTheme.bodyMuted),
|
||||
const SizedBox(height: ShadcnTheme.spacing4),
|
||||
ShadcnButton(
|
||||
text: '다시 시도',
|
||||
onPressed: _loadData,
|
||||
variant: ShadcnButtonVariant.primary,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLeftColumn() {
|
||||
return Column(
|
||||
children: [
|
||||
@@ -261,7 +296,27 @@ class _OverviewScreenRedesignState extends State<OverviewScreenRedesign> {
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
...List.generate(5, (index) => _buildActivityItem(index)),
|
||||
Consumer<OverviewController>(
|
||||
builder: (context, controller, child) {
|
||||
final activities = controller.recentActivities ?? [];
|
||||
if (activities.isEmpty) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'최근 활동이 없습니다',
|
||||
style: ShadcnTheme.bodyMuted,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return Column(
|
||||
children: activities.take(5).map((activity) =>
|
||||
_buildActivityItem(activity),
|
||||
).toList(),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -376,41 +431,41 @@ class _OverviewScreenRedesignState extends State<OverviewScreenRedesign> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActivityItem(int index) {
|
||||
final activities = [
|
||||
{
|
||||
'icon': Icons.inventory,
|
||||
'title': '장비 입고 처리',
|
||||
'subtitle': '크레인 #CR-001 입고 완료',
|
||||
'time': '2분 전',
|
||||
},
|
||||
{
|
||||
'icon': Icons.local_shipping,
|
||||
'title': '장비 출고 처리',
|
||||
'subtitle': '포클레인 #FK-005 출고 완료',
|
||||
'time': '5분 전',
|
||||
},
|
||||
{
|
||||
'icon': Icons.business,
|
||||
'title': '회사 등록',
|
||||
'subtitle': '새로운 회사 "ABC건설" 등록',
|
||||
'time': '10분 전',
|
||||
},
|
||||
{
|
||||
'icon': Icons.person_add,
|
||||
'title': '사용자 추가',
|
||||
'subtitle': '신규 사용자 계정 생성',
|
||||
'time': '15분 전',
|
||||
},
|
||||
{
|
||||
'icon': Icons.settings,
|
||||
'title': '시스템 점검',
|
||||
'subtitle': '정기 시스템 점검 완료',
|
||||
'time': '30분 전',
|
||||
},
|
||||
];
|
||||
Widget _buildActivityItem(dynamic activity) {
|
||||
// 아이콘 매핑
|
||||
IconData getActivityIcon(String type) {
|
||||
switch (type) {
|
||||
case 'equipment_in':
|
||||
return Icons.inventory;
|
||||
case 'equipment_out':
|
||||
return Icons.local_shipping;
|
||||
case 'company':
|
||||
return Icons.business;
|
||||
case 'user':
|
||||
return Icons.person_add;
|
||||
default:
|
||||
return Icons.settings;
|
||||
}
|
||||
}
|
||||
|
||||
final activity = activities[index];
|
||||
// 색상 매핑
|
||||
Color getActivityColor(String type) {
|
||||
switch (type) {
|
||||
case 'equipment_in':
|
||||
return ShadcnTheme.success;
|
||||
case 'equipment_out':
|
||||
return ShadcnTheme.warning;
|
||||
case 'company':
|
||||
return ShadcnTheme.info;
|
||||
case 'user':
|
||||
return ShadcnTheme.primary;
|
||||
default:
|
||||
return ShadcnTheme.mutedForeground;
|
||||
}
|
||||
}
|
||||
|
||||
final color = getActivityColor(activity.type);
|
||||
final dateFormat = DateFormat('MM/dd HH:mm');
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
@@ -419,12 +474,12 @@ class _OverviewScreenRedesignState extends State<OverviewScreenRedesign> {
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: ShadcnTheme.success.withOpacity(0.1),
|
||||
color: color.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Icon(
|
||||
activity['icon'] as IconData,
|
||||
color: ShadcnTheme.success,
|
||||
getActivityIcon(activity.type),
|
||||
color: color,
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
@@ -434,17 +489,20 @@ class _OverviewScreenRedesignState extends State<OverviewScreenRedesign> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
activity['title'] as String,
|
||||
activity.title,
|
||||
style: ShadcnTheme.bodyMedium,
|
||||
),
|
||||
Text(
|
||||
activity['subtitle'] as String,
|
||||
activity.description,
|
||||
style: ShadcnTheme.bodySmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Text(activity['time'] as String, style: ShadcnTheme.bodySmall),
|
||||
Text(
|
||||
dateFormat.format(activity.createdAt),
|
||||
style: ShadcnTheme.bodySmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
42
lib/services/dashboard_service.dart
Normal file
42
lib/services/dashboard_service.dart
Normal file
@@ -0,0 +1,42 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:superport/core/errors/failures.dart';
|
||||
import 'package:superport/data/datasources/remote/dashboard_remote_datasource.dart';
|
||||
import 'package:superport/data/models/dashboard/equipment_status_distribution.dart';
|
||||
import 'package:superport/data/models/dashboard/expiring_license.dart';
|
||||
import 'package:superport/data/models/dashboard/overview_stats.dart';
|
||||
import 'package:superport/data/models/dashboard/recent_activity.dart';
|
||||
|
||||
abstract class DashboardService {
|
||||
Future<Either<Failure, OverviewStats>> getOverviewStats();
|
||||
Future<Either<Failure, List<RecentActivity>>> getRecentActivities();
|
||||
Future<Either<Failure, EquipmentStatusDistribution>> getEquipmentStatusDistribution();
|
||||
Future<Either<Failure, List<ExpiringLicense>>> getExpiringLicenses({int days = 30});
|
||||
}
|
||||
|
||||
@LazySingleton(as: DashboardService)
|
||||
class DashboardServiceImpl implements DashboardService {
|
||||
final DashboardRemoteDataSource _remoteDataSource;
|
||||
|
||||
DashboardServiceImpl(this._remoteDataSource);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, OverviewStats>> getOverviewStats() async {
|
||||
return await _remoteDataSource.getOverviewStats();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<RecentActivity>>> getRecentActivities() async {
|
||||
return await _remoteDataSource.getRecentActivities();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, EquipmentStatusDistribution>> getEquipmentStatusDistribution() async {
|
||||
return await _remoteDataSource.getEquipmentStatusDistribution();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<ExpiringLicense>>> getExpiringLicenses({int days = 30}) async {
|
||||
return await _remoteDataSource.getExpiringLicenses(days: days);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user