refactor: UI 화면 통합 및 불필요한 파일 정리
- 모든 *_redesign.dart 파일을 기본 화면 파일로 통합 - 백업용 컨트롤러 파일들 제거 (*_controller.backup.dart) - 사용하지 않는 예제 및 테스트 파일 제거 - Clean Architecture 적용 후 남은 정리 작업 완료 - 테스트 코드 정리 및 구조 개선 준비 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,210 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:superport/models/warehouse_location_model.dart';
|
||||
import 'package:superport/services/warehouse_service.dart';
|
||||
import 'package:superport/core/errors/failures.dart';
|
||||
import 'package:superport/core/utils/error_handler.dart';
|
||||
|
||||
/// 입고지 리스트 상태 및 CRUD만 담당하는 컨트롤러 클래스 (SRP 적용)
|
||||
/// UI, 네비게이션, 다이얼로그 등은 포함하지 않음
|
||||
/// 향후 서비스/리포지토리 DI 구조로 확장 가능
|
||||
class WarehouseLocationListController extends ChangeNotifier {
|
||||
late final WarehouseService _warehouseService;
|
||||
|
||||
List<WarehouseLocation> _warehouseLocations = [];
|
||||
List<WarehouseLocation> _filteredLocations = [];
|
||||
bool _isLoading = false;
|
||||
String? _error;
|
||||
String _searchQuery = '';
|
||||
int _currentPage = 1;
|
||||
final int _pageSize = 20;
|
||||
bool _hasMore = true;
|
||||
int _total = 0;
|
||||
|
||||
// 필터 옵션
|
||||
bool? _isActive;
|
||||
|
||||
WarehouseLocationListController() {
|
||||
if (GetIt.instance.isRegistered<WarehouseService>()) {
|
||||
_warehouseService = GetIt.instance<WarehouseService>();
|
||||
} else {
|
||||
throw Exception('WarehouseService not registered');
|
||||
}
|
||||
}
|
||||
|
||||
// Getters
|
||||
List<WarehouseLocation> get warehouseLocations => _filteredLocations;
|
||||
bool get isLoading => _isLoading;
|
||||
String? get error => _error;
|
||||
String get searchQuery => _searchQuery;
|
||||
int get currentPage => _currentPage;
|
||||
bool get hasMore => _hasMore;
|
||||
int get total => _total;
|
||||
bool? get isActive => _isActive;
|
||||
|
||||
/// 데이터 로드
|
||||
Future<void> loadWarehouseLocations({bool isInitialLoad = true}) async {
|
||||
if (_isLoading) return;
|
||||
|
||||
_isLoading = true;
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
|
||||
// API 사용 시 ErrorHandler 적용
|
||||
print('╔══════════════════════════════════════════════════════════');
|
||||
print('║ 🏭 입고지 목록 API 호출 시작');
|
||||
print('║ • 활성 필터: ${_isActive != null ? (_isActive! ? "활성" : "비활성") : "전체"}');
|
||||
print('╚══════════════════════════════════════════════════════════');
|
||||
|
||||
final fetchedLocations = await ErrorHandler.handleApiCall<List<WarehouseLocation>>(
|
||||
() => _warehouseService.getWarehouseLocations(
|
||||
page: 1,
|
||||
perPage: 1000, // 충분히 큰 값으로 전체 데이터 로드
|
||||
isActive: _isActive,
|
||||
),
|
||||
onError: (failure) {
|
||||
_error = ErrorHandler.getUserFriendlyMessage(failure);
|
||||
print('[WarehouseLocationListController] API 에러: ${failure.message}');
|
||||
},
|
||||
);
|
||||
|
||||
if (fetchedLocations != null) {
|
||||
print('╔══════════════════════════════════════════════════════════');
|
||||
print('║ 📊 입고지 목록 로드 완료');
|
||||
print('║ ▶ 총 입고지 수: ${fetchedLocations.length}개');
|
||||
print('╟──────────────────────────────────────────────────────────');
|
||||
|
||||
// 상태별 통계 (입고지에 상태가 있다면)
|
||||
int activeCount = 0;
|
||||
int inactiveCount = 0;
|
||||
for (final location in fetchedLocations) {
|
||||
// isActive 필드가 있다면 활용
|
||||
activeCount++; // 현재는 모두 활성으로 가정
|
||||
}
|
||||
|
||||
print('║ • 활성 입고지: $activeCount개');
|
||||
if (inactiveCount > 0) {
|
||||
print('║ • 비활성 입고지: $inactiveCount개');
|
||||
}
|
||||
|
||||
print('╟──────────────────────────────────────────────────────────');
|
||||
print('║ 📑 전체 데이터 로드 완료');
|
||||
print('║ • View에서 페이지네이션 처리 예정');
|
||||
print('╚══════════════════════════════════════════════════════════');
|
||||
|
||||
_warehouseLocations = fetchedLocations;
|
||||
_hasMore = false; // 전체 데이터를 로드했으므로 더 이상 로드할 필요 없음
|
||||
_total = fetchedLocations.length;
|
||||
_applySearchFilter();
|
||||
print('[WarehouseLocationListController] After filtering: ${_filteredLocations.length} locations shown');
|
||||
}
|
||||
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// 다음 페이지 로드
|
||||
Future<void> loadNextPage() async {
|
||||
if (!_hasMore || _isLoading) return;
|
||||
await loadWarehouseLocations(isInitialLoad: false);
|
||||
}
|
||||
|
||||
// 검색
|
||||
void search(String query) {
|
||||
_searchQuery = query;
|
||||
_applySearchFilter();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// 검색 필터 적용
|
||||
void _applySearchFilter() {
|
||||
if (_searchQuery.isEmpty) {
|
||||
_filteredLocations = List.from(_warehouseLocations);
|
||||
} else {
|
||||
_filteredLocations = _warehouseLocations.where((location) {
|
||||
return location.name.toLowerCase().contains(_searchQuery.toLowerCase()) ||
|
||||
location.address.toString().toLowerCase().contains(_searchQuery.toLowerCase());
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
|
||||
// 필터 설정
|
||||
void setFilters({bool? isActive}) {
|
||||
_isActive = isActive;
|
||||
loadWarehouseLocations();
|
||||
}
|
||||
|
||||
// 필터 초기화
|
||||
void clearFilters() {
|
||||
_isActive = null;
|
||||
_searchQuery = '';
|
||||
loadWarehouseLocations();
|
||||
}
|
||||
|
||||
/// 입고지 추가
|
||||
Future<void> addWarehouseLocation(WarehouseLocation location) async {
|
||||
await ErrorHandler.handleApiCall<void>(
|
||||
() => _warehouseService.createWarehouseLocation(location),
|
||||
onError: (failure) {
|
||||
_error = ErrorHandler.getUserFriendlyMessage(failure);
|
||||
notifyListeners();
|
||||
},
|
||||
);
|
||||
|
||||
// 목록 새로고침
|
||||
await loadWarehouseLocations();
|
||||
}
|
||||
|
||||
/// 입고지 수정
|
||||
Future<void> updateWarehouseLocation(WarehouseLocation location) async {
|
||||
await ErrorHandler.handleApiCall<void>(
|
||||
() => _warehouseService.updateWarehouseLocation(location),
|
||||
onError: (failure) {
|
||||
_error = ErrorHandler.getUserFriendlyMessage(failure);
|
||||
notifyListeners();
|
||||
},
|
||||
);
|
||||
|
||||
// 목록에서 업데이트
|
||||
final index = _warehouseLocations.indexWhere((l) => l.id == location.id);
|
||||
if (index != -1) {
|
||||
_warehouseLocations[index] = location;
|
||||
_applySearchFilter();
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
/// 입고지 삭제
|
||||
Future<void> deleteWarehouseLocation(int id) async {
|
||||
await ErrorHandler.handleApiCall<void>(
|
||||
() => _warehouseService.deleteWarehouseLocation(id),
|
||||
onError: (failure) {
|
||||
_error = ErrorHandler.getUserFriendlyMessage(failure);
|
||||
notifyListeners();
|
||||
},
|
||||
);
|
||||
|
||||
// 목록에서 제거
|
||||
_warehouseLocations.removeWhere((l) => l.id == id);
|
||||
_applySearchFilter();
|
||||
_total--;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// 새로고침
|
||||
Future<void> refresh() async {
|
||||
await loadWarehouseLocations();
|
||||
}
|
||||
|
||||
// 사용 중인 창고 위치 조회
|
||||
Future<List<WarehouseLocation>> getInUseWarehouseLocations() async {
|
||||
final locations = await ErrorHandler.handleApiCall<List<WarehouseLocation>>(
|
||||
() => _warehouseService.getInUseWarehouseLocations(),
|
||||
onError: (failure) {
|
||||
_error = ErrorHandler.getUserFriendlyMessage(failure);
|
||||
notifyListeners();
|
||||
},
|
||||
);
|
||||
return locations ?? [];
|
||||
}
|
||||
}
|
||||
@@ -31,8 +31,8 @@ class WarehouseLocationListController extends BaseListController<WarehouseLocati
|
||||
required PaginationParams params,
|
||||
Map<String, dynamic>? additionalFilters,
|
||||
}) async {
|
||||
// API 사용
|
||||
final fetchedLocations = await ErrorHandler.handleApiCall<List<WarehouseLocation>>(
|
||||
// API 사용 (PaginatedResponse 반환)
|
||||
final response = await ErrorHandler.handleApiCall(
|
||||
() => _warehouseService.getWarehouseLocations(
|
||||
page: params.page,
|
||||
perPage: params.perPage,
|
||||
@@ -43,21 +43,31 @@ class WarehouseLocationListController extends BaseListController<WarehouseLocati
|
||||
},
|
||||
);
|
||||
|
||||
final items = fetchedLocations ?? [];
|
||||
if (response == null) {
|
||||
return PagedResult(
|
||||
items: [],
|
||||
meta: PaginationMeta(
|
||||
currentPage: params.page,
|
||||
perPage: params.perPage,
|
||||
total: 0,
|
||||
totalPages: 0,
|
||||
hasNext: false,
|
||||
hasPrevious: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 임시로 메타데이터 생성 (추후 API에서 실제 메타데이터 반환 시 수정)
|
||||
// PaginatedResponse를 PagedResult로 변환
|
||||
final meta = PaginationMeta(
|
||||
currentPage: params.page,
|
||||
perPage: params.perPage,
|
||||
total: items.length < params.perPage ?
|
||||
(params.page - 1) * params.perPage + items.length :
|
||||
params.page * params.perPage + 1,
|
||||
totalPages: items.length < params.perPage ? params.page : params.page + 1,
|
||||
hasNext: items.length >= params.perPage,
|
||||
hasPrevious: params.page > 1,
|
||||
currentPage: response.page,
|
||||
perPage: response.size,
|
||||
total: response.totalElements,
|
||||
totalPages: response.totalPages,
|
||||
hasNext: !response.last,
|
||||
hasPrevious: !response.first,
|
||||
);
|
||||
|
||||
return PagedResult(items: items, meta: meta);
|
||||
return PagedResult(items: response.items, meta: meta);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||
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/screens/common/theme_shadcn.dart';
|
||||
import 'package:superport/screens/common/templates/form_layout_template.dart';
|
||||
import 'controllers/warehouse_location_form_controller.dart';
|
||||
|
||||
@@ -50,7 +50,7 @@ class _WarehouseLocationFormScreenState
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(_controller.isEditMode ? '입고지가 수정되었습니다' : '입고지가 추가되었습니다'),
|
||||
backgroundColor: AppThemeTailwind.success,
|
||||
backgroundColor: ShadcnTheme.success,
|
||||
),
|
||||
);
|
||||
// 리스트 화면으로 돌아가기
|
||||
@@ -62,7 +62,7 @@ class _WarehouseLocationFormScreenState
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(_controller.error ?? '저장에 실패했습니다'),
|
||||
backgroundColor: AppThemeTailwind.danger,
|
||||
backgroundColor: ShadcnTheme.error,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,19 +14,18 @@ import 'package:superport/utils/constants.dart';
|
||||
import 'package:superport/core/widgets/auth_guard.dart';
|
||||
|
||||
/// shadcn/ui 스타일로 재설계된 입고지 관리 화면
|
||||
class WarehouseLocationListRedesign extends StatefulWidget {
|
||||
const WarehouseLocationListRedesign({Key? key}) : super(key: key);
|
||||
class WarehouseLocationList extends StatefulWidget {
|
||||
const WarehouseLocationList({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<WarehouseLocationListRedesign> createState() =>
|
||||
_WarehouseLocationListRedesignState();
|
||||
State<WarehouseLocationList> createState() =>
|
||||
_WarehouseLocationListState();
|
||||
}
|
||||
|
||||
class _WarehouseLocationListRedesignState
|
||||
extends State<WarehouseLocationListRedesign> {
|
||||
class _WarehouseLocationListState
|
||||
extends State<WarehouseLocationList> {
|
||||
late WarehouseLocationListController _controller;
|
||||
int _currentPage = 1;
|
||||
final int _pageSize = 10;
|
||||
// 페이지 상태는 이제 Controller에서 관리
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -46,8 +45,7 @@ class _WarehouseLocationListRedesignState
|
||||
|
||||
/// 리스트 새로고침
|
||||
void _reload() {
|
||||
_currentPage = 1;
|
||||
_controller.loadWarehouseLocations();
|
||||
_controller.refresh(); // Controller에서 페이지 리셋 처리
|
||||
}
|
||||
|
||||
/// 입고지 추가 폼으로 이동
|
||||
@@ -107,15 +105,9 @@ class _WarehouseLocationListRedesignState
|
||||
value: _controller,
|
||||
child: Consumer<WarehouseLocationListController>(
|
||||
builder: (context, controller, child) {
|
||||
final int totalCount = controller.warehouseLocations.length;
|
||||
final int startIndex = (_currentPage - 1) * _pageSize;
|
||||
final int endIndex =
|
||||
(startIndex + _pageSize) > totalCount
|
||||
? totalCount
|
||||
: (startIndex + _pageSize);
|
||||
final List<WarehouseLocation> pagedLocations = totalCount > 0 && startIndex < totalCount
|
||||
? controller.warehouseLocations.sublist(startIndex, endIndex)
|
||||
: [];
|
||||
// Controller가 이미 페이징된 데이터를 제공
|
||||
final List<WarehouseLocation> pagedLocations = controller.warehouseLocations;
|
||||
final int totalCount = controller.total; // 실제 전체 개수
|
||||
|
||||
return BaseListScreen(
|
||||
isLoading: controller.isLoading && controller.warehouseLocations.isEmpty,
|
||||
@@ -150,17 +142,15 @@ class _WarehouseLocationListRedesignState
|
||||
),
|
||||
|
||||
// 데이터 테이블
|
||||
dataTable: _buildDataTable(pagedLocations, startIndex),
|
||||
dataTable: _buildDataTable(pagedLocations),
|
||||
|
||||
// 페이지네이션
|
||||
pagination: totalCount > _pageSize ? Pagination(
|
||||
pagination: totalCount > controller.pageSize ? Pagination(
|
||||
totalCount: totalCount,
|
||||
currentPage: _currentPage,
|
||||
pageSize: _pageSize,
|
||||
currentPage: controller.currentPage,
|
||||
pageSize: controller.pageSize,
|
||||
onPageChanged: (page) {
|
||||
setState(() {
|
||||
_currentPage = page;
|
||||
});
|
||||
controller.goToPage(page);
|
||||
},
|
||||
) : null,
|
||||
);
|
||||
@@ -171,7 +161,7 @@ class _WarehouseLocationListRedesignState
|
||||
}
|
||||
|
||||
/// 데이터 테이블
|
||||
Widget _buildDataTable(List<WarehouseLocation> pagedLocations, int startIndex) {
|
||||
Widget _buildDataTable(List<WarehouseLocation> pagedLocations) {
|
||||
if (pagedLocations.isEmpty) {
|
||||
return StandardEmptyState(
|
||||
title:
|
||||
@@ -257,7 +247,7 @@ class _WarehouseLocationListRedesignState
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text(
|
||||
'${startIndex + index + 1}',
|
||||
'${(_controller.currentPage - 1) * _controller.pageSize + index + 1}',
|
||||
style: ShadcnTheme.bodySmall,
|
||||
),
|
||||
),
|
||||
Reference in New Issue
Block a user