import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:get_it/get_it.dart'; import 'package:superport/data/models/equipment/equipment_history_dto.dart'; import 'package:superport/services/equipment_service.dart'; import 'package:superport/screens/common/custom_widgets.dart'; import 'package:superport/core/errors/failures.dart'; import 'package:intl/intl.dart'; class EquipmentHistoryScreen extends StatefulWidget { final int equipmentId; final String equipmentName; const EquipmentHistoryScreen({ Key? key, required this.equipmentId, required this.equipmentName, }) : super(key: key); @override State createState() => _EquipmentHistoryScreenState(); } class _EquipmentHistoryScreenState extends State { final EquipmentService _equipmentService = GetIt.instance(); List _histories = []; bool _isLoading = false; // 초기값을 false로 변경 bool _isInitialLoad = true; // 초기 로딩 상태 추가 String? _error; int _currentPage = 1; final int _perPage = 20; bool _hasMore = true; final ScrollController _scrollController = ScrollController(); @override void initState() { super.initState(); print('[INIT] EquipmentHistoryScreen initialized for equipment ${widget.equipmentId}'); _loadHistory(isInitialLoad: true); _scrollController.addListener(_onScroll); } @override void dispose() { _scrollController.dispose(); super.dispose(); } void _onScroll() { if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent - 200) { _loadMoreHistory(); } } Future _loadHistory({bool isRefresh = false, bool isInitialLoad = false}) async { print('[_loadHistory] Called - isRefresh: $isRefresh, isInitialLoad: $isInitialLoad, _isLoading: $_isLoading'); if (isRefresh) { _currentPage = 1; _hasMore = true; _histories.clear(); } // 초기 로딩이 아닌 경우에만 중복 호출 방지 if (!isInitialLoad && (!_hasMore || (!isRefresh && _isLoading))) { print('[_loadHistory] Skipping - hasMore: $_hasMore, isLoading: $_isLoading'); return; } if (!mounted) return; setState(() { _isLoading = true; _error = null; if (isInitialLoad) _isInitialLoad = false; }); try { print('[DEBUG] ==== STARTING HISTORY LOAD ===='); print('[DEBUG] Equipment ID: ${widget.equipmentId}'); print('[DEBUG] Equipment Name: ${widget.equipmentName}'); print('[DEBUG] Page: $_currentPage, PerPage: $_perPage'); print('[DEBUG] Current time: ${DateTime.now()}'); // 타임아웃 설정 final histories = await _equipmentService.getEquipmentHistory( widget.equipmentId, page: _currentPage, perPage: _perPage, ).timeout( const Duration(seconds: 10), onTimeout: () { print('[ERROR] API call timeout after 10 seconds'); throw Exception('API 호출 시간 초과 (10초)'); }, ); print('[DEBUG] API call completed successfully'); print('[DEBUG] Received ${histories.length} history records'); if (histories.isNotEmpty) { print('[DEBUG] First history record: ${histories.first.toJson()}'); } else { print('[DEBUG] No history records found'); } if (!mounted) { print('[WARNING] Widget not mounted, skipping setState'); return; } setState(() { if (isRefresh) { _histories = histories; } else { _histories.addAll(histories); } _hasMore = histories.length == _perPage; if (_hasMore) _currentPage++; _isLoading = false; print('[DEBUG] State updated - Loading: false, Histories count: ${_histories.length}'); }); } on Failure catch (e) { print('[ERROR] Failure loading history: ${e.message}'); if (!mounted) return; setState(() { _error = e.message; _isLoading = false; }); } catch (e, stackTrace) { print('[ERROR] Unexpected error loading history: $e'); print('[ERROR] Stack trace: $stackTrace'); if (!mounted) return; setState(() { _error = '이력을 불러오는 중 오류가 발생했습니다: $e'; _isLoading = false; }); } finally { print('[DEBUG] ==== HISTORY LOAD COMPLETED ===='); } } Future _loadMoreHistory() async { await _loadHistory(); } String _formatDate(DateTime? date) { if (date == null) return '-'; return DateFormat('yyyy-MM-dd HH:mm').format(date); } String _getTransactionTypeText(String? type) { switch (type) { case 'I': return '입고'; case 'O': return '출고'; case 'R': return '대여'; case 'T': return '반납'; case 'D': return '폐기'; default: return type ?? '-'; } } Color _getTransactionTypeColor(String? type) { switch (type) { case 'I': return Colors.green; case 'O': return Colors.blue; case 'R': return Colors.orange; case 'T': return Colors.teal; case 'D': return Colors.red; default: return Colors.grey; } } Widget _buildHistoryItem(EquipmentHistoryDto history) { final typeColor = _getTransactionTypeColor(history.transactionType); return Card( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: ListTile( leading: CircleAvatar( backgroundColor: typeColor.withOpacity(0.2), child: Text( _getTransactionTypeText(history.transactionType), style: TextStyle( color: typeColor, fontWeight: FontWeight.bold, fontSize: 12, ), ), ), title: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Text( history.remarks ?? '비고 없음', overflow: TextOverflow.ellipsis, ), ), Text( '수량: ${history.quantity}', style: const TextStyle(fontSize: 14), ), ], ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 4), Text(_formatDate(history.transactionDate)), if (history.userName != null) Text('담당자: ${history.userName}'), ], ), ), ); } @override Widget build(BuildContext context) { print('[BUILD] EquipmentHistoryScreen - Loading: $_isLoading, InitialLoad: $_isInitialLoad, Error: $_error, Histories: ${_histories.length}'); return Scaffold( appBar: AppBar( title: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('장비 이력'), Text( widget.equipmentName, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.normal), ), ], ), actions: [ IconButton( icon: const Icon(Icons.refresh), onPressed: () => _loadHistory(isRefresh: true), tooltip: '새로고침', ), ], ), body: Builder( builder: (context) { print('[UI] Building body - Loading: $_isLoading, InitialLoad: $_isInitialLoad, Error: $_error, Histories: ${_histories.length}'); // 초기 로딩 또는 일반 로딩 중 if ((_isInitialLoad || _isLoading) && _histories.isEmpty) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const CircularProgressIndicator(), const SizedBox(height: 16), Text('장비 ID ${widget.equipmentId}의 이력을 불러오는 중...'), const SizedBox(height: 8), TextButton( onPressed: () { print('[USER] Cancel loading clicked'); setState(() { _isLoading = false; _error = '사용자가 로딩을 취소했습니다'; }); }, child: const Text('로딩 취소'), ), ], ), ); } if (_error != null && _histories.isEmpty) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.error_outline, size: 48, color: Colors.red), const SizedBox(height: 16), Text('오류 발생', style: Theme.of(context).textTheme.titleLarge), const SizedBox(height: 8), Padding( padding: const EdgeInsets.symmetric(horizontal: 32), child: Text( _error!, textAlign: TextAlign.center, style: const TextStyle(color: Colors.red), ), ), const SizedBox(height: 16), ElevatedButton.icon( icon: const Icon(Icons.refresh), label: const Text('다시 시도'), onPressed: () => _loadHistory(isRefresh: true), ), ], ), ); } return RefreshIndicator( onRefresh: () => _loadHistory(isRefresh: true), child: _histories.isEmpty ? ListView( children: [ const SizedBox(height: 200), const Icon(Icons.history, size: 48, color: Colors.grey), const SizedBox(height: 16), const Center( child: Text( '이력이 없습니다.', style: TextStyle(fontSize: 16, color: Colors.grey), ), ), const SizedBox(height: 8), Center( child: TextButton( onPressed: () => _loadHistory(isRefresh: true), child: const Text('새로고침'), ), ), ], ) : ListView.builder( controller: _scrollController, itemCount: _histories.length + (_hasMore ? 1 : 0), itemBuilder: (context, index) { if (index == _histories.length) { return const Padding( padding: EdgeInsets.all(16.0), child: Center(child: CircularProgressIndicator()), ); } return _buildHistoryItem(_histories[index]); }, ), ); }, ), ); } }