feat: API 연동 개선 및 라이선스 모델 확장
- 라이선스 모델 전면 개편 (상세 필드 추가, 계산 필드 구현) - API 응답 처리 개선 (HTTP 상태 코드 기반) - 장비 출고 폼 컨트롤러 추가 - 회사 지점 정보 모델 추가 - 공통 데이터 모델 구조 추가 - 전체 서비스 레이어 API 호출 방식 통일 - UI 컴포넌트 마이너 개선
This commit is contained in:
@@ -1138,6 +1138,30 @@ class ErrorHandler {
|
||||
- 주소 정보 관리 개선
|
||||
- **DI 설정 완료**: WarehouseRemoteDataSource, WarehouseService 등록
|
||||
|
||||
#### 9차 작업 (2025-07-25)
|
||||
19. **라이선스 관리 API 추가 개선**
|
||||
- **License 모델 확장**:
|
||||
- 기존 단순 모델에서 API 호환 모델로 전면 개편
|
||||
- licenseKey, productName, vendor, userCount 등 필드 추가
|
||||
- 계산 필드 추가 (daysUntilExpiry, isExpired, status)
|
||||
- 기존 코드 호환을 위한 getter 추가 (name, durationMonths, visitCycle)
|
||||
- **LicenseService 개선**:
|
||||
- 새로운 License 모델에 맞춰 DTO 변환 로직 수정
|
||||
- createLicense, updateLicense 메서드 간소화
|
||||
- **LicenseListController 추가 개선**:
|
||||
- Environment.useApi 사용으로 Feature Flag 개선
|
||||
- 검색 디바운싱 추가 (300ms)
|
||||
- 정렬 기능 추가 (sortBy, sortOrder)
|
||||
- 상태별 라이선스 개수 조회 메서드 추가
|
||||
- dispose 메서드 추가 (타이머 정리)
|
||||
- **작업 현황**:
|
||||
- DTO 모델 생성 ✅
|
||||
- RemoteDataSource 구현 ✅
|
||||
- Service 구현 ✅
|
||||
- DI 설정 ✅
|
||||
- Controller 개선 ✅
|
||||
- 화면 Provider 패턴 적용 🔲 (다음 작업)
|
||||
|
||||
---
|
||||
|
||||
_마지막 업데이트: 2025-07-24_ (라이선스 및 창고 관리 API 연동 100% 완료. 모든 핵심 기능 API 통합 완료!)
|
||||
_마지막 업데이트: 2025-07-25_ (라이선스 관리 API 개선 완료. 다음 목표: 화면 Provider 패턴 적용)
|
||||
@@ -74,4 +74,9 @@ class ApiEndpoints {
|
||||
// 검색 및 조회
|
||||
static const String lookups = '/lookups';
|
||||
static const String categories = '/lookups/categories';
|
||||
|
||||
// 동적 엔드포인트 생성 메서드
|
||||
static String licenseById(String id) => '/licenses/$id';
|
||||
static String assignLicense(String id) => '/licenses/$id/assign';
|
||||
static String unassignLicense(String id) => '/licenses/$id/unassign';
|
||||
}
|
||||
@@ -114,4 +114,20 @@ class BusinessException implements Exception {
|
||||
|
||||
@override
|
||||
String toString() => 'BusinessException: $message (code: $code)';
|
||||
}
|
||||
|
||||
/// API 관련 예외
|
||||
class ApiException implements Exception {
|
||||
final String message;
|
||||
final int? statusCode;
|
||||
final Map<String, dynamic>? errors;
|
||||
|
||||
ApiException({
|
||||
required this.message,
|
||||
this.statusCode,
|
||||
this.errors,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() => 'ApiException: $message (code: $statusCode)';
|
||||
}
|
||||
@@ -28,43 +28,43 @@ class ServerFailure extends Failure {
|
||||
final Map<String, dynamic>? errors;
|
||||
|
||||
const ServerFailure({
|
||||
required String message,
|
||||
String? code,
|
||||
required super.message,
|
||||
super.code,
|
||||
this.statusCode,
|
||||
this.errors,
|
||||
}) : super(message: message, code: code);
|
||||
});
|
||||
}
|
||||
|
||||
/// 캐시 실패
|
||||
class CacheFailure extends Failure {
|
||||
const CacheFailure({
|
||||
required String message,
|
||||
String? code,
|
||||
}) : super(message: message, code: code);
|
||||
required super.message,
|
||||
super.code,
|
||||
});
|
||||
}
|
||||
|
||||
/// 네트워크 실패
|
||||
class NetworkFailure extends Failure {
|
||||
const NetworkFailure({
|
||||
required String message,
|
||||
String? code,
|
||||
}) : super(message: message, code: code);
|
||||
required super.message,
|
||||
super.code,
|
||||
});
|
||||
}
|
||||
|
||||
/// 인증 실패
|
||||
class AuthenticationFailure extends Failure {
|
||||
const AuthenticationFailure({
|
||||
required String message,
|
||||
String? code,
|
||||
}) : super(message: message, code: code);
|
||||
required super.message,
|
||||
super.code,
|
||||
});
|
||||
}
|
||||
|
||||
/// 권한 실패
|
||||
class AuthorizationFailure extends Failure {
|
||||
const AuthorizationFailure({
|
||||
required String message,
|
||||
String? code,
|
||||
}) : super(message: message, code: code);
|
||||
required super.message,
|
||||
super.code,
|
||||
});
|
||||
}
|
||||
|
||||
/// 유효성 검사 실패
|
||||
@@ -72,10 +72,10 @@ class ValidationFailure extends Failure {
|
||||
final Map<String, List<String>>? fieldErrors;
|
||||
|
||||
const ValidationFailure({
|
||||
required String message,
|
||||
String? code,
|
||||
required super.message,
|
||||
super.code,
|
||||
this.fieldErrors,
|
||||
}) : super(message: message, code: code);
|
||||
});
|
||||
}
|
||||
|
||||
/// 리소스 찾을 수 없음 실패
|
||||
@@ -84,11 +84,11 @@ class NotFoundFailure extends Failure {
|
||||
final String? resourceId;
|
||||
|
||||
const NotFoundFailure({
|
||||
required String message,
|
||||
String? code,
|
||||
required super.message,
|
||||
super.code,
|
||||
this.resourceType,
|
||||
this.resourceId,
|
||||
}) : super(message: message, code: code);
|
||||
});
|
||||
}
|
||||
|
||||
/// 중복 리소스 실패
|
||||
@@ -97,19 +97,19 @@ class DuplicateFailure extends Failure {
|
||||
final String? value;
|
||||
|
||||
const DuplicateFailure({
|
||||
required String message,
|
||||
String? code,
|
||||
required super.message,
|
||||
super.code,
|
||||
this.field,
|
||||
this.value,
|
||||
}) : super(message: message, code: code);
|
||||
});
|
||||
}
|
||||
|
||||
/// 비즈니스 로직 실패
|
||||
class BusinessFailure extends Failure {
|
||||
const BusinessFailure({
|
||||
required String message,
|
||||
String? code,
|
||||
}) : super(message: message, code: code);
|
||||
required super.message,
|
||||
super.code,
|
||||
});
|
||||
}
|
||||
|
||||
/// 타입 정의
|
||||
|
||||
@@ -29,12 +29,12 @@ class AuthRemoteDataSourceImpl implements AuthRemoteDataSource {
|
||||
data: request.toJson(),
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
if (response.statusCode == 200 && response.data != null) {
|
||||
final loginResponse = LoginResponse.fromJson(response.data);
|
||||
return Right(loginResponse);
|
||||
} else {
|
||||
return Left(ServerFailure(
|
||||
message: response.error?.message ?? '로그인 실패',
|
||||
message: response.statusMessage ?? '로그인 실패',
|
||||
));
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -58,11 +58,11 @@ class AuthRemoteDataSourceImpl implements AuthRemoteDataSource {
|
||||
data: request.toJson(),
|
||||
);
|
||||
|
||||
if (response.success) {
|
||||
if (response.statusCode == 200) {
|
||||
return const Right(null);
|
||||
} else {
|
||||
return Left(ServerFailure(
|
||||
message: response.error?.message ?? '로그아웃 실패',
|
||||
message: response.statusMessage ?? '로그아웃 실패',
|
||||
));
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -81,12 +81,12 @@ class AuthRemoteDataSourceImpl implements AuthRemoteDataSource {
|
||||
data: request.toJson(),
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
if (response.statusCode == 200 && response.data != null) {
|
||||
final tokenResponse = TokenResponse.fromJson(response.data);
|
||||
return Right(tokenResponse);
|
||||
} else {
|
||||
return Left(ServerFailure(
|
||||
message: response.error?.message ?? '토큰 갱신 실패',
|
||||
message: response.statusMessage ?? '토큰 갱신 실패',
|
||||
));
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:superport/core/constants/api_endpoints.dart';
|
||||
import 'package:superport/core/errors/exceptions.dart';
|
||||
@@ -80,11 +79,11 @@ class CompanyRemoteDataSourceImpl implements CompanyRemoteDataSource {
|
||||
final apiResponse = ApiResponse<PaginatedResponse<CompanyListDto>>.fromJson(
|
||||
response.data,
|
||||
(json) => PaginatedResponse<CompanyListDto>.fromJson(
|
||||
json,
|
||||
(item) => CompanyListDto.fromJson(item),
|
||||
json as Map<String, dynamic>,
|
||||
(item) => CompanyListDto.fromJson(item as Map<String, dynamic>),
|
||||
),
|
||||
);
|
||||
return apiResponse.data;
|
||||
return apiResponse.data!;
|
||||
} else {
|
||||
throw ApiException(
|
||||
message: 'Failed to load companies',
|
||||
@@ -108,9 +107,9 @@ class CompanyRemoteDataSourceImpl implements CompanyRemoteDataSource {
|
||||
if (response.statusCode == 201) {
|
||||
final apiResponse = ApiResponse<CompanyResponse>.fromJson(
|
||||
response.data,
|
||||
(json) => CompanyResponse.fromJson(json),
|
||||
(json) => CompanyResponse.fromJson(json as Map<String, dynamic>),
|
||||
);
|
||||
return apiResponse.data;
|
||||
return apiResponse.data!;
|
||||
} else {
|
||||
throw ApiException(
|
||||
message: 'Failed to create company',
|
||||
@@ -134,7 +133,7 @@ class CompanyRemoteDataSourceImpl implements CompanyRemoteDataSource {
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(
|
||||
message: e.response?.data['message'] ?? 'Failed to fetch company detail',
|
||||
code: e.response?.statusCode,
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -150,7 +149,7 @@ class CompanyRemoteDataSourceImpl implements CompanyRemoteDataSource {
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(
|
||||
message: e.response?.data['message'] ?? 'Failed to fetch company with branches',
|
||||
code: e.response?.statusCode,
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -167,7 +166,7 @@ class CompanyRemoteDataSourceImpl implements CompanyRemoteDataSource {
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(
|
||||
message: e.response?.data['message'] ?? 'Failed to update company',
|
||||
code: e.response?.statusCode,
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -181,7 +180,7 @@ class CompanyRemoteDataSourceImpl implements CompanyRemoteDataSource {
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(
|
||||
message: e.response?.data['message'] ?? 'Failed to delete company',
|
||||
code: e.response?.statusCode,
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -198,7 +197,7 @@ class CompanyRemoteDataSourceImpl implements CompanyRemoteDataSource {
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(
|
||||
message: e.response?.data['message'] ?? 'Failed to fetch company names',
|
||||
code: e.response?.statusCode,
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -216,7 +215,7 @@ class CompanyRemoteDataSourceImpl implements CompanyRemoteDataSource {
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(
|
||||
message: e.response?.data['message'] ?? 'Failed to create branch',
|
||||
code: e.response?.statusCode,
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -232,7 +231,7 @@ class CompanyRemoteDataSourceImpl implements CompanyRemoteDataSource {
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(
|
||||
message: e.response?.data['message'] ?? 'Failed to fetch branch detail',
|
||||
code: e.response?.statusCode,
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -249,7 +248,7 @@ class CompanyRemoteDataSourceImpl implements CompanyRemoteDataSource {
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(
|
||||
message: e.response?.data['message'] ?? 'Failed to update branch',
|
||||
code: e.response?.statusCode,
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -263,7 +262,7 @@ class CompanyRemoteDataSourceImpl implements CompanyRemoteDataSource {
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(
|
||||
message: e.response?.data['message'] ?? 'Failed to delete branch',
|
||||
code: e.response?.statusCode,
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -280,7 +279,7 @@ class CompanyRemoteDataSourceImpl implements CompanyRemoteDataSource {
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(
|
||||
message: e.response?.data['message'] ?? 'Failed to fetch company branches',
|
||||
code: e.response?.statusCode,
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -297,7 +296,7 @@ class CompanyRemoteDataSourceImpl implements CompanyRemoteDataSource {
|
||||
.map((item) => CompanyWithBranches.fromJson(item))
|
||||
.toList(),
|
||||
);
|
||||
return apiResponse.data;
|
||||
return apiResponse.data!;
|
||||
} else {
|
||||
throw ApiException(
|
||||
message: 'Failed to load companies with branches',
|
||||
@@ -321,9 +320,9 @@ class CompanyRemoteDataSourceImpl implements CompanyRemoteDataSource {
|
||||
if (response.statusCode == 200) {
|
||||
final apiResponse = ApiResponse<Map<String, dynamic>>.fromJson(
|
||||
response.data,
|
||||
(json) => json,
|
||||
(json) => json as Map<String, dynamic>,
|
||||
);
|
||||
return apiResponse.data['exists'] ?? false;
|
||||
return apiResponse.data?['exists'] ?? false;
|
||||
} else {
|
||||
throw ApiException(
|
||||
message: 'Failed to check duplicate',
|
||||
@@ -351,7 +350,7 @@ class CompanyRemoteDataSourceImpl implements CompanyRemoteDataSource {
|
||||
.map((item) => CompanyListDto.fromJson(item))
|
||||
.toList(),
|
||||
);
|
||||
return apiResponse.data;
|
||||
return apiResponse.data!;
|
||||
} else {
|
||||
throw ApiException(
|
||||
message: 'Failed to search companies',
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
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';
|
||||
@@ -32,12 +30,12 @@ class DashboardRemoteDataSourceImpl implements DashboardRemoteDataSource {
|
||||
final stats = OverviewStats.fromJson(response.data);
|
||||
return Right(stats);
|
||||
} else {
|
||||
return Left(ServerFailure('응답 데이터가 없습니다'));
|
||||
return Left(ServerFailure(message: '응답 데이터가 없습니다'));
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
return Left(_handleDioError(e));
|
||||
} catch (e) {
|
||||
return Left(ServerFailure('통계 데이터를 가져오는 중 오류가 발생했습니다'));
|
||||
return Left(ServerFailure(message: '통계 데이터를 가져오는 중 오류가 발생했습니다'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,12 +50,12 @@ class DashboardRemoteDataSourceImpl implements DashboardRemoteDataSource {
|
||||
.toList();
|
||||
return Right(activities);
|
||||
} else {
|
||||
return Left(ServerFailure('응답 데이터가 올바르지 않습니다'));
|
||||
return Left(ServerFailure(message: '응답 데이터가 올바르지 않습니다'));
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
return Left(_handleDioError(e));
|
||||
} catch (e) {
|
||||
return Left(ServerFailure('최근 활동을 가져오는 중 오류가 발생했습니다'));
|
||||
return Left(ServerFailure(message: '최근 활동을 가져오는 중 오류가 발생했습니다'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,12 +68,12 @@ class DashboardRemoteDataSourceImpl implements DashboardRemoteDataSource {
|
||||
final distribution = EquipmentStatusDistribution.fromJson(response.data);
|
||||
return Right(distribution);
|
||||
} else {
|
||||
return Left(ServerFailure('응답 데이터가 없습니다'));
|
||||
return Left(ServerFailure(message: '응답 데이터가 없습니다'));
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
return Left(_handleDioError(e));
|
||||
} catch (e) {
|
||||
return Left(ServerFailure('장비 상태 분포를 가져오는 중 오류가 발생했습니다'));
|
||||
return Left(ServerFailure(message: '장비 상태 분포를 가져오는 중 오류가 발생했습니다'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,12 +91,12 @@ class DashboardRemoteDataSourceImpl implements DashboardRemoteDataSource {
|
||||
.toList();
|
||||
return Right(licenses);
|
||||
} else {
|
||||
return Left(ServerFailure('응답 데이터가 올바르지 않습니다'));
|
||||
return Left(ServerFailure(message: '응답 데이터가 올바르지 않습니다'));
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
return Left(_handleDioError(e));
|
||||
} catch (e) {
|
||||
return Left(ServerFailure('만료 예정 라이선스를 가져오는 중 오류가 발생했습니다'));
|
||||
return Left(ServerFailure(message: '만료 예정 라이선스를 가져오는 중 오류가 발생했습니다'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,26 +105,26 @@ class DashboardRemoteDataSourceImpl implements DashboardRemoteDataSource {
|
||||
case DioExceptionType.connectionTimeout:
|
||||
case DioExceptionType.sendTimeout:
|
||||
case DioExceptionType.receiveTimeout:
|
||||
return NetworkFailure('네트워크 연결 시간이 초과되었습니다');
|
||||
return NetworkFailure(message: '네트워크 연결 시간이 초과되었습니다');
|
||||
case DioExceptionType.connectionError:
|
||||
return NetworkFailure('서버에 연결할 수 없습니다');
|
||||
return NetworkFailure(message: '서버에 연결할 수 없습니다');
|
||||
case DioExceptionType.badResponse:
|
||||
final statusCode = error.response?.statusCode ?? 0;
|
||||
final message = error.response?.data?['message'] ?? '서버 오류가 발생했습니다';
|
||||
|
||||
if (statusCode == 401) {
|
||||
return AuthFailure('인증이 만료되었습니다');
|
||||
return AuthenticationFailure(message: '인증이 만료되었습니다');
|
||||
} else if (statusCode == 403) {
|
||||
return AuthFailure('접근 권한이 없습니다');
|
||||
return AuthenticationFailure(message: '접근 권한이 없습니다');
|
||||
} else if (statusCode >= 400 && statusCode < 500) {
|
||||
return ServerFailure(message);
|
||||
return ServerFailure(message: message);
|
||||
} else {
|
||||
return ServerFailure('서버 오류가 발생했습니다 ($statusCode)');
|
||||
return ServerFailure(message: '서버 오류가 발생했습니다 ($statusCode)');
|
||||
}
|
||||
case DioExceptionType.cancel:
|
||||
return ServerFailure('요청이 취소되었습니다');
|
||||
return ServerFailure(message: '요청이 취소되었습니다');
|
||||
default:
|
||||
return ServerFailure('알 수 없는 오류가 발생했습니다');
|
||||
return ServerFailure(message: '알 수 없는 오류가 발생했습니다');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import 'package:superport/core/errors/exceptions.dart';
|
||||
import 'package:superport/data/datasources/remote/api_client.dart';
|
||||
import 'package:superport/data/models/license/license_dto.dart';
|
||||
import 'package:superport/data/models/license/license_request_dto.dart';
|
||||
import 'package:superport/data/models/license/license_query_dto.dart';
|
||||
|
||||
abstract class LicenseRemoteDataSource {
|
||||
Future<LicenseListResponseDto> getLicenses({
|
||||
|
||||
20
lib/data/models/common/api_response.dart
Normal file
20
lib/data/models/common/api_response.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'api_response.freezed.dart';
|
||||
part 'api_response.g.dart';
|
||||
|
||||
@Freezed(genericArgumentFactories: true)
|
||||
class ApiResponse<T> with _$ApiResponse<T> {
|
||||
const factory ApiResponse({
|
||||
required bool success,
|
||||
required String message,
|
||||
T? data,
|
||||
String? error,
|
||||
}) = _ApiResponse<T>;
|
||||
|
||||
factory ApiResponse.fromJson(
|
||||
Map<String, dynamic> json,
|
||||
T Function(Object?) fromJsonT,
|
||||
) =>
|
||||
_$ApiResponseFromJson<T>(json, fromJsonT);
|
||||
}
|
||||
221
lib/data/models/common/api_response.freezed.dart
Normal file
221
lib/data/models/common/api_response.freezed.dart
Normal file
@@ -0,0 +1,221 @@
|
||||
// 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 'api_response.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');
|
||||
|
||||
ApiResponse<T> _$ApiResponseFromJson<T>(
|
||||
Map<String, dynamic> json, T Function(Object?) fromJsonT) {
|
||||
return _ApiResponse<T>.fromJson(json, fromJsonT);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$ApiResponse<T> {
|
||||
bool get success => throw _privateConstructorUsedError;
|
||||
String get message => throw _privateConstructorUsedError;
|
||||
T? get data => throw _privateConstructorUsedError;
|
||||
String? get error => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this ApiResponse to a JSON map.
|
||||
Map<String, dynamic> toJson(Object? Function(T) toJsonT) =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of ApiResponse
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$ApiResponseCopyWith<T, ApiResponse<T>> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $ApiResponseCopyWith<T, $Res> {
|
||||
factory $ApiResponseCopyWith(
|
||||
ApiResponse<T> value, $Res Function(ApiResponse<T>) then) =
|
||||
_$ApiResponseCopyWithImpl<T, $Res, ApiResponse<T>>;
|
||||
@useResult
|
||||
$Res call({bool success, String message, T? data, String? error});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$ApiResponseCopyWithImpl<T, $Res, $Val extends ApiResponse<T>>
|
||||
implements $ApiResponseCopyWith<T, $Res> {
|
||||
_$ApiResponseCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of ApiResponse
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? success = null,
|
||||
Object? message = null,
|
||||
Object? data = freezed,
|
||||
Object? error = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
success: null == success
|
||||
? _value.success
|
||||
: success // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
message: null == message
|
||||
? _value.message
|
||||
: message // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
data: freezed == data
|
||||
? _value.data
|
||||
: data // ignore: cast_nullable_to_non_nullable
|
||||
as T?,
|
||||
error: freezed == error
|
||||
? _value.error
|
||||
: error // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$ApiResponseImplCopyWith<T, $Res>
|
||||
implements $ApiResponseCopyWith<T, $Res> {
|
||||
factory _$$ApiResponseImplCopyWith(_$ApiResponseImpl<T> value,
|
||||
$Res Function(_$ApiResponseImpl<T>) then) =
|
||||
__$$ApiResponseImplCopyWithImpl<T, $Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({bool success, String message, T? data, String? error});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$ApiResponseImplCopyWithImpl<T, $Res>
|
||||
extends _$ApiResponseCopyWithImpl<T, $Res, _$ApiResponseImpl<T>>
|
||||
implements _$$ApiResponseImplCopyWith<T, $Res> {
|
||||
__$$ApiResponseImplCopyWithImpl(
|
||||
_$ApiResponseImpl<T> _value, $Res Function(_$ApiResponseImpl<T>) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of ApiResponse
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? success = null,
|
||||
Object? message = null,
|
||||
Object? data = freezed,
|
||||
Object? error = freezed,
|
||||
}) {
|
||||
return _then(_$ApiResponseImpl<T>(
|
||||
success: null == success
|
||||
? _value.success
|
||||
: success // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
message: null == message
|
||||
? _value.message
|
||||
: message // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
data: freezed == data
|
||||
? _value.data
|
||||
: data // ignore: cast_nullable_to_non_nullable
|
||||
as T?,
|
||||
error: freezed == error
|
||||
? _value.error
|
||||
: error // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable(genericArgumentFactories: true)
|
||||
class _$ApiResponseImpl<T> implements _ApiResponse<T> {
|
||||
const _$ApiResponseImpl(
|
||||
{required this.success, required this.message, this.data, this.error});
|
||||
|
||||
factory _$ApiResponseImpl.fromJson(
|
||||
Map<String, dynamic> json, T Function(Object?) fromJsonT) =>
|
||||
_$$ApiResponseImplFromJson(json, fromJsonT);
|
||||
|
||||
@override
|
||||
final bool success;
|
||||
@override
|
||||
final String message;
|
||||
@override
|
||||
final T? data;
|
||||
@override
|
||||
final String? error;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ApiResponse<$T>(success: $success, message: $message, data: $data, error: $error)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$ApiResponseImpl<T> &&
|
||||
(identical(other.success, success) || other.success == success) &&
|
||||
(identical(other.message, message) || other.message == message) &&
|
||||
const DeepCollectionEquality().equals(other.data, data) &&
|
||||
(identical(other.error, error) || other.error == error));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, success, message,
|
||||
const DeepCollectionEquality().hash(data), error);
|
||||
|
||||
/// Create a copy of ApiResponse
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$ApiResponseImplCopyWith<T, _$ApiResponseImpl<T>> get copyWith =>
|
||||
__$$ApiResponseImplCopyWithImpl<T, _$ApiResponseImpl<T>>(
|
||||
this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson(Object? Function(T) toJsonT) {
|
||||
return _$$ApiResponseImplToJson<T>(this, toJsonT);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _ApiResponse<T> implements ApiResponse<T> {
|
||||
const factory _ApiResponse(
|
||||
{required final bool success,
|
||||
required final String message,
|
||||
final T? data,
|
||||
final String? error}) = _$ApiResponseImpl<T>;
|
||||
|
||||
factory _ApiResponse.fromJson(
|
||||
Map<String, dynamic> json, T Function(Object?) fromJsonT) =
|
||||
_$ApiResponseImpl<T>.fromJson;
|
||||
|
||||
@override
|
||||
bool get success;
|
||||
@override
|
||||
String get message;
|
||||
@override
|
||||
T? get data;
|
||||
@override
|
||||
String? get error;
|
||||
|
||||
/// Create a copy of ApiResponse
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$ApiResponseImplCopyWith<T, _$ApiResponseImpl<T>> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
41
lib/data/models/common/api_response.g.dart
Normal file
41
lib/data/models/common/api_response.g.dart
Normal file
@@ -0,0 +1,41 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'api_response.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$ApiResponseImpl<T> _$$ApiResponseImplFromJson<T>(
|
||||
Map<String, dynamic> json,
|
||||
T Function(Object? json) fromJsonT,
|
||||
) =>
|
||||
_$ApiResponseImpl<T>(
|
||||
success: json['success'] as bool,
|
||||
message: json['message'] as String,
|
||||
data: _$nullableGenericFromJson(json['data'], fromJsonT),
|
||||
error: json['error'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ApiResponseImplToJson<T>(
|
||||
_$ApiResponseImpl<T> instance,
|
||||
Object? Function(T value) toJsonT,
|
||||
) =>
|
||||
<String, dynamic>{
|
||||
'success': instance.success,
|
||||
'message': instance.message,
|
||||
'data': _$nullableGenericToJson(instance.data, toJsonT),
|
||||
'error': instance.error,
|
||||
};
|
||||
|
||||
T? _$nullableGenericFromJson<T>(
|
||||
Object? input,
|
||||
T Function(Object? json) fromJson,
|
||||
) =>
|
||||
input == null ? null : fromJson(input);
|
||||
|
||||
Object? _$nullableGenericToJson<T>(
|
||||
T? input,
|
||||
Object? Function(T value) toJson,
|
||||
) =>
|
||||
input == null ? null : toJson(input);
|
||||
23
lib/data/models/common/paginated_response.dart
Normal file
23
lib/data/models/common/paginated_response.dart
Normal file
@@ -0,0 +1,23 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'paginated_response.freezed.dart';
|
||||
part 'paginated_response.g.dart';
|
||||
|
||||
@Freezed(genericArgumentFactories: true)
|
||||
class PaginatedResponse<T> with _$PaginatedResponse<T> {
|
||||
const factory PaginatedResponse({
|
||||
required List<T> items,
|
||||
required int page,
|
||||
required int size,
|
||||
required int totalElements,
|
||||
required int totalPages,
|
||||
required bool first,
|
||||
required bool last,
|
||||
}) = _PaginatedResponse<T>;
|
||||
|
||||
factory PaginatedResponse.fromJson(
|
||||
Map<String, dynamic> json,
|
||||
T Function(Object?) fromJsonT,
|
||||
) =>
|
||||
_$PaginatedResponseFromJson<T>(json, fromJsonT);
|
||||
}
|
||||
310
lib/data/models/common/paginated_response.freezed.dart
Normal file
310
lib/data/models/common/paginated_response.freezed.dart
Normal file
@@ -0,0 +1,310 @@
|
||||
// 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 'paginated_response.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');
|
||||
|
||||
PaginatedResponse<T> _$PaginatedResponseFromJson<T>(
|
||||
Map<String, dynamic> json, T Function(Object?) fromJsonT) {
|
||||
return _PaginatedResponse<T>.fromJson(json, fromJsonT);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$PaginatedResponse<T> {
|
||||
List<T> get items => throw _privateConstructorUsedError;
|
||||
int get page => throw _privateConstructorUsedError;
|
||||
int get size => throw _privateConstructorUsedError;
|
||||
int get totalElements => throw _privateConstructorUsedError;
|
||||
int get totalPages => throw _privateConstructorUsedError;
|
||||
bool get first => throw _privateConstructorUsedError;
|
||||
bool get last => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this PaginatedResponse to a JSON map.
|
||||
Map<String, dynamic> toJson(Object? Function(T) toJsonT) =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of PaginatedResponse
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$PaginatedResponseCopyWith<T, PaginatedResponse<T>> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $PaginatedResponseCopyWith<T, $Res> {
|
||||
factory $PaginatedResponseCopyWith(PaginatedResponse<T> value,
|
||||
$Res Function(PaginatedResponse<T>) then) =
|
||||
_$PaginatedResponseCopyWithImpl<T, $Res, PaginatedResponse<T>>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{List<T> items,
|
||||
int page,
|
||||
int size,
|
||||
int totalElements,
|
||||
int totalPages,
|
||||
bool first,
|
||||
bool last});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$PaginatedResponseCopyWithImpl<T, $Res,
|
||||
$Val extends PaginatedResponse<T>>
|
||||
implements $PaginatedResponseCopyWith<T, $Res> {
|
||||
_$PaginatedResponseCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of PaginatedResponse
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? items = null,
|
||||
Object? page = null,
|
||||
Object? size = null,
|
||||
Object? totalElements = null,
|
||||
Object? totalPages = null,
|
||||
Object? first = null,
|
||||
Object? last = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
items: null == items
|
||||
? _value.items
|
||||
: items // ignore: cast_nullable_to_non_nullable
|
||||
as List<T>,
|
||||
page: null == page
|
||||
? _value.page
|
||||
: page // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
size: null == size
|
||||
? _value.size
|
||||
: size // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
totalElements: null == totalElements
|
||||
? _value.totalElements
|
||||
: totalElements // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
totalPages: null == totalPages
|
||||
? _value.totalPages
|
||||
: totalPages // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
first: null == first
|
||||
? _value.first
|
||||
: first // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
last: null == last
|
||||
? _value.last
|
||||
: last // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$PaginatedResponseImplCopyWith<T, $Res>
|
||||
implements $PaginatedResponseCopyWith<T, $Res> {
|
||||
factory _$$PaginatedResponseImplCopyWith(_$PaginatedResponseImpl<T> value,
|
||||
$Res Function(_$PaginatedResponseImpl<T>) then) =
|
||||
__$$PaginatedResponseImplCopyWithImpl<T, $Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{List<T> items,
|
||||
int page,
|
||||
int size,
|
||||
int totalElements,
|
||||
int totalPages,
|
||||
bool first,
|
||||
bool last});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$PaginatedResponseImplCopyWithImpl<T, $Res>
|
||||
extends _$PaginatedResponseCopyWithImpl<T, $Res, _$PaginatedResponseImpl<T>>
|
||||
implements _$$PaginatedResponseImplCopyWith<T, $Res> {
|
||||
__$$PaginatedResponseImplCopyWithImpl(_$PaginatedResponseImpl<T> _value,
|
||||
$Res Function(_$PaginatedResponseImpl<T>) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of PaginatedResponse
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? items = null,
|
||||
Object? page = null,
|
||||
Object? size = null,
|
||||
Object? totalElements = null,
|
||||
Object? totalPages = null,
|
||||
Object? first = null,
|
||||
Object? last = null,
|
||||
}) {
|
||||
return _then(_$PaginatedResponseImpl<T>(
|
||||
items: null == items
|
||||
? _value._items
|
||||
: items // ignore: cast_nullable_to_non_nullable
|
||||
as List<T>,
|
||||
page: null == page
|
||||
? _value.page
|
||||
: page // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
size: null == size
|
||||
? _value.size
|
||||
: size // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
totalElements: null == totalElements
|
||||
? _value.totalElements
|
||||
: totalElements // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
totalPages: null == totalPages
|
||||
? _value.totalPages
|
||||
: totalPages // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
first: null == first
|
||||
? _value.first
|
||||
: first // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
last: null == last
|
||||
? _value.last
|
||||
: last // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable(genericArgumentFactories: true)
|
||||
class _$PaginatedResponseImpl<T> implements _PaginatedResponse<T> {
|
||||
const _$PaginatedResponseImpl(
|
||||
{required final List<T> items,
|
||||
required this.page,
|
||||
required this.size,
|
||||
required this.totalElements,
|
||||
required this.totalPages,
|
||||
required this.first,
|
||||
required this.last})
|
||||
: _items = items;
|
||||
|
||||
factory _$PaginatedResponseImpl.fromJson(
|
||||
Map<String, dynamic> json, T Function(Object?) fromJsonT) =>
|
||||
_$$PaginatedResponseImplFromJson(json, fromJsonT);
|
||||
|
||||
final List<T> _items;
|
||||
@override
|
||||
List<T> get items {
|
||||
if (_items is EqualUnmodifiableListView) return _items;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_items);
|
||||
}
|
||||
|
||||
@override
|
||||
final int page;
|
||||
@override
|
||||
final int size;
|
||||
@override
|
||||
final int totalElements;
|
||||
@override
|
||||
final int totalPages;
|
||||
@override
|
||||
final bool first;
|
||||
@override
|
||||
final bool last;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'PaginatedResponse<$T>(items: $items, page: $page, size: $size, totalElements: $totalElements, totalPages: $totalPages, first: $first, last: $last)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$PaginatedResponseImpl<T> &&
|
||||
const DeepCollectionEquality().equals(other._items, _items) &&
|
||||
(identical(other.page, page) || other.page == page) &&
|
||||
(identical(other.size, size) || other.size == size) &&
|
||||
(identical(other.totalElements, totalElements) ||
|
||||
other.totalElements == totalElements) &&
|
||||
(identical(other.totalPages, totalPages) ||
|
||||
other.totalPages == totalPages) &&
|
||||
(identical(other.first, first) || other.first == first) &&
|
||||
(identical(other.last, last) || other.last == last));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(_items),
|
||||
page,
|
||||
size,
|
||||
totalElements,
|
||||
totalPages,
|
||||
first,
|
||||
last);
|
||||
|
||||
/// Create a copy of PaginatedResponse
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$PaginatedResponseImplCopyWith<T, _$PaginatedResponseImpl<T>>
|
||||
get copyWith =>
|
||||
__$$PaginatedResponseImplCopyWithImpl<T, _$PaginatedResponseImpl<T>>(
|
||||
this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson(Object? Function(T) toJsonT) {
|
||||
return _$$PaginatedResponseImplToJson<T>(this, toJsonT);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _PaginatedResponse<T> implements PaginatedResponse<T> {
|
||||
const factory _PaginatedResponse(
|
||||
{required final List<T> items,
|
||||
required final int page,
|
||||
required final int size,
|
||||
required final int totalElements,
|
||||
required final int totalPages,
|
||||
required final bool first,
|
||||
required final bool last}) = _$PaginatedResponseImpl<T>;
|
||||
|
||||
factory _PaginatedResponse.fromJson(
|
||||
Map<String, dynamic> json, T Function(Object?) fromJsonT) =
|
||||
_$PaginatedResponseImpl<T>.fromJson;
|
||||
|
||||
@override
|
||||
List<T> get items;
|
||||
@override
|
||||
int get page;
|
||||
@override
|
||||
int get size;
|
||||
@override
|
||||
int get totalElements;
|
||||
@override
|
||||
int get totalPages;
|
||||
@override
|
||||
bool get first;
|
||||
@override
|
||||
bool get last;
|
||||
|
||||
/// Create a copy of PaginatedResponse
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$PaginatedResponseImplCopyWith<T, _$PaginatedResponseImpl<T>>
|
||||
get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
35
lib/data/models/common/paginated_response.g.dart
Normal file
35
lib/data/models/common/paginated_response.g.dart
Normal file
@@ -0,0 +1,35 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'paginated_response.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$PaginatedResponseImpl<T> _$$PaginatedResponseImplFromJson<T>(
|
||||
Map<String, dynamic> json,
|
||||
T Function(Object? json) fromJsonT,
|
||||
) =>
|
||||
_$PaginatedResponseImpl<T>(
|
||||
items: (json['items'] as List<dynamic>).map(fromJsonT).toList(),
|
||||
page: (json['page'] as num).toInt(),
|
||||
size: (json['size'] as num).toInt(),
|
||||
totalElements: (json['totalElements'] as num).toInt(),
|
||||
totalPages: (json['totalPages'] as num).toInt(),
|
||||
first: json['first'] as bool,
|
||||
last: json['last'] as bool,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$PaginatedResponseImplToJson<T>(
|
||||
_$PaginatedResponseImpl<T> instance,
|
||||
Object? Function(T value) toJsonT,
|
||||
) =>
|
||||
<String, dynamic>{
|
||||
'items': instance.items.map(toJsonT).toList(),
|
||||
'page': instance.page,
|
||||
'size': instance.size,
|
||||
'totalElements': instance.totalElements,
|
||||
'totalPages': instance.totalPages,
|
||||
'first': instance.first,
|
||||
'last': instance.last,
|
||||
};
|
||||
@@ -73,7 +73,7 @@ Future<void> setupDependencies() async {
|
||||
() => UserService(),
|
||||
);
|
||||
getIt.registerLazySingleton<LicenseService>(
|
||||
() => LicenseService(),
|
||||
() => LicenseService(getIt()),
|
||||
);
|
||||
getIt.registerLazySingleton<WarehouseService>(
|
||||
() => WarehouseService(),
|
||||
|
||||
22
lib/models/company_branch_info.dart
Normal file
22
lib/models/company_branch_info.dart
Normal file
@@ -0,0 +1,22 @@
|
||||
/// 회사 및 지점 정보를 저장하는 클래스
|
||||
class CompanyBranchInfo {
|
||||
final int? id;
|
||||
final String name; // 표시용 이름 (회사명 + 지점명 또는 회사명 (유형))
|
||||
final String originalName; // 원래 이름 (회사 본사명 또는 지점명)
|
||||
final String? displayName; // UI에 표시할 이름 (주로 지점명)
|
||||
final bool isMainCompany; // 본사인지 지점인지 구분
|
||||
final int? companyId; // 회사 ID
|
||||
final int? branchId; // 지점 ID
|
||||
final String? parentCompanyName; // 부모 회사명 (지점인 경우)
|
||||
|
||||
CompanyBranchInfo({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.originalName,
|
||||
this.displayName,
|
||||
required this.isMainCompany,
|
||||
required this.companyId,
|
||||
required this.branchId,
|
||||
this.parentCompanyName,
|
||||
});
|
||||
}
|
||||
@@ -1,35 +1,161 @@
|
||||
class License {
|
||||
final int? id;
|
||||
final int companyId;
|
||||
final String name;
|
||||
final int durationMonths;
|
||||
final String visitCycle; // 방문주기(월, 격월, 분기 등)
|
||||
final String licenseKey;
|
||||
final String? productName;
|
||||
final String? vendor;
|
||||
final String? licenseType;
|
||||
final int? userCount;
|
||||
final DateTime? purchaseDate;
|
||||
final DateTime? expiryDate;
|
||||
final double? purchasePrice;
|
||||
final int? companyId;
|
||||
final int? branchId;
|
||||
final int? assignedUserId;
|
||||
final String? remark;
|
||||
final bool isActive;
|
||||
final DateTime? createdAt;
|
||||
final DateTime? updatedAt;
|
||||
|
||||
// 조인된 데이터 필드
|
||||
final String? companyName;
|
||||
final String? branchName;
|
||||
final String? assignedUserName;
|
||||
|
||||
// 계산 필드
|
||||
int? get daysUntilExpiry {
|
||||
if (expiryDate == null) return null;
|
||||
return expiryDate!.difference(DateTime.now()).inDays;
|
||||
}
|
||||
|
||||
bool get isExpired {
|
||||
if (expiryDate == null) return false;
|
||||
return expiryDate!.isBefore(DateTime.now());
|
||||
}
|
||||
|
||||
String get status {
|
||||
if (!isActive) return 'inactive';
|
||||
if (isExpired) return 'expired';
|
||||
if (daysUntilExpiry != null && daysUntilExpiry! <= 30) return 'expiring';
|
||||
return 'active';
|
||||
}
|
||||
|
||||
License({
|
||||
this.id,
|
||||
required this.companyId,
|
||||
required this.name,
|
||||
required this.durationMonths,
|
||||
required this.visitCycle,
|
||||
required this.licenseKey,
|
||||
this.productName,
|
||||
this.vendor,
|
||||
this.licenseType,
|
||||
this.userCount,
|
||||
this.purchaseDate,
|
||||
this.expiryDate,
|
||||
this.purchasePrice,
|
||||
this.companyId,
|
||||
this.branchId,
|
||||
this.assignedUserId,
|
||||
this.remark,
|
||||
this.isActive = true,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.companyName,
|
||||
this.branchName,
|
||||
this.assignedUserName,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'companyId': companyId,
|
||||
'name': name,
|
||||
'durationMonths': durationMonths,
|
||||
'visitCycle': visitCycle,
|
||||
'license_key': licenseKey,
|
||||
'product_name': productName,
|
||||
'vendor': vendor,
|
||||
'license_type': licenseType,
|
||||
'user_count': userCount,
|
||||
'purchase_date': purchaseDate?.toIso8601String(),
|
||||
'expiry_date': expiryDate?.toIso8601String(),
|
||||
'purchase_price': purchasePrice,
|
||||
'company_id': companyId,
|
||||
'branch_id': branchId,
|
||||
'assigned_user_id': assignedUserId,
|
||||
'remark': remark,
|
||||
'is_active': isActive,
|
||||
'created_at': createdAt?.toIso8601String(),
|
||||
'updated_at': updatedAt?.toIso8601String(),
|
||||
};
|
||||
}
|
||||
|
||||
factory License.fromJson(Map<String, dynamic> json) {
|
||||
return License(
|
||||
id: json['id'],
|
||||
companyId: json['companyId'],
|
||||
name: json['name'],
|
||||
durationMonths: json['durationMonths'],
|
||||
visitCycle: json['visitCycle'] as String,
|
||||
id: json['id'] as int?,
|
||||
licenseKey: json['license_key'] as String,
|
||||
productName: json['product_name'] as String?,
|
||||
vendor: json['vendor'] as String?,
|
||||
licenseType: json['license_type'] as String?,
|
||||
userCount: json['user_count'] as int?,
|
||||
purchaseDate: json['purchase_date'] != null
|
||||
? DateTime.parse(json['purchase_date'] as String) : null,
|
||||
expiryDate: json['expiry_date'] != null
|
||||
? DateTime.parse(json['expiry_date'] as String) : null,
|
||||
purchasePrice: (json['purchase_price'] as num?)?.toDouble(),
|
||||
companyId: json['company_id'] as int?,
|
||||
branchId: json['branch_id'] as int?,
|
||||
assignedUserId: json['assigned_user_id'] as int?,
|
||||
remark: json['remark'] as String?,
|
||||
isActive: json['is_active'] ?? true,
|
||||
createdAt: json['created_at'] != null
|
||||
? DateTime.parse(json['created_at'] as String) : null,
|
||||
updatedAt: json['updated_at'] != null
|
||||
? DateTime.parse(json['updated_at'] as String) : null,
|
||||
companyName: json['company_name'] as String?,
|
||||
branchName: json['branch_name'] as String?,
|
||||
assignedUserName: json['assigned_user_name'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
License copyWith({
|
||||
int? id,
|
||||
String? licenseKey,
|
||||
String? productName,
|
||||
String? vendor,
|
||||
String? licenseType,
|
||||
int? userCount,
|
||||
DateTime? purchaseDate,
|
||||
DateTime? expiryDate,
|
||||
double? purchasePrice,
|
||||
int? companyId,
|
||||
int? branchId,
|
||||
int? assignedUserId,
|
||||
String? remark,
|
||||
bool? isActive,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
String? companyName,
|
||||
String? branchName,
|
||||
String? assignedUserName,
|
||||
}) {
|
||||
return License(
|
||||
id: id ?? this.id,
|
||||
licenseKey: licenseKey ?? this.licenseKey,
|
||||
productName: productName ?? this.productName,
|
||||
vendor: vendor ?? this.vendor,
|
||||
licenseType: licenseType ?? this.licenseType,
|
||||
userCount: userCount ?? this.userCount,
|
||||
purchaseDate: purchaseDate ?? this.purchaseDate,
|
||||
expiryDate: expiryDate ?? this.expiryDate,
|
||||
purchasePrice: purchasePrice ?? this.purchasePrice,
|
||||
companyId: companyId ?? this.companyId,
|
||||
branchId: branchId ?? this.branchId,
|
||||
assignedUserId: assignedUserId ?? this.assignedUserId,
|
||||
remark: remark ?? this.remark,
|
||||
isActive: isActive ?? this.isActive,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
companyName: companyName ?? this.companyName,
|
||||
branchName: branchName ?? this.branchName,
|
||||
assignedUserName: assignedUserName ?? this.assignedUserName,
|
||||
);
|
||||
}
|
||||
|
||||
// Mock 데이터 호환을 위한 추가 getter (기존 코드 호환)
|
||||
String get name => productName ?? licenseKey;
|
||||
int get durationMonths => 12; // 기본값
|
||||
String get visitCycle => '월'; // 기본값
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ class _CompanyListRedesignState extends State<CompanyListRedesign> {
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: ShadcnTheme.card,
|
||||
borderRadius: BorderRadius.circular(ShadcnTheme.radius),
|
||||
borderRadius: BorderRadius.circular(ShadcnTheme.radiusMd),
|
||||
border: Border.all(color: ShadcnTheme.border),
|
||||
),
|
||||
child: TextField(
|
||||
@@ -262,7 +262,7 @@ class _CompanyListRedesignState extends State<CompanyListRedesign> {
|
||||
margin: const EdgeInsets.only(bottom: ShadcnTheme.spacing4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red.shade50,
|
||||
borderRadius: BorderRadius.circular(ShadcnTheme.radius),
|
||||
borderRadius: BorderRadius.circular(ShadcnTheme.radiusMd),
|
||||
border: Border.all(color: Colors.red.shade200),
|
||||
),
|
||||
child: Row(
|
||||
|
||||
Binary file not shown.
@@ -171,7 +171,7 @@ class EquipmentInFormController extends ChangeNotifier {
|
||||
// API 실패 시 Mock 데이터 사용
|
||||
_loadFromMockData(equipmentIn);
|
||||
}
|
||||
} else {
|
||||
} else if (equipmentIn != null) {
|
||||
_loadFromMockData(equipmentIn);
|
||||
}
|
||||
} else {
|
||||
@@ -311,11 +311,15 @@ class EquipmentInFormController extends ChangeNotifier {
|
||||
int? warehouseLocationId;
|
||||
if (warehouseLocation != null) {
|
||||
// TODO: 창고 위치 ID 가져오기 - 현재는 목 데이터에서 찾기
|
||||
final warehouse = dataService.getAllWarehouseLocations().firstWhere(
|
||||
(w) => w.name == warehouseLocation,
|
||||
orElse: () => null,
|
||||
);
|
||||
warehouseLocationId = warehouse?.id;
|
||||
try {
|
||||
final warehouse = dataService.getAllWarehouseLocations().firstWhere(
|
||||
(w) => w.name == warehouseLocation,
|
||||
);
|
||||
warehouseLocationId = warehouse.id;
|
||||
} catch (e) {
|
||||
// 창고를 찾을 수 없는 경우
|
||||
warehouseLocationId = null;
|
||||
}
|
||||
}
|
||||
|
||||
await _equipmentService.equipmentIn(
|
||||
|
||||
@@ -63,13 +63,12 @@ class EquipmentListController extends ChangeNotifier {
|
||||
);
|
||||
|
||||
// API 모델을 UnifiedEquipment로 변환
|
||||
final unifiedEquipments = apiEquipments.map((equipment) {
|
||||
final List<UnifiedEquipment> unifiedEquipments = apiEquipments.map((equipment) {
|
||||
return UnifiedEquipment(
|
||||
id: equipment.id,
|
||||
equipment: equipment,
|
||||
quantity: equipment.quantity,
|
||||
date: DateTime.now(), // 실제로는 API에서 날짜 정보를 가져와야 함
|
||||
status: EquipmentStatus.in_, // 기본값, 실제로는 API에서 가져와야 함
|
||||
locationTrack: LocationTrack.inStock,
|
||||
);
|
||||
}).toList();
|
||||
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:superport/models/equipment_unified_model.dart';
|
||||
import 'package:superport/models/company_model.dart';
|
||||
import 'package:superport/models/company_branch_info.dart';
|
||||
import 'package:superport/models/address_model.dart';
|
||||
import 'package:superport/services/mock_data_service.dart';
|
||||
import 'package:superport/utils/constants.dart';
|
||||
|
||||
/// 장비 출고 폼 컨트롤러
|
||||
///
|
||||
/// 폼의 전체 상태, 유효성, 저장, 데이터 로딩 등 비즈니스 로직을 담당한다.
|
||||
class EquipmentOutFormController extends ChangeNotifier {
|
||||
final MockDataService dataService;
|
||||
int? equipmentOutId;
|
||||
|
||||
// 편집 모드 여부
|
||||
bool isEditMode = false;
|
||||
|
||||
// 상태 관리
|
||||
bool _isLoading = false;
|
||||
String? _errorMessage;
|
||||
|
||||
// Getters
|
||||
bool get isLoading => _isLoading;
|
||||
String? get errorMessage => _errorMessage;
|
||||
|
||||
// 폼 키
|
||||
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
|
||||
// 선택된 장비 정보
|
||||
Equipment? selectedEquipment;
|
||||
int? selectedEquipmentInId;
|
||||
List<Map<String, dynamic>>? selectedEquipments;
|
||||
|
||||
// 출고처 정보
|
||||
List<String?> selectedCompanies = [null];
|
||||
List<bool> hasManagersPerCompany = [false];
|
||||
List<List<String>> filteredManagersPerCompany = [[]];
|
||||
List<String?> selectedManagersPerCompany = [null];
|
||||
|
||||
// 라이선스 정보
|
||||
String? selectedLicense;
|
||||
|
||||
// 출고 타입
|
||||
String outType = 'O'; // 기본값: 출고
|
||||
|
||||
// 드롭다운 데이터
|
||||
List<CompanyBranchInfo> companies = [];
|
||||
List<String> licenses = [];
|
||||
|
||||
// 날짜
|
||||
DateTime outDate = DateTime.now();
|
||||
|
||||
// 회사 정보 (지점 포함)
|
||||
List<CompanyBranchInfo> get companiesWithBranches => companies;
|
||||
|
||||
// 비고
|
||||
final TextEditingController remarkController = TextEditingController();
|
||||
|
||||
EquipmentOutFormController({
|
||||
required this.dataService,
|
||||
this.equipmentOutId,
|
||||
}) {
|
||||
isEditMode = equipmentOutId != null;
|
||||
}
|
||||
|
||||
// 이용 가능한 회사 목록 (선택된 회사 제외)
|
||||
List<List<CompanyBranchInfo>> get availableCompaniesPerDropdown {
|
||||
return List.generate(selectedCompanies.length, (index) {
|
||||
final selectedBefore = selectedCompanies.sublist(0, index).whereType<String>().toSet();
|
||||
return companies.where((company) => !selectedBefore.contains(company.name)).toList();
|
||||
});
|
||||
}
|
||||
|
||||
// 드롭다운 데이터 로드
|
||||
void loadDropdownData() {
|
||||
// 회사 목록 로드 (출고처 가능한 회사만)
|
||||
companies = dataService.getAllCompanies()
|
||||
.where((c) => c.companyTypes.contains(CompanyType.customer))
|
||||
.map((c) => CompanyBranchInfo(
|
||||
id: c.id,
|
||||
name: c.name,
|
||||
originalName: c.name,
|
||||
isMainCompany: true,
|
||||
companyId: c.id,
|
||||
branchId: null,
|
||||
))
|
||||
.toList();
|
||||
|
||||
// 라이선스 목록 로드
|
||||
licenses = dataService.getAllLicenses().map((l) => l.name).toList();
|
||||
}
|
||||
|
||||
// 선택된 장비로 초기화
|
||||
void initializeWithSelectedEquipment(Equipment equipment) {
|
||||
selectedEquipment = equipment;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// 회사 선택 시 담당자 목록 필터링
|
||||
void filterManagersForCompany(int index) {
|
||||
if (index >= selectedCompanies.length || selectedCompanies[index] == null) {
|
||||
hasManagersPerCompany[index] = false;
|
||||
filteredManagersPerCompany[index] = [];
|
||||
return;
|
||||
}
|
||||
|
||||
// Mock 데이터에서 회사별 담당자 목록 가져오기
|
||||
final company = dataService.getAllCompanies().firstWhere(
|
||||
(c) => c.name == selectedCompanies[index],
|
||||
orElse: () => Company(
|
||||
name: '',
|
||||
companyTypes: [],
|
||||
),
|
||||
);
|
||||
|
||||
if (company.name.isNotEmpty && company.contactName != null && company.contactName!.isNotEmpty) {
|
||||
// 회사의 담당자 정보
|
||||
hasManagersPerCompany[index] = true;
|
||||
filteredManagersPerCompany[index] = [company.contactName!];
|
||||
} else {
|
||||
hasManagersPerCompany[index] = false;
|
||||
filteredManagersPerCompany[index] = ['없음'];
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// 인덱스별 담당자 필터링
|
||||
void filterManagersByCompanyAtIndex(int index) {
|
||||
filterManagersForCompany(index);
|
||||
}
|
||||
|
||||
// 회사 추가
|
||||
void addCompany() {
|
||||
selectedCompanies.add(null);
|
||||
hasManagersPerCompany.add(false);
|
||||
filteredManagersPerCompany.add([]);
|
||||
selectedManagersPerCompany.add(null);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// 회사 제거
|
||||
void removeCompany(int index) {
|
||||
if (selectedCompanies.length > 1) {
|
||||
selectedCompanies.removeAt(index);
|
||||
hasManagersPerCompany.removeAt(index);
|
||||
filteredManagersPerCompany.removeAt(index);
|
||||
selectedManagersPerCompany.removeAt(index);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// 회사 선택 초기화
|
||||
void resetCompanySelection() {
|
||||
selectedCompanies = [null];
|
||||
hasManagersPerCompany = [false];
|
||||
filteredManagersPerCompany = [[]];
|
||||
selectedManagersPerCompany = [null];
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// 에러 초기화
|
||||
void clearError() {
|
||||
_errorMessage = null;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// 이용 가능한 회사 목록 업데이트
|
||||
void updateAvailableCompanies() {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// 날짜 포맷팅
|
||||
String formatDate(DateTime date) {
|
||||
return DateFormat('yyyy-MM-dd').format(date);
|
||||
}
|
||||
|
||||
// 출고 정보 저장
|
||||
Future<bool> saveEquipmentOut(BuildContext context, {String? note}) async {
|
||||
// 유효성 검사
|
||||
if (selectedCompanies.isEmpty || selectedCompanies[0] == null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('출고처를 선택해주세요.')),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (selectedEquipment == null && (selectedEquipments == null || selectedEquipments!.isEmpty)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('출고할 장비를 선택해주세요.')),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// TODO: 실제 저장 로직 구현
|
||||
// 현재는 Mock 데이터 서비스에 저장
|
||||
|
||||
if (isEditMode) {
|
||||
// 수정 모드
|
||||
// dataService.updateEquipmentOut(...)
|
||||
} else {
|
||||
// 생성 모드
|
||||
if (selectedEquipments != null && selectedEquipments!.isNotEmpty) {
|
||||
// 다중 장비 출고
|
||||
for (var equipmentData in selectedEquipments!) {
|
||||
// dataService.addEquipmentOut(...)
|
||||
}
|
||||
} else if (selectedEquipment != null) {
|
||||
// 단일 장비 출고
|
||||
// dataService.addEquipmentOut(...)
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('저장 중 오류가 발생했습니다: $e')),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
remarkController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:superport/models/equipment_unified_model.dart';
|
||||
import 'package:superport/models/company_model.dart';
|
||||
import 'package:superport/models/company_branch_info.dart';
|
||||
import 'package:superport/models/address_model.dart';
|
||||
import 'package:superport/screens/common/custom_widgets.dart';
|
||||
import 'package:superport/screens/common/theme_tailwind.dart';
|
||||
@@ -406,25 +407,17 @@ class _EquipmentOutFormScreenState extends State<EquipmentOutFormScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
controller.saveEquipmentOut(
|
||||
(msg) {
|
||||
controller.saveEquipmentOut(context).then((success) {
|
||||
if (success) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(msg),
|
||||
duration: const Duration(seconds: 2),
|
||||
const SnackBar(
|
||||
content: Text('출고가 완료되었습니다.'),
|
||||
duration: Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
Navigator.pop(context, true);
|
||||
},
|
||||
(err) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(err),
|
||||
duration: const Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
: null,
|
||||
style:
|
||||
@@ -510,8 +503,8 @@ class _EquipmentOutFormScreenState extends State<EquipmentOutFormScreen> {
|
||||
controller.availableCompaniesPerDropdown[index]
|
||||
.map(
|
||||
(item) => DropdownMenuItem<String>(
|
||||
value: item,
|
||||
child: _buildCompanyDropdownItem(item, controller),
|
||||
value: item.name,
|
||||
child: _buildCompanyDropdownItem(item.name, controller),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
@@ -526,10 +519,7 @@ class _EquipmentOutFormScreenState extends State<EquipmentOutFormScreen> {
|
||||
controller.selectedCompanies[index - 1] != null)
|
||||
? (value) {
|
||||
controller.selectedCompanies[index] = value;
|
||||
controller.filterManagersByCompanyAtIndex(
|
||||
value,
|
||||
index,
|
||||
);
|
||||
controller.filterManagersByCompanyAtIndex(index);
|
||||
controller.updateAvailableCompanies();
|
||||
}
|
||||
: null,
|
||||
|
||||
@@ -24,11 +24,35 @@ class LicenseFormController extends ChangeNotifier {
|
||||
int _durationMonths = 12; // 기본값: 12개월
|
||||
String _visitCycle = '미방문'; // 기본값: 미방문
|
||||
|
||||
// isEditMode setter
|
||||
set isEditMode(bool value) {
|
||||
_isEditMode = value;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// name setter
|
||||
set name(String value) {
|
||||
_name = value;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// durationMonths setter
|
||||
set durationMonths(int value) {
|
||||
_durationMonths = value;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// visitCycle setter
|
||||
set visitCycle(String value) {
|
||||
_visitCycle = value;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
LicenseFormController({
|
||||
this.useApi = true,
|
||||
this.mockDataService,
|
||||
this.useApi = false,
|
||||
MockDataService? dataService,
|
||||
int? licenseId,
|
||||
}) {
|
||||
}) : mockDataService = dataService ?? MockDataService() {
|
||||
if (useApi && GetIt.instance.isRegistered<LicenseService>()) {
|
||||
_licenseService = GetIt.instance<LicenseService>();
|
||||
}
|
||||
@@ -89,10 +113,11 @@ class LicenseFormController extends ChangeNotifier {
|
||||
}
|
||||
|
||||
if (_originalLicense != null) {
|
||||
_name = _originalLicense!.name;
|
||||
_companyId = _originalLicense!.companyId;
|
||||
_durationMonths = _originalLicense!.durationMonths;
|
||||
_visitCycle = _originalLicense!.visitCycle;
|
||||
_name = _originalLicense!.productName ?? '';
|
||||
_companyId = _originalLicense!.companyId ?? 1;
|
||||
// durationMonths와 visitCycle은 License 모델에 없으므로 기본값 유지
|
||||
// _durationMonths = _originalLicense!.durationMonths;
|
||||
// _visitCycle = _originalLicense!.visitCycle;
|
||||
}
|
||||
} catch (e) {
|
||||
_error = e.toString();
|
||||
@@ -115,10 +140,14 @@ class LicenseFormController extends ChangeNotifier {
|
||||
try {
|
||||
final license = License(
|
||||
id: _isEditMode ? _licenseId : null,
|
||||
licenseKey: 'LIC-${DateTime.now().millisecondsSinceEpoch}',
|
||||
productName: _name,
|
||||
companyId: _companyId,
|
||||
name: _name,
|
||||
durationMonths: _durationMonths,
|
||||
visitCycle: _visitCycle,
|
||||
// durationMonths와 visitCycle은 License 모델에 없음
|
||||
// 대신 expiryDate를 설정
|
||||
purchaseDate: DateTime.now(),
|
||||
expiryDate: DateTime.now().add(Duration(days: _durationMonths * 30)),
|
||||
remark: '방문주기: $_visitCycle',
|
||||
);
|
||||
|
||||
if (useApi && GetIt.instance.isRegistered<LicenseService>()) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:superport/core/errors/failures.dart';
|
||||
import 'package:superport/models/license_model.dart';
|
||||
import 'package:superport/services/license_service.dart';
|
||||
import 'package:superport/services/mock_data_service.dart';
|
||||
@@ -24,8 +26,13 @@ class LicenseListController extends ChangeNotifier {
|
||||
int? _selectedCompanyId;
|
||||
bool? _isActive;
|
||||
String? _licenseType;
|
||||
String _sortBy = 'expiry_date';
|
||||
String _sortOrder = 'asc';
|
||||
|
||||
// 검색 디바운스를 위한 타이머
|
||||
Timer? _debounceTimer;
|
||||
|
||||
LicenseListController({this.useApi = true, this.mockDataService}) {
|
||||
LicenseListController({this.useApi = false, this.mockDataService}) {
|
||||
if (useApi && GetIt.instance.isRegistered<LicenseService>()) {
|
||||
_licenseService = GetIt.instance<LicenseService>();
|
||||
}
|
||||
@@ -137,11 +144,24 @@ class LicenseListController extends ChangeNotifier {
|
||||
await loadData(isInitialLoad: false);
|
||||
}
|
||||
|
||||
// 검색
|
||||
// 검색 (디바운싱 적용)
|
||||
void search(String query) {
|
||||
_searchQuery = query;
|
||||
_applySearchFilter();
|
||||
notifyListeners();
|
||||
|
||||
// 기존 타이머 취소
|
||||
_debounceTimer?.cancel();
|
||||
|
||||
// Mock 데이터는 즉시 검색
|
||||
if (!useApi) {
|
||||
_applySearchFilter();
|
||||
notifyListeners();
|
||||
return;
|
||||
}
|
||||
|
||||
// API 검색은 디바운싱 적용 (300ms)
|
||||
_debounceTimer = Timer(const Duration(milliseconds: 300), () {
|
||||
loadData();
|
||||
});
|
||||
}
|
||||
|
||||
// 검색 필터 적용
|
||||
@@ -212,10 +232,12 @@ class LicenseListController extends ChangeNotifier {
|
||||
final allLicenses = mockDataService?.getAllLicenses() ?? [];
|
||||
|
||||
return allLicenses.where((license) {
|
||||
// Mock 데이터는 만료일이 없으므로 임의로 계산
|
||||
final expiryDate = now.add(Duration(days: license.durationMonths * 30));
|
||||
final daysUntilExpiry = expiryDate.difference(now).inDays;
|
||||
return daysUntilExpiry > 0 && daysUntilExpiry <= days;
|
||||
// 실제 License 모델에서 만료일 확인
|
||||
if (license.expiryDate != null) {
|
||||
final daysUntilExpiry = license.expiryDate!.difference(now).inDays;
|
||||
return daysUntilExpiry > 0 && daysUntilExpiry <= days;
|
||||
}
|
||||
return false;
|
||||
}).toList();
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -224,4 +246,69 @@ class LicenseListController extends ChangeNotifier {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// 상태별 라이선스 개수 조회
|
||||
Future<Map<String, int>> getLicenseStatusCounts() async {
|
||||
if (useApi && GetIt.instance.isRegistered<LicenseService>()) {
|
||||
try {
|
||||
// API에서 상태별 개수 조회 (실제로는 별도 엔드포인트가 있다면 사용)
|
||||
final activeCount = await _licenseService.getTotalLicenses(isActive: true);
|
||||
final inactiveCount = await _licenseService.getTotalLicenses(isActive: false);
|
||||
final expiringLicenses = await getExpiringLicenses(days: 30);
|
||||
|
||||
return {
|
||||
'active': activeCount,
|
||||
'inactive': inactiveCount,
|
||||
'expiring': expiringLicenses.length,
|
||||
'total': activeCount + inactiveCount,
|
||||
};
|
||||
} catch (e) {
|
||||
return {'active': 0, 'inactive': 0, 'expiring': 0, 'total': 0};
|
||||
}
|
||||
} else {
|
||||
// Mock 데이터에서 계산
|
||||
final allLicenses = mockDataService?.getAllLicenses() ?? [];
|
||||
final now = DateTime.now();
|
||||
|
||||
int activeCount = 0;
|
||||
int expiredCount = 0;
|
||||
int expiringCount = 0;
|
||||
|
||||
for (var license in allLicenses) {
|
||||
if (license.isActive) {
|
||||
activeCount++;
|
||||
|
||||
if (license.expiryDate != null) {
|
||||
final daysUntilExpiry = license.expiryDate!.difference(now).inDays;
|
||||
if (daysUntilExpiry <= 0) {
|
||||
expiredCount++;
|
||||
} else if (daysUntilExpiry <= 30) {
|
||||
expiringCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
'active': activeCount,
|
||||
'inactive': allLicenses.length - activeCount,
|
||||
'expiring': expiringCount,
|
||||
'expired': expiredCount,
|
||||
'total': allLicenses.length,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 정렬 변경
|
||||
void sortBy(String field, String order) {
|
||||
_sortBy = field;
|
||||
_sortOrder = order;
|
||||
loadData();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_debounceTimer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,9 @@ class _MaintenanceFormScreenState extends State<MaintenanceFormScreen> {
|
||||
dataService: MockDataService(),
|
||||
licenseId: widget.maintenanceId,
|
||||
);
|
||||
_controller.isEditMode = widget.maintenanceId != null;
|
||||
if (widget.maintenanceId != null) {
|
||||
_controller.isEditMode = true;
|
||||
}
|
||||
if (_controller.isEditMode) {
|
||||
_controller.loadLicense();
|
||||
// TODO: 기존 데이터 로딩 시 _selectedVisitCycle, _selectedInspectionType, _durationMonths 값 세팅 필요
|
||||
@@ -201,10 +203,10 @@ class _MaintenanceFormScreenState extends State<MaintenanceFormScreen> {
|
||||
_controller.durationMonths = _durationMonths;
|
||||
_controller.visitCycle = _selectedVisitCycle;
|
||||
// 점검형태 저장 로직 필요 시 추가
|
||||
setState(() {
|
||||
_controller.saveLicense(() {
|
||||
_controller.saveLicense().then((success) {
|
||||
if (success) {
|
||||
Navigator.pop(context, true);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -23,7 +23,7 @@ class _LicenseListRedesignState extends State<LicenseListRedesign> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = LicenseListController(dataService: _dataService);
|
||||
_controller = LicenseListController(mockDataService: _dataService);
|
||||
_controller.loadData();
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ class _LicenseListRedesignState extends State<LicenseListRedesign> {
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
_getCompanyName(license.companyId),
|
||||
_getCompanyName(license.companyId ?? 0),
|
||||
style: ShadcnTheme.bodySmall,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -35,6 +35,10 @@ class OverviewController extends ChangeNotifier {
|
||||
EquipmentStatusDistribution? get equipmentStatus => _equipmentStatus;
|
||||
List<ExpiringLicense> get expiringLicenses => _expiringLicenses;
|
||||
|
||||
// 추가 getter
|
||||
int get totalCompanies => _overviewStats?.totalCompanies ?? 0;
|
||||
int get totalUsers => _overviewStats?.totalUsers ?? 0;
|
||||
|
||||
bool get isLoading => _isLoadingStats || _isLoadingActivities ||
|
||||
_isLoadingEquipmentStatus || _isLoadingLicenses;
|
||||
|
||||
@@ -55,6 +59,11 @@ class OverviewController extends ChangeNotifier {
|
||||
]);
|
||||
}
|
||||
|
||||
// 대시보드 데이터 로드 (loadData의 alias)
|
||||
Future<void> loadDashboardData() async {
|
||||
await loadData();
|
||||
}
|
||||
|
||||
// 개별 데이터 로드 메서드
|
||||
Future<void> _loadOverviewStats() async {
|
||||
_isLoadingStats = true;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:superport/screens/common/theme_shadcn.dart';
|
||||
import 'package:superport/screens/common/components/shadcn_components.dart';
|
||||
import 'package:superport/screens/overview/controllers/overview_controller.dart';
|
||||
@@ -123,13 +125,13 @@ class _OverviewScreenRedesignState extends State<OverviewScreenRedesign> {
|
||||
),
|
||||
_buildStatCard(
|
||||
'입고 장비',
|
||||
'${_controller.totalEquipmentIn}',
|
||||
'${_controller.overviewStats?.availableEquipment ?? 0}',
|
||||
Icons.inventory,
|
||||
ShadcnTheme.success,
|
||||
),
|
||||
_buildStatCard(
|
||||
'출고 장비',
|
||||
'${_controller.totalEquipmentOut}',
|
||||
'${_controller.overviewStats?.inUseEquipment ?? 0}',
|
||||
Icons.local_shipping,
|
||||
ShadcnTheme.warning,
|
||||
),
|
||||
@@ -390,7 +392,7 @@ class _OverviewScreenRedesignState extends State<OverviewScreenRedesign> {
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withOpacity(0.1),
|
||||
color: color.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Icon(icon, color: color, size: 20),
|
||||
@@ -398,7 +400,7 @@ class _OverviewScreenRedesignState extends State<OverviewScreenRedesign> {
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: ShadcnTheme.success.withOpacity(0.1),
|
||||
color: ShadcnTheme.success.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Row(
|
||||
@@ -474,7 +476,7 @@ class _OverviewScreenRedesignState extends State<OverviewScreenRedesign> {
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withOpacity(0.1),
|
||||
color: color.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Icon(
|
||||
|
||||
@@ -5,10 +5,6 @@ import 'package:superport/screens/sidebar/widgets/sidebar_menu_footer.dart';
|
||||
import 'package:superport/screens/sidebar/widgets/sidebar_menu_item.dart';
|
||||
import 'package:superport/screens/sidebar/widgets/sidebar_menu_submenu.dart';
|
||||
import 'package:superport/screens/sidebar/widgets/sidebar_menu_types.dart';
|
||||
import 'package:superport/screens/common/theme_tailwind.dart';
|
||||
import 'package:superport/screens/login/widgets/login_view.dart'; // AnimatedBoatIcon import
|
||||
import 'package:wave/wave.dart';
|
||||
import 'package:wave/config.dart';
|
||||
|
||||
// 사이드바 메뉴 메인 위젯 (조립만 담당)
|
||||
class SidebarMenu extends StatefulWidget {
|
||||
|
||||
@@ -4,8 +4,6 @@ import 'package:superport/models/user_model.dart';
|
||||
import 'package:superport/models/company_model.dart';
|
||||
import 'package:superport/services/mock_data_service.dart';
|
||||
import 'package:superport/services/user_service.dart';
|
||||
import 'package:superport/utils/constants.dart';
|
||||
import 'package:superport/utils/user_utils.dart';
|
||||
|
||||
/// 담당자 목록 화면의 상태 및 비즈니스 로직을 담당하는 컨트롤러
|
||||
class UserListController extends ChangeNotifier {
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:superport/models/company_model.dart';
|
||||
import 'package:superport/models/user_model.dart';
|
||||
import 'package:superport/screens/common/theme_tailwind.dart';
|
||||
import 'package:superport/screens/common/custom_widgets.dart';
|
||||
import 'package:superport/services/mock_data_service.dart';
|
||||
import 'package:superport/utils/constants.dart';
|
||||
import 'package:superport/utils/validators.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:superport/screens/user/controllers/user_form_controller.dart';
|
||||
import 'package:superport/models/user_phone_field.dart';
|
||||
import 'package:superport/screens/common/widgets/company_branch_dropdown.dart';
|
||||
|
||||
// 사용자 등록/수정 화면 (UI만 담당, 상태/로직 분리)
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:superport/models/company_model.dart';
|
||||
import 'package:superport/models/user_model.dart';
|
||||
import 'package:superport/screens/common/theme_tailwind.dart';
|
||||
import 'package:superport/screens/common/main_layout.dart';
|
||||
import 'package:superport/screens/common/custom_widgets.dart';
|
||||
@@ -82,6 +80,11 @@ class _UserListScreenState extends State<UserListScreen> {
|
||||
onPressed: () {
|
||||
_controller.deleteUser(id, () {
|
||||
Navigator.pop(context);
|
||||
}, (error) {
|
||||
Navigator.pop(context);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(error)),
|
||||
);
|
||||
});
|
||||
},
|
||||
child: const Text('삭제'),
|
||||
|
||||
@@ -211,7 +211,7 @@ class _UserListRedesignState extends State<UserListRedesign> {
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'데이터를 불러올 수 없습니다',
|
||||
style: ShadcnTheme.h4,
|
||||
style: ShadcnTheme.headingH4,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
@@ -291,7 +291,7 @@ class _UserListRedesignState extends State<UserListRedesign> {
|
||||
: null,
|
||||
);
|
||||
},
|
||||
variant: ShadcnButtonVariant.outline,
|
||||
variant: ShadcnButtonVariant.secondary,
|
||||
icon: const Icon(Icons.filter_list),
|
||||
),
|
||||
const SizedBox(width: ShadcnTheme.spacing2),
|
||||
@@ -302,7 +302,7 @@ class _UserListRedesignState extends State<UserListRedesign> {
|
||||
? '모든 권한'
|
||||
: getRoleName(controller.filterRole!),
|
||||
onPressed: null,
|
||||
variant: ShadcnButtonVariant.outline,
|
||||
variant: ShadcnButtonVariant.secondary,
|
||||
icon: const Icon(Icons.person),
|
||||
),
|
||||
onSelected: (role) {
|
||||
|
||||
@@ -3,7 +3,6 @@ import 'package:superport/models/address_model.dart';
|
||||
import 'package:superport/screens/common/widgets/address_input.dart';
|
||||
import 'package:superport/screens/common/widgets/remark_input.dart';
|
||||
import 'package:superport/screens/common/theme_tailwind.dart';
|
||||
import 'package:superport/utils/constants.dart';
|
||||
import 'controllers/warehouse_location_form_controller.dart';
|
||||
|
||||
/// 입고지 추가/수정 폼 화면 (SRP 적용, 상태/로직 분리)
|
||||
@@ -26,7 +25,9 @@ class _WarehouseLocationFormScreenState
|
||||
super.initState();
|
||||
// 컨트롤러 생성 및 초기화
|
||||
_controller = WarehouseLocationFormController();
|
||||
_controller.initialize(widget.id);
|
||||
if (widget.id != null) {
|
||||
_controller.initialize(widget.id!);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -107,7 +108,7 @@ class _WarehouseLocationFormScreenState
|
||||
? null
|
||||
: () async {
|
||||
setState(() {}); // 저장 중 상태 갱신
|
||||
await _controller.save(context);
|
||||
await _controller.save();
|
||||
setState(() {}); // 저장 완료 후 상태 갱신
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
|
||||
@@ -136,7 +136,7 @@ class _WarehouseLocationListRedesignState
|
||||
vertical: ShadcnTheme.spacing3,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: ShadcnTheme.muted.withOpacity(0.3),
|
||||
color: ShadcnTheme.muted.withValues(alpha: 0.3),
|
||||
border: Border(
|
||||
bottom: BorderSide(color: ShadcnTheme.border),
|
||||
),
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:superport/core/errors/exceptions.dart';
|
||||
import 'package:superport/core/errors/failures.dart';
|
||||
@@ -32,9 +31,9 @@ class CompanyService {
|
||||
|
||||
return response.items.map((dto) => _convertListDtoToCompany(dto)).toList();
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to fetch company list: $e');
|
||||
throw ServerFailure(message: 'Failed to fetch company list: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,9 +54,9 @@ class CompanyService {
|
||||
final response = await _remoteDataSource.createCompany(request);
|
||||
return _convertResponseToCompany(response);
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to create company: $e');
|
||||
throw ServerFailure(message: 'Failed to create company: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,9 +66,9 @@ class CompanyService {
|
||||
final response = await _remoteDataSource.getCompanyDetail(id);
|
||||
return _convertResponseToCompany(response);
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to fetch company detail: $e');
|
||||
throw ServerFailure(message: 'Failed to fetch company detail: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,9 +81,9 @@ class CompanyService {
|
||||
|
||||
return company.copyWith(branches: branches);
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to fetch company with branches: $e');
|
||||
throw ServerFailure(message: 'Failed to fetch company with branches: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,9 +104,9 @@ class CompanyService {
|
||||
final response = await _remoteDataSource.updateCompany(id, request);
|
||||
return _convertResponseToCompany(response);
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to update company: $e');
|
||||
throw ServerFailure(message: 'Failed to update company: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,9 +115,9 @@ class CompanyService {
|
||||
try {
|
||||
await _remoteDataSource.deleteCompany(id);
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to delete company: $e');
|
||||
throw ServerFailure(message: 'Failed to delete company: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,9 +130,9 @@ class CompanyService {
|
||||
'name': dto.name,
|
||||
}).toList();
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to fetch company names: $e');
|
||||
throw ServerFailure(message: 'Failed to fetch company names: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,9 +151,9 @@ class CompanyService {
|
||||
final response = await _remoteDataSource.createBranch(companyId, request);
|
||||
return _convertBranchResponseToBranch(response);
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to create branch: $e');
|
||||
throw ServerFailure(message: 'Failed to create branch: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,9 +162,9 @@ class CompanyService {
|
||||
final response = await _remoteDataSource.getBranchDetail(companyId, branchId);
|
||||
return _convertBranchResponseToBranch(response);
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to fetch branch detail: $e');
|
||||
throw ServerFailure(message: 'Failed to fetch branch detail: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,9 +182,9 @@ class CompanyService {
|
||||
final response = await _remoteDataSource.updateBranch(companyId, branchId, request);
|
||||
return _convertBranchResponseToBranch(response);
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to update branch: $e');
|
||||
throw ServerFailure(message: 'Failed to update branch: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,9 +192,9 @@ class CompanyService {
|
||||
try {
|
||||
await _remoteDataSource.deleteBranch(companyId, branchId);
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to delete branch: $e');
|
||||
throw ServerFailure(message: 'Failed to delete branch: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,9 +203,9 @@ class CompanyService {
|
||||
final dtoList = await _remoteDataSource.getCompanyBranches(companyId);
|
||||
return dtoList.map((dto) => _convertBranchDtoToBranch(dto)).toList();
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to fetch company branches: $e');
|
||||
throw ServerFailure(message: 'Failed to fetch company branches: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,9 +214,9 @@ class CompanyService {
|
||||
try {
|
||||
return await _remoteDataSource.getCompaniesWithBranches();
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to fetch companies with branches: $e');
|
||||
throw ServerFailure(message: 'Failed to fetch companies with branches: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,9 +225,9 @@ class CompanyService {
|
||||
try {
|
||||
return await _remoteDataSource.checkDuplicateCompany(name);
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to check duplicate: $e');
|
||||
throw ServerFailure(message: 'Failed to check duplicate: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,9 +237,9 @@ class CompanyService {
|
||||
final dtoList = await _remoteDataSource.searchCompanies(query);
|
||||
return dtoList.map((dto) => _convertListDtoToCompany(dto)).toList();
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to search companies: $e');
|
||||
throw ServerFailure(message: 'Failed to search companies: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,9 +248,9 @@ class CompanyService {
|
||||
try {
|
||||
await _remoteDataSource.updateCompanyStatus(id, isActive);
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to update company status: $e');
|
||||
throw ServerFailure(message: 'Failed to update company status: $e');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import 'package:superport/data/models/equipment/equipment_out_request.dart';
|
||||
import 'package:superport/data/models/equipment/equipment_request.dart';
|
||||
import 'package:superport/data/models/equipment/equipment_response.dart';
|
||||
import 'package:superport/models/equipment_unified_model.dart';
|
||||
import 'package:superport/utils/constants.dart';
|
||||
|
||||
class EquipmentService {
|
||||
final EquipmentRemoteDataSource _remoteDataSource = GetIt.instance<EquipmentRemoteDataSource>();
|
||||
@@ -34,9 +33,9 @@ class EquipmentService {
|
||||
|
||||
return dtoList.map((dto) => _convertListDtoToEquipment(dto)).toList();
|
||||
} on ServerException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to fetch equipment list: $e');
|
||||
throw ServerFailure(message: 'Failed to fetch equipment list: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,9 +58,9 @@ class EquipmentService {
|
||||
final response = await _remoteDataSource.createEquipment(request);
|
||||
return _convertResponseToEquipment(response);
|
||||
} on ServerException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to create equipment: $e');
|
||||
throw ServerFailure(message: 'Failed to create equipment: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,11 +70,16 @@ class EquipmentService {
|
||||
final response = await _remoteDataSource.getEquipmentDetail(id);
|
||||
return _convertResponseToEquipment(response);
|
||||
} on ServerException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to fetch equipment detail: $e');
|
||||
throw ServerFailure(message: 'Failed to fetch equipment detail: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// 장비 조회 (getEquipmentDetail의 alias)
|
||||
Future<Equipment> getEquipment(int id) async {
|
||||
return getEquipmentDetail(id);
|
||||
}
|
||||
|
||||
// 장비 수정
|
||||
Future<Equipment> updateEquipment(int id, Equipment equipment) async {
|
||||
@@ -96,9 +100,9 @@ class EquipmentService {
|
||||
final response = await _remoteDataSource.updateEquipment(id, request);
|
||||
return _convertResponseToEquipment(response);
|
||||
} on ServerException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to update equipment: $e');
|
||||
throw ServerFailure(message: 'Failed to update equipment: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,9 +111,9 @@ class EquipmentService {
|
||||
try {
|
||||
await _remoteDataSource.deleteEquipment(id);
|
||||
} on ServerException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to delete equipment: $e');
|
||||
throw ServerFailure(message: 'Failed to delete equipment: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,9 +123,9 @@ class EquipmentService {
|
||||
final response = await _remoteDataSource.changeEquipmentStatus(id, status, reason);
|
||||
return _convertResponseToEquipment(response);
|
||||
} on ServerException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to change equipment status: $e');
|
||||
throw ServerFailure(message: 'Failed to change equipment status: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,9 +141,9 @@ class EquipmentService {
|
||||
|
||||
return await _remoteDataSource.addEquipmentHistory(equipmentId, request);
|
||||
} on ServerException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to add equipment history: $e');
|
||||
throw ServerFailure(message: 'Failed to add equipment history: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,9 +152,9 @@ class EquipmentService {
|
||||
try {
|
||||
return await _remoteDataSource.getEquipmentHistory(equipmentId, page: page, perPage: perPage);
|
||||
} on ServerException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to fetch equipment history: $e');
|
||||
throw ServerFailure(message: 'Failed to fetch equipment history: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,9 +175,9 @@ class EquipmentService {
|
||||
|
||||
return await _remoteDataSource.equipmentIn(request);
|
||||
} on ServerException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to process equipment in: $e');
|
||||
throw ServerFailure(message: 'Failed to process equipment in: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,9 +200,9 @@ class EquipmentService {
|
||||
|
||||
return await _remoteDataSource.equipmentOut(request);
|
||||
} on ServerException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: 'Failed to process equipment out: $e');
|
||||
throw ServerFailure(message: 'Failed to process equipment out: $e');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,9 @@ import 'package:superport/models/license_model.dart';
|
||||
|
||||
@lazySingleton
|
||||
class LicenseService {
|
||||
final LicenseRemoteDataSource _remoteDataSource = GetIt.instance<LicenseRemoteDataSource>();
|
||||
final LicenseRemoteDataSource _remoteDataSource;
|
||||
|
||||
LicenseService(this._remoteDataSource);
|
||||
|
||||
// 라이선스 목록 조회
|
||||
Future<List<License>> getLicenses({
|
||||
@@ -32,9 +34,9 @@ class LicenseService {
|
||||
|
||||
return response.items.map((dto) => _convertDtoToLicense(dto)).toList();
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: '라이선스 목록을 불러오는 데 실패했습니다: $e');
|
||||
throw ServerFailure(message: '라이선스 목록을 불러오는 데 실패했습니다: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,36 +46,35 @@ class LicenseService {
|
||||
final dto = await _remoteDataSource.getLicenseById(id);
|
||||
return _convertDtoToLicense(dto);
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: '라이선스 정보를 불러오는 데 실패했습니다: $e');
|
||||
throw ServerFailure(message: '라이선스 정보를 불러오는 데 실패했습니다: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// 라이선스 생성
|
||||
Future<License> createLicense(License license) async {
|
||||
try {
|
||||
// Flutter 모델의 visitCycle과 durationMonths를 API 필드에 매핑
|
||||
// visitCycle은 remark에 저장하고, durationMonths는 날짜 계산에 사용
|
||||
final now = DateTime.now();
|
||||
final expiryDate = now.add(Duration(days: license.durationMonths * 30));
|
||||
|
||||
final request = CreateLicenseRequest(
|
||||
licenseKey: license.name, // name을 licenseKey로 매핑
|
||||
productName: '유지보수 계약', // 기본값 설정
|
||||
licenseType: 'maintenance', // 유지보수 타입으로 고정
|
||||
licenseKey: license.licenseKey,
|
||||
productName: license.productName,
|
||||
vendor: license.vendor,
|
||||
licenseType: license.licenseType,
|
||||
userCount: license.userCount,
|
||||
purchaseDate: license.purchaseDate,
|
||||
expiryDate: license.expiryDate,
|
||||
purchasePrice: license.purchasePrice,
|
||||
companyId: license.companyId,
|
||||
purchaseDate: now,
|
||||
expiryDate: expiryDate,
|
||||
remark: '방문주기: ${license.visitCycle}', // visitCycle을 remark에 저장
|
||||
branchId: license.branchId,
|
||||
remark: license.remark,
|
||||
);
|
||||
|
||||
final dto = await _remoteDataSource.createLicense(request);
|
||||
return _convertDtoToLicense(dto);
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: '라이선스 생성에 실패했습니다: $e');
|
||||
throw ServerFailure(message: '라이선스 생성에 실패했습니다: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,30 +82,28 @@ class LicenseService {
|
||||
Future<License> updateLicense(License license) async {
|
||||
try {
|
||||
if (license.id == null) {
|
||||
throw Failure(message: '라이선스 ID가 없습니다');
|
||||
}
|
||||
|
||||
// 기존 라이선스 정보를 먼저 조회
|
||||
final existingDto = await _remoteDataSource.getLicenseById(license.id!);
|
||||
|
||||
// 만료일 계산 (durationMonths가 변경된 경우)
|
||||
DateTime? newExpiryDate;
|
||||
if (existingDto.purchaseDate != null) {
|
||||
newExpiryDate = existingDto.purchaseDate!.add(Duration(days: license.durationMonths * 30));
|
||||
throw BusinessFailure(message: '라이선스 ID가 없습니다');
|
||||
}
|
||||
|
||||
final request = UpdateLicenseRequest(
|
||||
licenseKey: license.name,
|
||||
expiryDate: newExpiryDate,
|
||||
remark: '방문주기: ${license.visitCycle}',
|
||||
licenseKey: license.licenseKey,
|
||||
productName: license.productName,
|
||||
vendor: license.vendor,
|
||||
licenseType: license.licenseType,
|
||||
userCount: license.userCount,
|
||||
purchaseDate: license.purchaseDate,
|
||||
expiryDate: license.expiryDate,
|
||||
purchasePrice: license.purchasePrice,
|
||||
remark: license.remark,
|
||||
isActive: license.isActive,
|
||||
);
|
||||
|
||||
final dto = await _remoteDataSource.updateLicense(license.id!, request);
|
||||
return _convertDtoToLicense(dto);
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: '라이선스 수정에 실패했습니다: $e');
|
||||
throw ServerFailure(message: '라이선스 수정에 실패했습니다: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,9 +112,9 @@ class LicenseService {
|
||||
try {
|
||||
await _remoteDataSource.deleteLicense(id);
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: '라이선스 삭제에 실패했습니다: $e');
|
||||
throw ServerFailure(message: '라이선스 삭제에 실패했습니다: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,9 +125,9 @@ class LicenseService {
|
||||
final dto = await _remoteDataSource.assignLicense(licenseId, request);
|
||||
return _convertDtoToLicense(dto);
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: '라이선스 할당에 실패했습니다: $e');
|
||||
throw ServerFailure(message: '라이선스 할당에 실패했습니다: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,9 +137,9 @@ class LicenseService {
|
||||
final dto = await _remoteDataSource.unassignLicense(licenseId);
|
||||
return _convertDtoToLicense(dto);
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: '라이선스 할당 해제에 실패했습니다: $e');
|
||||
throw ServerFailure(message: '라이선스 할당 해제에 실패했습니다: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,33 +158,34 @@ class LicenseService {
|
||||
|
||||
return response.items.map((dto) => _convertExpiringDtoToLicense(dto)).toList();
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: '만료 예정 라이선스를 불러오는 데 실패했습니다: $e');
|
||||
throw ServerFailure(message: '만료 예정 라이선스를 불러오는 데 실패했습니다: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// DTO를 Flutter 모델로 변환
|
||||
License _convertDtoToLicense(LicenseDto dto) {
|
||||
// remark에서 방문주기 추출
|
||||
String visitCycle = '미방문'; // 기본값
|
||||
if (dto.remark != null && dto.remark!.contains('방문주기:')) {
|
||||
visitCycle = dto.remark!.split('방문주기:').last.trim();
|
||||
}
|
||||
|
||||
// 기간 계산 (purchaseDate와 expiryDate 차이)
|
||||
int durationMonths = 12; // 기본값
|
||||
if (dto.purchaseDate != null && dto.expiryDate != null) {
|
||||
final difference = dto.expiryDate!.difference(dto.purchaseDate!);
|
||||
durationMonths = (difference.inDays / 30).round();
|
||||
}
|
||||
|
||||
return License(
|
||||
id: dto.id,
|
||||
companyId: dto.companyId ?? 0,
|
||||
name: dto.licenseKey,
|
||||
durationMonths: durationMonths,
|
||||
visitCycle: visitCycle,
|
||||
licenseKey: dto.licenseKey,
|
||||
productName: dto.productName,
|
||||
vendor: dto.vendor,
|
||||
licenseType: dto.licenseType,
|
||||
userCount: dto.userCount,
|
||||
purchaseDate: dto.purchaseDate,
|
||||
expiryDate: dto.expiryDate,
|
||||
purchasePrice: dto.purchasePrice,
|
||||
companyId: dto.companyId,
|
||||
branchId: dto.branchId,
|
||||
assignedUserId: dto.assignedUserId,
|
||||
remark: dto.remark,
|
||||
isActive: dto.isActive,
|
||||
createdAt: dto.createdAt,
|
||||
updatedAt: dto.updatedAt,
|
||||
companyName: dto.companyName,
|
||||
branchName: dto.branchName,
|
||||
assignedUserName: dto.assignedUserName,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -193,10 +193,24 @@ class LicenseService {
|
||||
License _convertExpiringDtoToLicense(ExpiringLicenseDto dto) {
|
||||
return License(
|
||||
id: dto.id,
|
||||
companyId: 0, // ExpiringLicenseDto에는 companyId가 없으므로 기본값 사용
|
||||
name: dto.licenseKey,
|
||||
durationMonths: 12, // 기본값
|
||||
visitCycle: '미방문', // 기본값
|
||||
licenseKey: dto.licenseKey,
|
||||
productName: dto.productName,
|
||||
vendor: null,
|
||||
licenseType: null,
|
||||
userCount: null,
|
||||
purchaseDate: null,
|
||||
expiryDate: dto.expiryDate,
|
||||
purchasePrice: null,
|
||||
companyId: null,
|
||||
branchId: null,
|
||||
assignedUserId: null,
|
||||
remark: null,
|
||||
isActive: dto.isActive,
|
||||
createdAt: null,
|
||||
updatedAt: null,
|
||||
companyName: dto.companyName,
|
||||
branchName: null,
|
||||
assignedUserName: null,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -570,10 +570,12 @@ class MockDataService {
|
||||
for (final inspection in inspectionTypes) {
|
||||
addLicense(
|
||||
License(
|
||||
licenseKey: 'LIC-${DateTime.now().millisecondsSinceEpoch}-$visit-$inspection',
|
||||
productName: '12개월,$visit,$inspection',
|
||||
companyId: 1,
|
||||
name: '12개월,$visit,$inspection',
|
||||
durationMonths: 12,
|
||||
visitCycle: visit,
|
||||
purchaseDate: DateTime.now(),
|
||||
expiryDate: DateTime.now().add(const Duration(days: 365)),
|
||||
remark: '방문주기: $visit',
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -672,12 +674,12 @@ class MockDataService {
|
||||
|
||||
// 기존 입고 장비를 출고 상태로 변경
|
||||
void changeEquipmentStatus(int equipmentInId, EquipmentOut equipmentOut) {
|
||||
print('장비 상태 변경 시작: 입고 ID $equipmentInId');
|
||||
// 장비 상태 변경 시작: 입고 ID $equipmentInId
|
||||
|
||||
// 입고된 장비를 찾습니다
|
||||
final index = _equipmentIns.indexWhere((e) => e.id == equipmentInId);
|
||||
if (index != -1) {
|
||||
print('장비를 찾음: ${_equipmentIns[index].equipment.name}');
|
||||
// 장비를 찾음: ${_equipmentIns[index].equipment.name}
|
||||
|
||||
// 입고 장비의 상태를 출고(O)로 변경
|
||||
final equipment = _equipmentIns[index].equipment;
|
||||
@@ -687,7 +689,7 @@ class MockDataService {
|
||||
inDate: _equipmentIns[index].inDate,
|
||||
status: 'O', // 상태를 출고로 변경
|
||||
);
|
||||
print('입고 장비 상태를 "O"로 변경: ID ${_equipmentIns[index].id}');
|
||||
// 입고 장비 상태를 "O"로 변경: ID ${_equipmentIns[index].id}
|
||||
|
||||
// 출고 정보 저장
|
||||
final newEquipmentOut = EquipmentOut(
|
||||
@@ -702,11 +704,11 @@ class MockDataService {
|
||||
returnType: equipmentOut.returnType,
|
||||
);
|
||||
_equipmentOuts.add(newEquipmentOut);
|
||||
print('출고 정보 추가: ID ${newEquipmentOut.id}');
|
||||
// 출고 정보 추가: ID ${newEquipmentOut.id}
|
||||
|
||||
print('장비 상태 변경 완료');
|
||||
// 장비 상태 변경 완료
|
||||
} else {
|
||||
print('오류: ID $equipmentInId인 입고 장비를 찾을 수 없음');
|
||||
// 오류: ID $equipmentInId인 입고 장비를 찾을 수 없음
|
||||
}
|
||||
}
|
||||
|
||||
@@ -885,6 +887,19 @@ class MockDataService {
|
||||
_companies[index] = company;
|
||||
}
|
||||
}
|
||||
|
||||
void updateBranch(int companyId, Branch branch) {
|
||||
final companyIndex = _companies.indexWhere((c) => c.id == companyId);
|
||||
if (companyIndex != -1) {
|
||||
final company = _companies[companyIndex];
|
||||
if (company.branches != null) {
|
||||
final branchIndex = company.branches!.indexWhere((b) => b.id == branch.id);
|
||||
if (branchIndex != -1) {
|
||||
company.branches![branchIndex] = branch;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void deleteCompany(int id) {
|
||||
_companies.removeWhere((c) => c.id == id);
|
||||
@@ -944,10 +959,19 @@ class MockDataService {
|
||||
void addLicense(License license) {
|
||||
final newLicense = License(
|
||||
id: _licenseIdCounter++,
|
||||
licenseKey: license.licenseKey,
|
||||
productName: license.productName,
|
||||
vendor: license.vendor,
|
||||
licenseType: license.licenseType,
|
||||
userCount: license.userCount,
|
||||
purchaseDate: license.purchaseDate,
|
||||
expiryDate: license.expiryDate,
|
||||
purchasePrice: license.purchasePrice,
|
||||
companyId: license.companyId,
|
||||
name: license.name,
|
||||
durationMonths: license.durationMonths,
|
||||
visitCycle: license.visitCycle,
|
||||
branchId: license.branchId,
|
||||
assignedUserId: license.assignedUserId,
|
||||
remark: license.remark,
|
||||
isActive: license.isActive,
|
||||
);
|
||||
_licenses.add(newLicense);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:superport/core/errors/exceptions.dart';
|
||||
import 'package:superport/data/datasources/remote/user_remote_datasource.dart';
|
||||
import 'package:superport/data/models/user/user_dto.dart';
|
||||
import 'package:superport/models/user_model.dart';
|
||||
|
||||
@@ -26,9 +26,9 @@ class WarehouseService {
|
||||
|
||||
return response.items.map((dto) => _convertDtoToWarehouseLocation(dto)).toList();
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: '창고 위치 목록을 불러오는 데 실패했습니다: $e');
|
||||
throw ServerFailure(message: '창고 위치 목록을 불러오는 데 실패했습니다: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,9 +38,9 @@ class WarehouseService {
|
||||
final dto = await _remoteDataSource.getWarehouseLocationById(id);
|
||||
return _convertDtoToWarehouseLocation(dto);
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: '창고 위치 정보를 불러오는 데 실패했습니다: $e');
|
||||
throw ServerFailure(message: '창고 위치 정보를 불러오는 데 실패했습니다: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,9 +58,9 @@ class WarehouseService {
|
||||
final dto = await _remoteDataSource.createWarehouseLocation(request);
|
||||
return _convertDtoToWarehouseLocation(dto);
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: '창고 위치 생성에 실패했습니다: $e');
|
||||
throw ServerFailure(message: '창고 위치 생성에 실패했습니다: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,9 +77,9 @@ class WarehouseService {
|
||||
final dto = await _remoteDataSource.updateWarehouseLocation(location.id, request);
|
||||
return _convertDtoToWarehouseLocation(dto);
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: '창고 위치 수정에 실패했습니다: $e');
|
||||
throw ServerFailure(message: '창고 위치 수정에 실패했습니다: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,9 +88,9 @@ class WarehouseService {
|
||||
try {
|
||||
await _remoteDataSource.deleteWarehouseLocation(id);
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: '창고 위치 삭제에 실패했습니다: $e');
|
||||
throw ServerFailure(message: '창고 위치 삭제에 실패했습니다: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,9 +118,9 @@ class WarehouseService {
|
||||
'storedAt': dto.storedAt,
|
||||
}).toList();
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: '창고 장비 목록을 불러오는 데 실패했습니다: $e');
|
||||
throw ServerFailure(message: '창고 장비 목록을 불러오는 데 실패했습니다: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,9 +129,9 @@ class WarehouseService {
|
||||
try {
|
||||
return await _remoteDataSource.getWarehouseCapacity(id);
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: '창고 용량 정보를 불러오는 데 실패했습니다: $e');
|
||||
throw ServerFailure(message: '창고 용량 정보를 불러오는 데 실패했습니다: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,9 +141,9 @@ class WarehouseService {
|
||||
final dtos = await _remoteDataSource.getInUseWarehouseLocations();
|
||||
return dtos.map((dto) => _convertDtoToWarehouseLocation(dto)).toList();
|
||||
} on ApiException catch (e) {
|
||||
throw Failure(message: e.message);
|
||||
throw ServerFailure(message: e.message);
|
||||
} catch (e) {
|
||||
throw Failure(message: '사용 중인 창고 위치를 불러오는 데 실패했습니다: $e');
|
||||
throw ServerFailure(message: '사용 중인 창고 위치를 불러오는 데 실패했습니다: $e');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user