import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:flutter_animate/flutter_animate.dart'; import '../providers/work_order_provider.dart'; import '../providers/order_provider.dart'; import '../models/work_order.dart'; import '../models/order.dart'; import '../theme.dart'; import '../l10n/app_localizations.dart'; import '../widgets/skeleton.dart'; class OrderWorkPage extends ConsumerStatefulWidget { final String initialTab; const OrderWorkPage({super.key, this.initialTab = 'work'}); @override ConsumerState createState() => _OrderWorkPageState(); } class _OrderWorkPageState extends ConsumerState with SingleTickerProviderStateMixin { late TabController _tabController; WorkOrderStatus _workStatus = WorkOrderStatus.all; OrderStatus _orderStatus = OrderStatus.all; @override void initState() { super.initState(); _tabController = TabController( length: 2, vsync: this, initialIndex: widget.initialTab == 'order' ? 1 : 0, ); _tabController.addListener(_onTabChanged); Future.microtask(() { ref.read(workOrderProvider.notifier).loadOrders(WorkOrderStatus.all); ref.read(orderProvider.notifier).loadOrders(OrderStatus.all); }); } void _onTabChanged() { if (!_tabController.indexIsChanging) { setState(() {}); } } @override void dispose() { _tabController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; return Scaffold( backgroundColor: AppColors.background, appBar: AppBar( title: Text(l10n.ordersAndWorkOrders), backgroundColor: AppColors.surface, elevation: 0, scrolledUnderElevation: 0, surfaceTintColor: Colors.transparent, bottom: PreferredSize( preferredSize: const Size.fromHeight(52), child: Container( color: AppColors.surface, padding: const EdgeInsets.fromLTRB(16, 0, 16, 12), child: Container( height: 38, decoration: BoxDecoration( color: AppColors.background, borderRadius: BorderRadius.circular(10), ), child: TabBar( controller: _tabController, tabs: [ Tab(text: l10n.workOrder), Tab(text: l10n.order), ], indicator: BoxDecoration( borderRadius: BorderRadius.circular(8), color: AppColors.surface, ), indicatorSize: TabBarIndicatorSize.tab, indicatorPadding: const EdgeInsets.all(3), labelColor: AppColors.textPrimary, unselectedLabelColor: AppColors.textSecondary, labelStyle: const TextStyle( fontSize: 14, fontWeight: FontWeight.w600, ), unselectedLabelStyle: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), dividerColor: Colors.transparent, dividerHeight: 0, splashFactory: NoSplash.splashFactory, overlayColor: WidgetStateProperty.all(Colors.transparent), ), ), ), ), ), body: TabBarView( controller: _tabController, children: [ _WorkOrderTab( status: _workStatus, onStatusChanged: (s) { setState(() => _workStatus = s); ref.read(workOrderProvider.notifier).loadOrders(s); }, ), _OrderTab( status: _orderStatus, onStatusChanged: (s) { setState(() => _orderStatus = s); ref.read(orderProvider.notifier).loadOrders(s); }, ), ], ), ); } } class _WorkOrderTab extends ConsumerWidget { final WorkOrderStatus status; final ValueChanged onStatusChanged; const _WorkOrderTab({required this.status, required this.onStatusChanged}); @override Widget build(BuildContext context, WidgetRef ref) { final l10n = AppLocalizations.of(context)!; final state = ref.watch(workOrderProvider); return Column( children: [ _buildWorkFilterChips(context, l10n), Expanded( child: state.isLoading ? SkeletonList( count: 5, itemBuilder: (_) => const WorkOrderCardSkeleton(), ) : state.orders.isEmpty ? _buildEmptyState(l10n.noWorkOrders) : ListView.builder( padding: const EdgeInsets.all(16), itemCount: state.orders.length, itemBuilder: (context, index) { return _buildWorkOrderCard(context, state.orders[index], index); }, ), ), ], ); } Widget _buildWorkFilterChips(BuildContext context, AppLocalizations l10n) { final filters = [ (WorkOrderStatus.all, l10n.all), (WorkOrderStatus.pending, l10n.pending), (WorkOrderStatus.processing, l10n.processing), (WorkOrderStatus.completed, l10n.completed), ]; return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), color: AppColors.surface, child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: filters.map((f) { final isSelected = status == f.$1; return Padding( padding: const EdgeInsets.only(right: 8), child: FilterChip( selected: isSelected, onSelected: (_) => onStatusChanged(f.$1), backgroundColor: AppColors.background, selectedColor: AppColors.primary.withOpacity(0.1), checkmarkColor: AppColors.primary, label: Text(f.$2), labelStyle: TextStyle( fontSize: 13, fontWeight: isSelected ? FontWeight.w600 : FontWeight.w400, color: isSelected ? AppColors.primary : AppColors.textSecondary, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), side: BorderSide( color: isSelected ? AppColors.primary : Colors.transparent, ), ), ), ); }).toList(), ), ), ); } Widget _buildWorkOrderCard(BuildContext context, WorkOrder order, int index) { final l10n = AppLocalizations.of(context)!; return GestureDetector( onTap: () => context.push('/work-order/${order.id}'), child: Container( margin: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.03), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text( order.title, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: AppColors.textPrimary, ), ), ), Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( color: order.statusColor.withOpacity(0.1), borderRadius: BorderRadius.circular(20), ), child: Text( order.statusText, style: TextStyle( fontSize: 11, fontWeight: FontWeight.w500, color: order.statusColor, ), ), ), ], ), const SizedBox(height: 8), Text( order.description, maxLines: 2, overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: 14, color: AppColors.textSecondary, height: 1.4, ), ), const SizedBox(height: 12), Row( children: [ Icon(Icons.person_outline, size: 14, color: AppColors.textTertiary), const SizedBox(width: 4), Text( '${l10n.creator}: ${order.creatorName}', style: TextStyle(fontSize: 12, color: AppColors.textTertiary), ), if (order.assigneeName != null) ...[ const SizedBox(width: 12), Icon(Icons.assignment_ind_outlined, size: 14, color: AppColors.textTertiary), const SizedBox(width: 4), Text( '${order.status == WorkOrderStatus.pending ? l10n.transferDept : l10n.assignee}: ${order.assigneeName}', style: TextStyle(fontSize: 12, color: AppColors.textTertiary), ), ], const Spacer(), Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( color: _getPriorityColor(order.priority).withOpacity(0.1), borderRadius: BorderRadius.circular(4), ), child: Text( _getPriorityText(context, order.priority), style: TextStyle( fontSize: 11, color: _getPriorityColor(order.priority), fontWeight: FontWeight.w500, ), ), ), ], ), ], ), ), ).animate().fadeIn(duration: 300.ms, delay: (index * 50).ms).slideY(begin: 0.1, end: 0, duration: 300.ms); } Color _getPriorityColor(String priority) { switch (priority) { case 'urgent': return AppColors.error; case 'high': return AppColors.warning; default: return AppColors.textTertiary; } } String _getPriorityText(BuildContext context, String priority) { final l10n = AppLocalizations.of(context)!; switch (priority) { case 'urgent': return l10n.urgent; case 'high': return l10n.high; default: return l10n.normal; } } Widget _buildEmptyState(String text) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.inbox_outlined, size: 64, color: AppColors.textTertiary.withOpacity(0.5)), const SizedBox(height: 16), Text(text, style: TextStyle(fontSize: 16, color: AppColors.textSecondary)), ], ), ); } } class _OrderTab extends ConsumerWidget { final OrderStatus status; final ValueChanged onStatusChanged; const _OrderTab({required this.status, required this.onStatusChanged}); @override Widget build(BuildContext context, WidgetRef ref) { final l10n = AppLocalizations.of(context)!; final state = ref.watch(orderProvider); return Column( children: [ _buildOrderFilterChips(context, l10n), Expanded( child: state.isLoading ? SkeletonList( count: 5, itemBuilder: (_) => const OrderCardSkeleton(), ) : state.orders.isEmpty ? _buildEmptyState(l10n.noOrders) : ListView.builder( padding: const EdgeInsets.all(16), itemCount: state.orders.length, itemBuilder: (context, index) { return _buildOrderCard(context, state.orders[index], index); }, ), ), ], ); } Widget _buildOrderFilterChips(BuildContext context, AppLocalizations l10n) { final filters = [ (OrderStatus.all, l10n.all), (OrderStatus.pendingPayment, l10n.pendingPayment), (OrderStatus.pendingVerification, l10n.pendingVerify), (OrderStatus.verified, l10n.verified), (OrderStatus.pendingRefund, l10n.pendingRefund), (OrderStatus.refunded, l10n.refunded), ]; return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), color: AppColors.surface, child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: filters.map((f) { final isSelected = status == f.$1; return Padding( padding: const EdgeInsets.only(right: 8), child: FilterChip( selected: isSelected, onSelected: (_) => onStatusChanged(f.$1), backgroundColor: AppColors.background, selectedColor: AppColors.primary.withOpacity(0.1), checkmarkColor: AppColors.primary, label: Text(f.$2), labelStyle: TextStyle( fontSize: 13, fontWeight: isSelected ? FontWeight.w600 : FontWeight.w400, color: isSelected ? AppColors.primary : AppColors.textSecondary, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), side: BorderSide( color: isSelected ? AppColors.primary : Colors.transparent, ), ), ), ); }).toList(), ), ), ); } Widget _buildOrderCard(BuildContext context, OrderItem order, int index) { final l10n = AppLocalizations.of(context)!; return GestureDetector( onTap: () => context.push('/order/${order.id}'), child: Container( margin: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.03), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text( order.orderNo, style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: AppColors.textPrimary, ), ), ), Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( color: order.statusColor.withOpacity(0.1), borderRadius: BorderRadius.circular(20), ), child: Text( order.statusText, style: TextStyle( fontSize: 11, fontWeight: FontWeight.w500, color: order.statusColor, ), ), ), ], ), const SizedBox(height: 12), Text( order.productName, style: TextStyle( fontSize: 15, fontWeight: FontWeight.w500, color: AppColors.textPrimary, ), ), const SizedBox(height: 8), Row( children: [ Icon(Icons.person_outline, size: 14, color: AppColors.textTertiary), const SizedBox(width: 4), Text( order.customerName, style: TextStyle(fontSize: 13, color: AppColors.textSecondary), ), const SizedBox(width: 16), Icon(Icons.confirmation_number_outlined, size: 14, color: AppColors.textTertiary), const SizedBox(width: 4), Text( 'x${order.quantity}', style: TextStyle(fontSize: 13, color: AppColors.textSecondary), ), ], ), const SizedBox(height: 12), Row( children: [ Text( '¥${order.amount.toStringAsFixed(2)}', style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: AppColors.primary, ), ), const Spacer(), if (order.verifyCode != null) Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( color: AppColors.background, borderRadius: BorderRadius.circular(6), ), child: Text( '${l10n.verifyCode}: ${order.verifyCode}', style: TextStyle( fontSize: 12, fontFamily: 'monospace', color: AppColors.textSecondary, ), ), ), ], ), ], ), ), ).animate().fadeIn(duration: 300.ms, delay: (index * 50).ms).slideY(begin: 0.1, end: 0, duration: 300.ms); } Widget _buildEmptyState(String text) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.receipt_long_outlined, size: 64, color: AppColors.textTertiary.withOpacity(0.5)), const SizedBox(height: 16), Text(text, style: TextStyle(fontSize: 16, color: AppColors.textSecondary)), ], ), ); } }