feat: 장비 관리 기능 강화 및 이력 추적 개선

- EquipmentHistoryDto 모델 확장 (상세 정보 추가)
- 장비 이력 화면 UI/UX 개선
- 장비 입고 폼 검증 로직 강화
- 테스트 이력 화면 추가
- API 응답 처리 개선

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
JiWoong Sul
2025-08-09 02:17:16 +09:00
parent f8e8a95391
commit cddde57450
9 changed files with 738 additions and 258 deletions

View File

@@ -224,18 +224,73 @@ class EquipmentRemoteDataSourceImpl implements EquipmentRemoteDataSource {
'per_page': perPage,
};
print('[API] Requesting equipment history: ${ApiEndpoints.equipment}/$equipmentId/history');
print('[API] Query params: $queryParams');
final response = await _apiClient.get(
'${ApiEndpoints.equipment}/$equipmentId/history',
queryParameters: queryParams,
);
if (response.data['success'] == true && response.data['data'] != null) {
final List<dynamic> data = response.data['data'];
return data.map((json) => EquipmentHistoryDto.fromJson(json)).toList();
print('[API] Response status: ${response.statusCode}');
print('[API] Response data type: ${response.data.runtimeType}');
print('[API] Full response: ${response.data}');
// API 응답 구조 확인
if (response.data == null) {
print('[API ERROR] Response data is null');
throw ServerException(message: 'Empty response from server');
}
// 응답이 Map인지 확인
if (response.data is! Map) {
print('[API ERROR] Response is not a Map: ${response.data.runtimeType}');
throw ServerException(message: 'Invalid response format');
}
// success 필드 확인
final success = response.data['success'];
print('[API] Success field: $success');
if (success == true) {
final responseData = response.data['data'];
print('[API] Data field type: ${responseData?.runtimeType}');
if (responseData == null) {
print('[API] No data field, returning empty list');
return [];
}
if (responseData is! List) {
print('[API ERROR] Data is not a List: ${responseData.runtimeType}');
throw ServerException(message: 'Invalid data format');
}
final List<dynamic> data = responseData;
print('[API] History data count: ${data.length}');
if (data.isEmpty) {
print('[API] Empty history data');
return [];
}
print('[API] First history item: ${data.first}');
try {
final histories = data.map((json) {
print('[API] Parsing history item: $json');
return EquipmentHistoryDto.fromJson(json);
}).toList();
print('[API] Successfully parsed ${histories.length} history items');
return histories;
} catch (e) {
print('[API ERROR] Failed to parse history data: $e');
throw ServerException(message: 'Failed to parse history data: $e');
}
} else {
throw ServerException(
message: response.data['message'] ?? 'Failed to fetch equipment history',
);
final errorMessage = response.data['message'] ?? response.data['error'] ?? 'Failed to fetch equipment history';
print('[API ERROR] Request failed: $errorMessage');
throw ServerException(message: errorMessage);
}
} on DioException catch (e) {
throw ServerException(

View File

@@ -7,17 +7,17 @@ part 'equipment_history_dto.g.dart';
class EquipmentHistoryDto with _$EquipmentHistoryDto {
const factory EquipmentHistoryDto({
required int id,
required int equipmentId,
required String transactionType,
@JsonKey(name: 'equipment_id') required int equipmentId,
@JsonKey(name: 'transaction_type') required String transactionType,
required int quantity,
required DateTime transactionDate,
@JsonKey(name: 'transaction_date') required DateTime transactionDate,
String? remarks,
int? createdBy,
int? userId,
required DateTime createdAt,
@JsonKey(name: 'created_by') int? createdBy,
@JsonKey(name: 'user_id') int? userId,
@JsonKey(name: 'created_at') required DateTime createdAt,
// 추가 정보
String? userName,
String? performedBy,
@JsonKey(name: 'user_name') String? userName,
@JsonKey(name: 'performed_by') String? performedBy,
}) = _EquipmentHistoryDto;
factory EquipmentHistoryDto.fromJson(Map<String, dynamic> json) =>
@@ -27,11 +27,11 @@ class EquipmentHistoryDto with _$EquipmentHistoryDto {
@freezed
class CreateHistoryRequest with _$CreateHistoryRequest {
const factory CreateHistoryRequest({
required String transactionType,
@JsonKey(name: 'transaction_type') required String transactionType,
required int quantity,
DateTime? transactionDate,
@JsonKey(name: 'transaction_date') DateTime? transactionDate,
String? remarks,
int? userId,
@JsonKey(name: 'user_id') int? userId,
}) = _CreateHistoryRequest;
factory CreateHistoryRequest.fromJson(Map<String, dynamic> json) =>

View File

@@ -21,15 +21,23 @@ EquipmentHistoryDto _$EquipmentHistoryDtoFromJson(Map<String, dynamic> json) {
/// @nodoc
mixin _$EquipmentHistoryDto {
int get id => throw _privateConstructorUsedError;
@JsonKey(name: 'equipment_id')
int get equipmentId => throw _privateConstructorUsedError;
@JsonKey(name: 'transaction_type')
String get transactionType => throw _privateConstructorUsedError;
int get quantity => throw _privateConstructorUsedError;
@JsonKey(name: 'transaction_date')
DateTime get transactionDate => throw _privateConstructorUsedError;
String? get remarks => throw _privateConstructorUsedError;
@JsonKey(name: 'created_by')
int? get createdBy => throw _privateConstructorUsedError;
@JsonKey(name: 'user_id')
int? get userId => throw _privateConstructorUsedError;
@JsonKey(name: 'created_at')
DateTime get createdAt => throw _privateConstructorUsedError; // 추가 정보
@JsonKey(name: 'user_name')
String? get userName => throw _privateConstructorUsedError;
@JsonKey(name: 'performed_by')
String? get performedBy => throw _privateConstructorUsedError;
/// Serializes this EquipmentHistoryDto to a JSON map.
@@ -50,16 +58,16 @@ abstract class $EquipmentHistoryDtoCopyWith<$Res> {
@useResult
$Res call(
{int id,
int equipmentId,
String transactionType,
@JsonKey(name: 'equipment_id') int equipmentId,
@JsonKey(name: 'transaction_type') String transactionType,
int quantity,
DateTime transactionDate,
@JsonKey(name: 'transaction_date') DateTime transactionDate,
String? remarks,
int? createdBy,
int? userId,
DateTime createdAt,
String? userName,
String? performedBy});
@JsonKey(name: 'created_by') int? createdBy,
@JsonKey(name: 'user_id') int? userId,
@JsonKey(name: 'created_at') DateTime createdAt,
@JsonKey(name: 'user_name') String? userName,
@JsonKey(name: 'performed_by') String? performedBy});
}
/// @nodoc
@@ -148,16 +156,16 @@ abstract class _$$EquipmentHistoryDtoImplCopyWith<$Res>
@useResult
$Res call(
{int id,
int equipmentId,
String transactionType,
@JsonKey(name: 'equipment_id') int equipmentId,
@JsonKey(name: 'transaction_type') String transactionType,
int quantity,
DateTime transactionDate,
@JsonKey(name: 'transaction_date') DateTime transactionDate,
String? remarks,
int? createdBy,
int? userId,
DateTime createdAt,
String? userName,
String? performedBy});
@JsonKey(name: 'created_by') int? createdBy,
@JsonKey(name: 'user_id') int? userId,
@JsonKey(name: 'created_at') DateTime createdAt,
@JsonKey(name: 'user_name') String? userName,
@JsonKey(name: 'performed_by') String? performedBy});
}
/// @nodoc
@@ -239,16 +247,16 @@ class __$$EquipmentHistoryDtoImplCopyWithImpl<$Res>
class _$EquipmentHistoryDtoImpl implements _EquipmentHistoryDto {
const _$EquipmentHistoryDtoImpl(
{required this.id,
required this.equipmentId,
required this.transactionType,
@JsonKey(name: 'equipment_id') required this.equipmentId,
@JsonKey(name: 'transaction_type') required this.transactionType,
required this.quantity,
required this.transactionDate,
@JsonKey(name: 'transaction_date') required this.transactionDate,
this.remarks,
this.createdBy,
this.userId,
required this.createdAt,
this.userName,
this.performedBy});
@JsonKey(name: 'created_by') this.createdBy,
@JsonKey(name: 'user_id') this.userId,
@JsonKey(name: 'created_at') required this.createdAt,
@JsonKey(name: 'user_name') this.userName,
@JsonKey(name: 'performed_by') this.performedBy});
factory _$EquipmentHistoryDtoImpl.fromJson(Map<String, dynamic> json) =>
_$$EquipmentHistoryDtoImplFromJson(json);
@@ -256,25 +264,33 @@ class _$EquipmentHistoryDtoImpl implements _EquipmentHistoryDto {
@override
final int id;
@override
@JsonKey(name: 'equipment_id')
final int equipmentId;
@override
@JsonKey(name: 'transaction_type')
final String transactionType;
@override
final int quantity;
@override
@JsonKey(name: 'transaction_date')
final DateTime transactionDate;
@override
final String? remarks;
@override
@JsonKey(name: 'created_by')
final int? createdBy;
@override
@JsonKey(name: 'user_id')
final int? userId;
@override
@JsonKey(name: 'created_at')
final DateTime createdAt;
// 추가 정보
@override
@JsonKey(name: 'user_name')
final String? userName;
@override
@JsonKey(name: 'performed_by')
final String? performedBy;
@override
@@ -344,15 +360,17 @@ class _$EquipmentHistoryDtoImpl implements _EquipmentHistoryDto {
abstract class _EquipmentHistoryDto implements EquipmentHistoryDto {
const factory _EquipmentHistoryDto(
{required final int id,
required final int equipmentId,
required final String transactionType,
@JsonKey(name: 'equipment_id') required final int equipmentId,
@JsonKey(name: 'transaction_type') required final String transactionType,
required final int quantity,
@JsonKey(name: 'transaction_date')
required final DateTime transactionDate,
final String? remarks,
final int? createdBy,
final int? userId,
required final DateTime createdAt,
final String? userName,
@JsonKey(name: 'created_by') final int? createdBy,
@JsonKey(name: 'user_id') final int? userId,
@JsonKey(name: 'created_at') required final DateTime createdAt,
@JsonKey(name: 'user_name') final String? userName,
@JsonKey(name: 'performed_by')
final String? performedBy}) = _$EquipmentHistoryDtoImpl;
factory _EquipmentHistoryDto.fromJson(Map<String, dynamic> json) =
@@ -361,24 +379,32 @@ abstract class _EquipmentHistoryDto implements EquipmentHistoryDto {
@override
int get id;
@override
@JsonKey(name: 'equipment_id')
int get equipmentId;
@override
@JsonKey(name: 'transaction_type')
String get transactionType;
@override
int get quantity;
@override
@JsonKey(name: 'transaction_date')
DateTime get transactionDate;
@override
String? get remarks;
@override
@JsonKey(name: 'created_by')
int? get createdBy;
@override
@JsonKey(name: 'user_id')
int? get userId;
@override
@JsonKey(name: 'created_at')
DateTime get createdAt; // 추가 정보
@override
@JsonKey(name: 'user_name')
String? get userName;
@override
@JsonKey(name: 'performed_by')
String? get performedBy;
/// Create a copy of EquipmentHistoryDto
@@ -395,10 +421,13 @@ CreateHistoryRequest _$CreateHistoryRequestFromJson(Map<String, dynamic> json) {
/// @nodoc
mixin _$CreateHistoryRequest {
@JsonKey(name: 'transaction_type')
String get transactionType => throw _privateConstructorUsedError;
int get quantity => throw _privateConstructorUsedError;
@JsonKey(name: 'transaction_date')
DateTime? get transactionDate => throw _privateConstructorUsedError;
String? get remarks => throw _privateConstructorUsedError;
@JsonKey(name: 'user_id')
int? get userId => throw _privateConstructorUsedError;
/// Serializes this CreateHistoryRequest to a JSON map.
@@ -418,11 +447,11 @@ abstract class $CreateHistoryRequestCopyWith<$Res> {
_$CreateHistoryRequestCopyWithImpl<$Res, CreateHistoryRequest>;
@useResult
$Res call(
{String transactionType,
{@JsonKey(name: 'transaction_type') String transactionType,
int quantity,
DateTime? transactionDate,
@JsonKey(name: 'transaction_date') DateTime? transactionDate,
String? remarks,
int? userId});
@JsonKey(name: 'user_id') int? userId});
}
/// @nodoc
@@ -481,11 +510,11 @@ abstract class _$$CreateHistoryRequestImplCopyWith<$Res>
@override
@useResult
$Res call(
{String transactionType,
{@JsonKey(name: 'transaction_type') String transactionType,
int quantity,
DateTime? transactionDate,
@JsonKey(name: 'transaction_date') DateTime? transactionDate,
String? remarks,
int? userId});
@JsonKey(name: 'user_id') int? userId});
}
/// @nodoc
@@ -536,24 +565,27 @@ class __$$CreateHistoryRequestImplCopyWithImpl<$Res>
@JsonSerializable()
class _$CreateHistoryRequestImpl implements _CreateHistoryRequest {
const _$CreateHistoryRequestImpl(
{required this.transactionType,
{@JsonKey(name: 'transaction_type') required this.transactionType,
required this.quantity,
this.transactionDate,
@JsonKey(name: 'transaction_date') this.transactionDate,
this.remarks,
this.userId});
@JsonKey(name: 'user_id') this.userId});
factory _$CreateHistoryRequestImpl.fromJson(Map<String, dynamic> json) =>
_$$CreateHistoryRequestImplFromJson(json);
@override
@JsonKey(name: 'transaction_type')
final String transactionType;
@override
final int quantity;
@override
@JsonKey(name: 'transaction_date')
final DateTime? transactionDate;
@override
final String? remarks;
@override
@JsonKey(name: 'user_id')
final int? userId;
@override
@@ -601,24 +633,28 @@ class _$CreateHistoryRequestImpl implements _CreateHistoryRequest {
abstract class _CreateHistoryRequest implements CreateHistoryRequest {
const factory _CreateHistoryRequest(
{required final String transactionType,
{@JsonKey(name: 'transaction_type') required final String transactionType,
required final int quantity,
final DateTime? transactionDate,
@JsonKey(name: 'transaction_date') final DateTime? transactionDate,
final String? remarks,
@JsonKey(name: 'user_id')
final int? userId}) = _$CreateHistoryRequestImpl;
factory _CreateHistoryRequest.fromJson(Map<String, dynamic> json) =
_$CreateHistoryRequestImpl.fromJson;
@override
@JsonKey(name: 'transaction_type')
String get transactionType;
@override
int get quantity;
@override
@JsonKey(name: 'transaction_date')
DateTime? get transactionDate;
@override
String? get remarks;
@override
@JsonKey(name: 'user_id')
int? get userId;
/// Create a copy of CreateHistoryRequest

View File

@@ -10,52 +10,52 @@ _$EquipmentHistoryDtoImpl _$$EquipmentHistoryDtoImplFromJson(
Map<String, dynamic> json) =>
_$EquipmentHistoryDtoImpl(
id: (json['id'] as num).toInt(),
equipmentId: (json['equipmentId'] as num).toInt(),
transactionType: json['transactionType'] as String,
equipmentId: (json['equipment_id'] as num).toInt(),
transactionType: json['transaction_type'] as String,
quantity: (json['quantity'] as num).toInt(),
transactionDate: DateTime.parse(json['transactionDate'] as String),
transactionDate: DateTime.parse(json['transaction_date'] as String),
remarks: json['remarks'] as String?,
createdBy: (json['createdBy'] as num?)?.toInt(),
userId: (json['userId'] as num?)?.toInt(),
createdAt: DateTime.parse(json['createdAt'] as String),
userName: json['userName'] as String?,
performedBy: json['performedBy'] as String?,
createdBy: (json['created_by'] as num?)?.toInt(),
userId: (json['user_id'] as num?)?.toInt(),
createdAt: DateTime.parse(json['created_at'] as String),
userName: json['user_name'] as String?,
performedBy: json['performed_by'] as String?,
);
Map<String, dynamic> _$$EquipmentHistoryDtoImplToJson(
_$EquipmentHistoryDtoImpl instance) =>
<String, dynamic>{
'id': instance.id,
'equipmentId': instance.equipmentId,
'transactionType': instance.transactionType,
'equipment_id': instance.equipmentId,
'transaction_type': instance.transactionType,
'quantity': instance.quantity,
'transactionDate': instance.transactionDate.toIso8601String(),
'transaction_date': instance.transactionDate.toIso8601String(),
'remarks': instance.remarks,
'createdBy': instance.createdBy,
'userId': instance.userId,
'createdAt': instance.createdAt.toIso8601String(),
'userName': instance.userName,
'performedBy': instance.performedBy,
'created_by': instance.createdBy,
'user_id': instance.userId,
'created_at': instance.createdAt.toIso8601String(),
'user_name': instance.userName,
'performed_by': instance.performedBy,
};
_$CreateHistoryRequestImpl _$$CreateHistoryRequestImplFromJson(
Map<String, dynamic> json) =>
_$CreateHistoryRequestImpl(
transactionType: json['transactionType'] as String,
transactionType: json['transaction_type'] as String,
quantity: (json['quantity'] as num).toInt(),
transactionDate: json['transactionDate'] == null
transactionDate: json['transaction_date'] == null
? null
: DateTime.parse(json['transactionDate'] as String),
: DateTime.parse(json['transaction_date'] as String),
remarks: json['remarks'] as String?,
userId: (json['userId'] as num?)?.toInt(),
userId: (json['user_id'] as num?)?.toInt(),
);
Map<String, dynamic> _$$CreateHistoryRequestImplToJson(
_$CreateHistoryRequestImpl instance) =>
<String, dynamic>{
'transactionType': instance.transactionType,
'transaction_type': instance.transactionType,
'quantity': instance.quantity,
'transactionDate': instance.transactionDate?.toIso8601String(),
'transaction_date': instance.transactionDate?.toIso8601String(),
'remarks': instance.remarks,
'userId': instance.userId,
'user_id': instance.userId,
};