feat: API 연동 개선 및 라이선스 모델 확장

- 라이선스 모델 전면 개편 (상세 필드 추가, 계산 필드 구현)
- API 응답 처리 개선 (HTTP 상태 코드 기반)
- 장비 출고 폼 컨트롤러 추가
- 회사 지점 정보 모델 추가
- 공통 데이터 모델 구조 추가
- 전체 서비스 레이어 API 호출 방식 통일
- UI 컴포넌트 마이너 개선
This commit is contained in:
JiWoong Sul
2025-07-25 01:22:15 +09:00
parent 8384423cf2
commit 71b7b7f40b
42 changed files with 1543 additions and 315 deletions

View 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);
}

View 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;
}

View 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);

View 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);
}

View 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;
}

View 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,
};