import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../../core/l10n/app_localizations.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/utils/dialog_utils.dart' show extractErrorMessage; import '../../../core/utils/top_toast.dart'; import '../../../data/repositories/broker_repository.dart'; final _rewardSetListProvider = FutureProvider.autoDispose>>((ref) { return ref.read(brokerRepositoryProvider).getRewardSetList(); }); class MyInvitationsScreen extends ConsumerWidget { const MyInvitationsScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final cs = Theme.of(context).colorScheme; final listAsync = ref.watch(_rewardSetListProvider); return Scaffold( backgroundColor: cs.surface, appBar: AppBar( backgroundColor: cs.surface, elevation: 0, leading: IconButton( icon: const Icon(Icons.arrow_back_ios, size: 18), onPressed: () => context.pop(), ), title: Text(AppLocalizations.of(context)!.myInvitations, style: TextStyle(color: cs.onSurface, fontSize: 17, fontWeight: FontWeight.w600)), centerTitle: true, ), body: listAsync.when( loading: () => const Center(child: CircularProgressIndicator()), error: (e, _) => Center(child: Text('${AppLocalizations.of(context)!.loadFailed}: $e')), data: (list) { if (list.isEmpty) { return Center(child: Text(AppLocalizations.of(context)!.noInviteRecord, style: TextStyle(color: cs.onSurface.withAlpha(120)))); } return Column( children: [ // 表头 Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), decoration: BoxDecoration( color: cs.surfaceContainerHighest.withAlpha(60), border: Border(bottom: BorderSide(color: cs.outlineVariant.withAlpha(40))), ), child: Row(children: [ Expanded(child: Text(AppLocalizations.of(context)!.accountLabel, style: TextStyle(fontSize: 12, color: cs.onSurface.withAlpha(130)))), SizedBox(width: 80, child: Text(AppLocalizations.of(context)!.levelLabel, style: TextStyle(fontSize: 12, color: cs.onSurface.withAlpha(130)))), SizedBox(width: 50, child: Text(AppLocalizations.of(context)!.perpetual, textAlign: TextAlign.center, style: TextStyle(fontSize: 12, color: cs.onSurface.withAlpha(130)))), SizedBox(width: 50, child: Text(AppLocalizations.of(context)!.copyTrading, textAlign: TextAlign.center, style: TextStyle(fontSize: 12, color: cs.onSurface.withAlpha(130)))), ]), ), Expanded( child: ListView.builder( itemCount: list.length, itemBuilder: (context, i) => _InvitationRow(item: list[i], onRefresh: () => ref.invalidate(_rewardSetListProvider)), ), ), ], ); }, ), ); } } class _InvitationRow extends StatelessWidget { const _InvitationRow({required this.item, required this.onRefresh}); final Map item; final VoidCallback onRefresh; @override Widget build(BuildContext context) { final cs = Theme.of(context).colorScheme; // id = 账号标识(安卓用 id 字段显示账户列) final accountId = item['id']?.toString() ?? '--'; final superPartner = item['superPartner']?.toString() ?? ''; final l10n = AppLocalizations.of(context)!; final levelName = superPartner == '1' ? l10n.brokerLevel : l10n.regularLevel; // rate is a plain number from API, append "%" final rateVal = item['rate']?.toString() ?? '0'; final rateStr = rateVal.contains('%') ? rateVal : '$rateVal%'; final followRate = item['followRate']; final followRateStr = (followRate != null && followRate.toString() != '0') ? '$followRate%' : '--'; return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), decoration: BoxDecoration(border: Border(bottom: BorderSide(color: cs.outlineVariant.withAlpha(30)))), child: Row(children: [ Expanded(child: Text(accountId, style: TextStyle(fontSize: 14, color: cs.onSurface, fontWeight: FontWeight.w500))), SizedBox( width: 80, child: Row(children: [ Text(levelName, style: TextStyle(fontSize: 13, color: cs.onSurface)), const SizedBox(width: 4), GestureDetector( onTap: () => _showEditDialog(context, item, onRefresh), child: Icon(Icons.edit_outlined, size: 15, color: cs.primary), ), ]), ), SizedBox(width: 50, child: Text(rateStr, textAlign: TextAlign.center, style: TextStyle(fontSize: 13, color: cs.onSurface))), SizedBox(width: 50, child: Text(followRateStr, textAlign: TextAlign.center, style: TextStyle(fontSize: 13, color: cs.onSurface))), ]), ); } void _showEditDialog(BuildContext context, Map item, VoidCallback onRefresh) { showDialog( context: context, builder: (_) => _EditRateDialog(item: item, onSuccess: onRefresh), ); } } class _EditRateDialog extends ConsumerStatefulWidget { const _EditRateDialog({required this.item, required this.onSuccess}); final Map item; final VoidCallback onSuccess; @override ConsumerState<_EditRateDialog> createState() => _EditRateDialogState(); } class _EditRateDialogState extends ConsumerState<_EditRateDialog> { late TextEditingController _rateCtrl; late TextEditingController _wcRateCtrl; bool _loading = false; @override void initState() { super.initState(); final rateRaw = widget.item['rate']?.toString() ?? ''; _rateCtrl = TextEditingController(text: rateRaw.replaceAll('%', '')); _wcRateCtrl = TextEditingController(text: widget.item['followRate']?.toString() ?? ''); } @override void dispose() { _rateCtrl.dispose(); _wcRateCtrl.dispose(); super.dispose(); } Future _submit() async { final id = widget.item['id']?.toString() ?? ''; final rate = int.tryParse(_rateCtrl.text.trim()); final wcRate = int.tryParse(_wcRateCtrl.text.trim()); if (rate == null) { showTopToast(context, message: AppLocalizations.of(context)!.enterValidPerpRate, backgroundColor: AppColors.fall); return; } setState(() => _loading = true); try { await ref.read(brokerRepositoryProvider).setReward(memberId: id, rate: rate, followRate: wcRate); if (context.mounted) { Navigator.of(context).pop(); widget.onSuccess(); showTopToast(context, message: AppLocalizations.of(context)!.setSuccess, backgroundColor: AppColors.rise); } } catch (e) { if (context.mounted) { setState(() => _loading = false); showTopToast(context, message: extractErrorMessage(e), backgroundColor: AppColors.fall); } } } @override Widget build(BuildContext context) { final cs = Theme.of(context).colorScheme; final memberId = widget.item['memberId']?.toString() ?? widget.item['id']?.toString() ?? '--'; return Dialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), clipBehavior: Clip.hardEdge, insetPadding: const EdgeInsets.symmetric(horizontal: 16), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ // ── Body ── Padding( padding: const EdgeInsets.fromLTRB(20, 20, 20, 16), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row(children: [ Text(AppLocalizations.of(context)!.editCommissionRate, style: TextStyle(fontSize: 14, color: cs.onSurface.withAlpha(140))), const Spacer(), GestureDetector( onTap: () => Navigator.of(context).pop(), child: Icon(Icons.close, size: 20, color: cs.onSurface.withAlpha(140)), ), ]), const SizedBox(height: 6), Text(memberId, style: TextStyle(fontSize: 20, fontWeight: FontWeight.w700, color: cs.onSurface)), const SizedBox(height: 20), _RateInput(controller: _rateCtrl, hint: AppLocalizations.of(context)!.perpRebateRate), const SizedBox(height: 10), _RateInput(controller: _wcRateCtrl, hint: AppLocalizations.of(context)!.copyRebateRate), const SizedBox(height: 8), Text(AppLocalizations.of(context)!.commissionRateWarning, style: TextStyle(fontSize: 11, color: cs.onSurface.withAlpha(120), height: 1.5)), ], ), ), // ── Footer ── Divider(height: 1, thickness: 1, color: cs.outlineVariant.withAlpha(60)), SizedBox( width: double.infinity, height: 54, child: TextButton( style: TextButton.styleFrom( backgroundColor: Colors.black, foregroundColor: Colors.white, shape: const RoundedRectangleBorder(), padding: EdgeInsets.zero, ), onPressed: _loading ? null : _submit, child: _loading ? const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white)) : Text(AppLocalizations.of(context)!.confirm, style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w600)), ), ), ], ), ); } } class _RateInput extends StatelessWidget { const _RateInput({required this.controller, required this.hint}); final TextEditingController controller; final String hint; @override Widget build(BuildContext context) { final cs = Theme.of(context).colorScheme; return Container( height: 56, decoration: BoxDecoration( color: cs.surfaceContainerHighest.withAlpha(50), borderRadius: BorderRadius.circular(12), ), padding: const EdgeInsets.symmetric(horizontal: 16), child: Row(children: [ Expanded( child: TextField( controller: controller, keyboardType: TextInputType.number, style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500, color: cs.onSurface), decoration: InputDecoration( hintText: hint, hintStyle: TextStyle(color: cs.onSurface.withAlpha(80), fontSize: 14, fontWeight: FontWeight.w400), border: InputBorder.none, enabledBorder: InputBorder.none, focusedBorder: InputBorder.none, isDense: true, contentPadding: EdgeInsets.zero, ), ), ), Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), decoration: BoxDecoration( color: cs.onSurface.withAlpha(25), borderRadius: BorderRadius.circular(8), ), child: Text('%', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: cs.onSurface.withAlpha(200))), ), ]), ); } }