import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import './leave_model.dart'; import './leave_api.dart'; class LeaveDetail extends StatefulWidget { final Leave leave; const LeaveDetail({required this.leave, super.key}); @override State createState() => _LeaveDetailState(); } class _LeaveDetailState extends State { final LeaveApiService _apiService = LeaveApiService(); bool _isWithdrawing = false; // 實作撤回邏輯 Future _handleWithdraw() async { final confirm = await showDialog( context: context, builder: (ctx) => AlertDialog( title: const Text('確認撤回'), content: const Text('您確定要撤回此張請假單嗎?'), actions: [ TextButton(onPressed: () => Navigator.pop(ctx, false), child: const Text('取消')), TextButton(onPressed: () => Navigator.pop(ctx, true), child: const Text('確定', style: TextStyle(color: Colors.red))), ], ), ); if (confirm == true) { setState(() => _isWithdrawing = true); final success = await _apiService.withdrawLeave(widget.leave.billNo); setState(() => _isWithdrawing = false); if (success && mounted) { ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('已成功撤回'))); Navigator.pop(context, true); // 回傳 true 讓前一頁 (Manager) 重新整理列表 } } } @override Widget build(BuildContext context) { final leave = widget.leave; return Scaffold( appBar: AppBar(title: const Text('請假單詳情')), body: SingleChildScrollView( padding: const EdgeInsets.all(20.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildHeaderStatus(), const SizedBox(height: 24), _buildInfoCard(context), const SizedBox(height: 24), // 修正 3: 新增簽核進度區塊 const Text('簽核進度', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.blueGrey)), const SizedBox(height: 8), _buildFlowTimeline(), const SizedBox(height: 24), // 修正 5: 實作撤回按鈕邏輯 if (leave.flowStatus == '0' || leave.flowStatus == '1') SizedBox( width: double.infinity, child: OutlinedButton.icon( onPressed: _isWithdrawing ? null : _handleWithdraw, icon: _isWithdrawing ? const CircularProgressIndicator(strokeWidth: 2) : const Icon(Icons.history_outlined), label: Text(_isWithdrawing ? '處理中...' : '撤回申請'), style: OutlinedButton.styleFrom( foregroundColor: Colors.red, side: const BorderSide(color: Colors.red), padding: const EdgeInsets.symmetric(vertical: 12), ), ), ), ], ), ), ); } Widget _buildHeaderStatus() { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('單號: ${widget.leave.billNo}', style: const TextStyle(fontSize: 14, color: Colors.grey)), const SizedBox(height: 4), // 修正 4: 顯示 leaveTypeName Text(widget.leave.leaveTypeName, style: const TextStyle(fontSize: 28, fontWeight: FontWeight.bold)), ], ), Chip( backgroundColor: widget.leave.statusColor.withOpacity(0.1), side: BorderSide(color: widget.leave.statusColor), label: Text(widget.leave.statusText, style: TextStyle(color: widget.leave.statusColor, fontWeight: FontWeight.bold)), ), ], ); } Widget _buildInfoCard(BuildContext context) { // 處理代理人顯示文字 (防呆處理) String displayAgent = widget.leave.agentName.isNotEmpty ? '${widget.leave.agentName} (${widget.leave.agentId})' : widget.leave.agentId; return Card( elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15), side: BorderSide(color: Colors.grey.shade200)), child: Padding( padding: const EdgeInsets.all(20), child: Column( children: [ _buildDetailRow(Icons.calendar_month, '請假期間', widget.leave.formattedRange), const Divider(height: 30), _buildDetailRow(Icons.timer_outlined, '請假時數', '${widget.leave.days} 天 ${widget.leave.hours} 小時'), const Divider(height: 30), // 修正 6: 顯示帶有姓名的代理人資訊 _buildDetailRow(Icons.person_outline, '代理人', displayAgent), const Divider(height: 30), _buildDetailRow(Icons.edit_note, '事由', widget.leave.leaveNote ?? '未填寫'), ], ), ), ); } Widget _buildDetailRow(IconData icon, String label, String value) { return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(icon, size: 20, color: Colors.blueAccent), const SizedBox(width: 12), Text(label, style: const TextStyle(color: Colors.grey, fontSize: 14)), const Spacer(), Expanded( flex: 2, child: Text(value, textAlign: TextAlign.end, style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 15)), ), ], ); } // 實作簽核時間軸 (搭配 FutureBuilder) Widget _buildFlowTimeline() { return FutureBuilder>( future: _apiService.fetchFlowLogs(widget.leave.billNo), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) return const Center(child: CircularProgressIndicator()); if (!snapshot.hasData || snapshot.data!.isEmpty) return const Text('尚無簽核歷程', style: TextStyle(color: Colors.grey)); final logs = snapshot.data!; return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration(color: Colors.grey.shade50, borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.grey.shade200)), child: Column( children: logs.map((log) { return ListTile( contentPadding: EdgeInsets.zero, leading: Icon( log.status == '1' ? Icons.check_circle : (log.status == 'X' ? Icons.cancel : Icons.pending), color: log.status == '1' ? Colors.green : (log.status == 'X' ? Colors.red : Colors.orange), ), title: Text('${log.stepName} - ${log.approverName}'), subtitle: Text(log.time != null ? DateFormat('MM/dd HH:mm').format(log.time!) : ''), ); }).toList(), ), ); }, ); } }