import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:shadcn_ui/shadcn_ui.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; import 'package:provider/provider.dart'; import '../../../../core/theme/app_theme.dart'; import '../../../../core/theme/app_theme_extension.dart'; import '../../../../core/theme/app_color_scheme.dart'; import '../../../../core/theme/app_spacing.dart'; import '../../../../core/utils/toast_utils.dart'; import '../../../../providers/asset_provider.dart'; import '../../../components/glass_panel.dart'; import '../../../components/neon_glow.dart'; import '../../../shared/ui_constants.dart'; // ============================================ // Dialog helpers — shared sub-widgets // ============================================ /// 信息行 — 用於對話框中顯示 label/value 鍵值對 class InfoRow extends StatelessWidget { final String label; final String value; final bool isBold; const InfoRow({ super.key, required this.label, required this.value, this.isBold = false, }); @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( label, style: AppTextStyles.bodyMedium(context).copyWith( color: colorScheme.onSurfaceVariant, ), ), Text( value, style: AppTextStyles.bodyMedium(context).copyWith( fontWeight: isBold ? FontWeight.bold : FontWeight.w400, ), ), ], ); } } /// 錢包地址卡片 — 用於充值結果對話框中展示錢包地址 class WalletAddressCard extends StatelessWidget { final String address; final String network; const WalletAddressCard({ super.key, required this.address, required this.network, }); @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; return Container( padding: const EdgeInsets.all(AppSpacing.md), decoration: BoxDecoration( color: colorScheme.surfaceContainerHigh, borderRadius: BorderRadius.circular(AppRadius.md), border: Border.all( color: colorScheme.outlineVariant.withValues(alpha: 0.3), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text( address, style: AppTextStyles.bodyMedium(context).copyWith( fontFamily: 'monospace', ), ), ), GestureDetector( onTap: () { Clipboard.setData(ClipboardData(text: address)); ToastUtils.showSuccess('地址已複製到剪貼板'); }, child: Container( padding: const EdgeInsets.all(AppSpacing.xs), decoration: BoxDecoration( color: colorScheme.primary.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(AppRadius.sm), ), child: Icon( LucideIcons.copy, size: 16, color: colorScheme.primary, ), ), ), ], ), const SizedBox(height: AppSpacing.sm), Text( '網絡: $network', style: AppTextStyles.bodySmall(context), ), ], ), ); } } // ============================================ // Dialog functions — kept from original with style updates // ============================================ /// 充值對話框 void showDepositDialog(BuildContext context) { final amountController = TextEditingController(); final formKey = GlobalKey(); final colorScheme = Theme.of(context).colorScheme; showShadDialog( context: context, builder: (ctx) => Dialog( backgroundColor: Colors.transparent, child: GlassPanel( borderRadius: BorderRadius.circular(AppRadius.lg), padding: const EdgeInsets.all(AppSpacing.lg), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '充值', style: AppTextStyles.headlineLarge(context).copyWith( fontWeight: FontWeight.w700, ), ), const SizedBox(height: AppSpacing.xs), Text( 'Asset: USDT', style: AppTextStyles.bodyMedium(context).copyWith( color: colorScheme.onSurfaceVariant, ), ), ], ), Container( padding: const EdgeInsets.all(AppSpacing.sm), decoration: BoxDecoration( color: colorScheme.surfaceContainerHigh, borderRadius: BorderRadius.circular(AppRadius.md), ), child: Icon( LucideIcons.wallet, color: colorScheme.secondary, ), ), ], ), const SizedBox(height: AppSpacing.lg), ShadForm( key: formKey, child: ShadInputFormField( id: 'amount', controller: amountController, label: const Text('充值金額'), placeholder: const Text('最低 1000 USDT'), keyboardType: const TextInputType.numberWithOptions(decimal: true), validator: (v) { if (v == null || v.isEmpty) return '請輸入金額'; final n = double.tryParse(v); if (n == null || n <= 0) return '請輸入有效金額'; if (n < 1000) return '單筆最低充值1000 USDT'; return null; }, ), ), const SizedBox(height: AppSpacing.lg), Row( children: [ Expanded( child: NeonButton( text: '取消', type: NeonButtonType.outline, onPressed: () => Navigator.of(ctx).pop(), height: 48, showGlow: false, ), ), const SizedBox(width: AppSpacing.sm), Expanded( child: NeonButton( text: '下一步', type: NeonButtonType.primary, onPressed: () async { if (formKey.currentState!.saveAndValidate()) { Navigator.of(ctx).pop(); final response = await context.read().deposit( amount: amountController.text, ); if (context.mounted) { if (response.success && response.data != null) { showDepositResultDialog(context, response.data!); } else { showResultDialog(context, '申請失敗', response.message); } } } }, height: 48, showGlow: true, ), ), ], ), ], ), ), ), ); } /// 充值結果對話框 — 展示錢包地址和確認打款 void showDepositResultDialog(BuildContext context, Map data) { final orderNo = data['orderNo'] as String? ?? ''; final amount = data['amount']?.toString() ?? '0.00'; final walletAddress = data['walletAddress'] as String? ?? ''; final walletNetwork = data['walletNetwork'] as String? ?? 'TRC20'; final colorScheme = Theme.of(context).colorScheme; showShadDialog( context: context, builder: (ctx) => Dialog( backgroundColor: Colors.transparent, child: GlassPanel( borderRadius: BorderRadius.circular(AppRadius.lg), padding: const EdgeInsets.all(AppSpacing.lg), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ NeonIcon( icon: Icons.check_circle, color: context.appColors.up, size: 24, ), const SizedBox(width: AppSpacing.sm), Text( '充值申請成功', style: AppTextStyles.headlineLarge(context).copyWith( fontWeight: FontWeight.w700, ), ), ], ), const SizedBox(height: AppSpacing.lg), InfoRow(label: '訂單號', value: orderNo), const SizedBox(height: AppSpacing.sm), InfoRow(label: '充值金額', value: '$amount USDT', isBold: true), const SizedBox(height: AppSpacing.lg), Text( '請向以下地址轉賬:', style: AppTextStyles.bodyMedium(context).copyWith( color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: AppSpacing.sm), WalletAddressCard(address: walletAddress, network: walletNetwork), const SizedBox(height: AppSpacing.md), Container( padding: const EdgeInsets.all(AppSpacing.sm), decoration: BoxDecoration( color: AppColorScheme.warning.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(AppRadius.md), border: Border.all( color: AppColorScheme.warning.withValues(alpha: 0.2), ), ), child: Row( children: [ Icon(Icons.info_outline, size: 16, color: AppColorScheme.warning), const SizedBox(width: AppSpacing.sm), Expanded( child: Text( '轉賬完成後請點擊"已打款"按鈕確認', style: AppTextStyles.bodyMedium(context).copyWith( color: AppColorScheme.warning, ), ), ), ], ), ), const SizedBox(height: AppSpacing.lg), Row( children: [ Expanded( child: NeonButton( text: '稍後確認', type: NeonButtonType.outline, onPressed: () => Navigator.of(ctx).pop(), height: 44, showGlow: false, ), ), const SizedBox(width: AppSpacing.sm), Expanded( child: NeonButton( text: '已打款', type: NeonButtonType.primary, onPressed: () async { Navigator.of(ctx).pop(); final response = await context.read().confirmPay(orderNo); if (context.mounted) { showResultDialog( context, response.success ? '確認成功' : '確認失敗', response.success ? '請等待管理員審核' : response.message, ); } }, height: 44, showGlow: true, ), ), ], ), ], ), ), ), ); } /// 提現對話框 void showWithdrawDialog(BuildContext context, String? balance) { final amountController = TextEditingController(); final addressController = TextEditingController(); final contactController = TextEditingController(); final formKey = GlobalKey(); final feeNotifier = ValueNotifier('提現將扣除10%手續費'); final networksNotifier = ValueNotifier>([]); final selectedNetworkNotifier = ValueNotifier(null); final colorScheme = Theme.of(context).colorScheme; amountController.addListener(() { final amount = double.tryParse(amountController.text) ?? 0; if (amount > 0) { final fee = amount * 0.1; final receivable = amount - fee; feeNotifier.value = '手續費(10%): -${fee.toStringAsFixed(2)} USDT | 應收款: ${receivable.toStringAsFixed(2)} USDT'; } else { feeNotifier.value = '提現將扣除10%手續費'; } }); // 獲取網絡列表 context.read().getWalletNetworks().then((list) { networksNotifier.value = list; if (list.isNotEmpty) { selectedNetworkNotifier.value = list.first; } }); showShadDialog( context: context, builder: (ctx) => Dialog( backgroundColor: Colors.transparent, child: GlassPanel( borderRadius: BorderRadius.circular(AppRadius.lg), padding: const EdgeInsets.all(AppSpacing.lg), child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( padding: const EdgeInsets.all(AppSpacing.sm), decoration: BoxDecoration( color: colorScheme.primary.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(AppRadius.md), ), child: Icon( LucideIcons.wallet, color: colorScheme.primary, ), ), const SizedBox(width: AppSpacing.sm), Text( '提現', style: AppTextStyles.headlineLarge(context).copyWith( fontWeight: FontWeight.w700, ), ), ], ), const SizedBox(height: AppSpacing.xs), Text( '安全地將您的資產轉移到外部錢包地址', style: AppTextStyles.bodyMedium(context).copyWith( color: colorScheme.onSurfaceVariant, ), ), if (balance != null) ...[ const SizedBox(height: AppSpacing.md), Container( padding: const EdgeInsets.symmetric( horizontal: AppSpacing.md, vertical: AppSpacing.sm, ), decoration: BoxDecoration( color: context.appColors.upBackground, borderRadius: BorderRadius.circular(AppRadius.full), border: Border.all( color: context.appColors.up.withValues(alpha: 0.2), ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Text( '可用餘額: ', style: AppTextStyles.bodySmall(context).copyWith( color: colorScheme.onSurfaceVariant, ), ), Text( '$balance USDT', style: AppTextStyles.labelLarge(context).copyWith( fontWeight: FontWeight.bold, color: context.appColors.up, ), ), ], ), ), ], const SizedBox(height: AppSpacing.lg), ShadForm( key: formKey, child: Column( children: [ ShadInputFormField( id: 'amount', controller: amountController, label: const Text('提現金額'), placeholder: const Text('請輸入提現金額(USDT)'), keyboardType: const TextInputType.numberWithOptions(decimal: true), validator: Validators.amount, ), const SizedBox(height: AppSpacing.md), // 手續費/應收款提示 ValueListenableBuilder( valueListenable: feeNotifier, builder: (_, feeText, __) { return Container( padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md, vertical: AppSpacing.sm), decoration: BoxDecoration( color: colorScheme.surfaceContainerHigh.withValues(alpha: 0.5), borderRadius: BorderRadius.circular(AppRadius.md), ), child: Row( children: [ Icon(Icons.info_outline, size: 14, color: colorScheme.onSurfaceVariant), const SizedBox(width: AppSpacing.xs), Expanded( child: Text( feeText, style: AppTextStyles.bodySmall(context).copyWith( color: colorScheme.onSurfaceVariant, ), ), ), ], ), ); }, ), const SizedBox(height: AppSpacing.md), // 提現網絡選擇 ValueListenableBuilder>( valueListenable: networksNotifier, builder: (_, networks, __) { if (networks.isEmpty) return const SizedBox.shrink(); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('提現網絡', style: AppTextStyles.bodyMedium(context).copyWith( fontWeight: FontWeight.w500, )), const SizedBox(height: AppSpacing.xs), ValueListenableBuilder( valueListenable: selectedNetworkNotifier, builder: (_, selected, __) { return SizedBox( width: double.infinity, child: ShadSelect( placeholder: const Text('選擇提現網絡'), initialValue: selected, selectedOptionBuilder: (context, val) => Text(val), onChanged: (value) { if (value != null) selectedNetworkNotifier.value = value; }, options: networks.map((n) => ShadOption(value: n, child: Text(n))).toList(), ), ); }, ), ], ); }, ), const SizedBox(height: AppSpacing.md), ShadInputFormField( id: 'address', controller: addressController, label: const Text('目標地址'), placeholder: const Text('請輸入提現地址'), validator: (v) => Validators.required(v, '提現地址'), ), const SizedBox(height: AppSpacing.md), ShadInputFormField( id: 'contact', controller: contactController, label: const Text('聯繫方式(可選)'), placeholder: const Text('聯繫方式'), ), ], ), ), const SizedBox(height: AppSpacing.lg), Row( children: [ Expanded( child: NeonButton( text: '取消', type: NeonButtonType.outline, onPressed: () => Navigator.of(ctx).pop(), height: 44, showGlow: false, ), ), const SizedBox(width: AppSpacing.sm), Expanded( child: NeonButton( text: '提交', type: NeonButtonType.primary, onPressed: () async { if (formKey.currentState!.saveAndValidate()) { Navigator.of(ctx).pop(); final response = await context.read().withdraw( amount: amountController.text, withdrawAddress: addressController.text, withdrawContact: contactController.text.isNotEmpty ? contactController.text : null, network: selectedNetworkNotifier.value, ); if (context.mounted) { showResultDialog( context, response.success ? '申請成功' : '申請失敗', response.success ? '請等待管理員審批' : response.message, ); } } }, height: 44, showGlow: true, ), ), ], ), const SizedBox(height: AppSpacing.md), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.verified_user, size: 12, color: colorScheme.onSurfaceVariant.withValues(alpha: 0.5), ), const SizedBox(width: AppSpacing.xs), ], ), ], ), ), ), ), ); } /// 通用結果對話框 — 展示操作成功/失敗信息 void showResultDialog(BuildContext context, String title, String? message) { final colorScheme = Theme.of(context).colorScheme; showShadDialog( context: context, builder: (ctx) => Dialog( backgroundColor: Colors.transparent, child: GlassPanel( borderRadius: BorderRadius.circular(AppRadius.lg), padding: const EdgeInsets.all(AppSpacing.lg), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( title, style: AppTextStyles.headlineLarge(context).copyWith( fontWeight: FontWeight.w700, ), ), if (message != null) ...[ const SizedBox(height: AppSpacing.sm), Text( message, style: AppTextStyles.bodyLarge(context).copyWith( color: colorScheme.onSurfaceVariant, ), textAlign: TextAlign.center, ), ], const SizedBox(height: AppSpacing.lg), SizedBox( width: double.infinity, child: NeonButton( text: '確定', type: NeonButtonType.primary, onPressed: () => Navigator.of(ctx).pop(), height: 44, showGlow: false, ), ), ], ), ), ), ); }