import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:it0_app/l10n/app_localizations.dart'; import '../../../../core/theme/app_colors.dart'; import '../providers/referral_providers.dart'; import '../../domain/models/referral_info.dart'; class ReferralScreen extends ConsumerWidget { const ReferralScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final l10n = AppLocalizations.of(context); final isDark = Theme.of(context).brightness == Brightness.dark; final cardColor = isDark ? AppColors.surface : Colors.white; return DefaultTabController( length: 2, child: Scaffold( backgroundColor: AppColors.background, appBar: AppBar( backgroundColor: AppColors.background, title: Text(l10n.referralScreenTitle), actions: [ IconButton( icon: const Icon(Icons.refresh), onPressed: () { ref.invalidate(referralInfoProvider); ref.invalidate(referralListProvider); ref.invalidate(pendingRewardsProvider); ref.invalidate(userReferralInfoProvider); ref.invalidate(myCircleProvider); ref.invalidate(myPointsProvider); }, ), ], bottom: TabBar( indicatorColor: AppColors.primary, labelColor: AppColors.primary, unselectedLabelColor: Colors.grey, tabs: [ Tab(text: l10n.referralTabTenant), Tab(text: l10n.referralTabPersonal), ], ), ), body: TabBarView( children: [ _TenantReferralTab(cardColor: cardColor), _PersonalCircleTab(cardColor: cardColor), ], ), ), ); } } // ══════════════════════════════════════════════════════════════════════════════ // Tab 1 — Tenant / B2B referral (existing) // ══════════════════════════════════════════════════════════════════════════════ class _TenantReferralTab extends ConsumerWidget { final Color cardColor; const _TenantReferralTab({required this.cardColor}); @override Widget build(BuildContext context, WidgetRef ref) { final l10n = AppLocalizations.of(context); final infoAsync = ref.watch(referralInfoProvider); return infoAsync.when( loading: () => const Center(child: CircularProgressIndicator()), error: (e, _) => Center(child: Text('${l10n.loadFailed}: $e')), data: (info) => ListView( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), children: [ _ReferralCodeCard(info: info, cardColor: cardColor), const SizedBox(height: 16), _StatsRow(info: info, cardColor: cardColor), const SizedBox(height: 20), _RewardRulesCard(cardColor: cardColor), const SizedBox(height: 20), _SectionHeader( title: l10n.referralRecordsSection, onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const _ReferralListPage())), ), _ReferralPreviewList(cardColor: cardColor), const SizedBox(height: 20), _SectionHeader( title: l10n.pendingRewardsSection, onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const _RewardListPage())), ), _RewardPreviewList(cardColor: cardColor), const SizedBox(height: 40), ], ), ); } } // ══════════════════════════════════════════════════════════════════════════════ // Tab 2 — Personal circle / C2C // ══════════════════════════════════════════════════════════════════════════════ class _PersonalCircleTab extends ConsumerWidget { final Color cardColor; const _PersonalCircleTab({required this.cardColor}); @override Widget build(BuildContext context, WidgetRef ref) { final l10n = AppLocalizations.of(context); final infoAsync = ref.watch(userReferralInfoProvider); return infoAsync.when( loading: () => const Center(child: CircularProgressIndicator()), error: (e, _) => Center(child: Text('${l10n.loadFailed}: $e')), data: (info) => ListView( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), children: [ _UserCodeCard(info: info, cardColor: cardColor), const SizedBox(height: 16), _PointsBalanceCard(info: info, cardColor: cardColor), const SizedBox(height: 20), _CircleRulesCard(cardColor: cardColor), const SizedBox(height: 20), _SectionHeader( title: l10n.myCircleMembersSection, onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const _CircleListPage())), ), _CirclePreviewList(cardColor: cardColor), const SizedBox(height: 20), _SectionHeader( title: l10n.pointsHistorySection, onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const _PointsHistoryPage())), ), _PointsPreviewList(cardColor: cardColor), const SizedBox(height: 40), ], ), ); } } // ── User Referral Code Card ─────────────────────────────────────────────────── class _UserCodeCard extends StatelessWidget { final UserReferralInfo info; final Color cardColor; const _UserCodeCard({required this.info, required this.cardColor}); @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); return Card( color: cardColor, elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), child: Padding( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( l10n.myPersonalInviteCode, style: const TextStyle(color: Colors.grey, fontSize: 13), ), const SizedBox(height: 8), Row( children: [ Expanded( child: Text( info.code, style: const TextStyle( fontSize: 26, fontWeight: FontWeight.bold, letterSpacing: 2, color: Color(0xFF7C3AED), ), ), ), IconButton( icon: const Icon(Icons.copy, color: Color(0xFF7C3AED)), tooltip: l10n.copyReferralCodeTooltip, onPressed: () => _copy(context, info.code), ), ], ), const SizedBox(height: 12), Row( children: [ Expanded( child: OutlinedButton.icon( icon: const Icon(Icons.link, size: 18), label: Text(l10n.copyInviteLinkButton), onPressed: () => _copy(context, info.shareUrl), style: OutlinedButton.styleFrom( foregroundColor: const Color(0xFF7C3AED), side: const BorderSide(color: Color(0xFF7C3AED)), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10)), ), ), ), const SizedBox(width: 8), Expanded( child: FilledButton.icon( icon: const Icon(Icons.share, size: 18), label: Text(l10n.shareButton), onPressed: () => _share(context, info), style: FilledButton.styleFrom( backgroundColor: const Color(0xFF7C3AED), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10)), ), ), ), ], ), ], ), ), ); } void _copy(BuildContext context, String text) { final l10n = AppLocalizations.of(context); Clipboard.setData(ClipboardData(text: text)); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(l10n.copiedToClipboard), duration: const Duration(seconds: 2)), ); } void _share(BuildContext context, UserReferralInfo info) { final text = '邀请你加入 IT0 智能体管理平台,用AI管理你的数字工作!加入即获 200 积分奖励!\n邀请码:${info.code}\n链接:${info.shareUrl}'; _copy(context, text); } } // ── Points Balance Card ─────────────────────────────────────────────────────── class _PointsBalanceCard extends StatelessWidget { final UserReferralInfo info; final Color cardColor; const _PointsBalanceCard({required this.info, required this.cardColor}); @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); return Card( color: cardColor, elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), child: Padding( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon(Icons.stars_rounded, color: Color(0xFFF59E0B), size: 20), const SizedBox(width: 6), Text(l10n.pointsBalanceTitle, style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 15)), ], ), const SizedBox(height: 16), Row( children: [ _PointsStatItem( label: l10n.currentBalanceLabel, value: '${info.pointsBalance}', unit: 'pts', color: const Color(0xFF7C3AED), ), const SizedBox(width: 12), _PointsStatItem( label: l10n.circleMembersCountLabel, value: '${info.circleSize}', unit: '人', color: const Color(0xFF10B981), ), const SizedBox(width: 12), _PointsStatItem( label: l10n.totalEarnedLabel, value: '${info.totalEarned}', unit: 'pts', color: const Color(0xFF6366F1), ), ], ), ], ), ), ); } } class _PointsStatItem extends StatelessWidget { final String label; final String value; final String unit; final Color color; const _PointsStatItem({ required this.label, required this.value, required this.unit, required this.color, }); @override Widget build(BuildContext context) { return Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: const TextStyle(fontSize: 12, color: Colors.grey)), const SizedBox(height: 4), RichText( text: TextSpan( children: [ TextSpan( text: value, style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: color, ), ), TextSpan( text: ' $unit', style: TextStyle(fontSize: 12, color: color.withAlpha(180)), ), ], ), ), ], ), ); } } // ── Circle Rules Card ───────────────────────────────────────────────────────── class _CircleRulesCard extends StatelessWidget { final Color cardColor; const _CircleRulesCard({required this.cardColor}); @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); return Card( color: cardColor, elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon(Icons.people_alt_rounded, color: Color(0xFF7C3AED), size: 20), const SizedBox(width: 6), Text(l10n.circleRewardRulesTitle, style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 15)), ], ), const SizedBox(height: 12), _RuleItem( icon: Icons.card_giftcard_rounded, color: const Color(0xFF7C3AED), text: l10n.circleRule1, ), const SizedBox(height: 8), _RuleItem( icon: Icons.star_rounded, color: const Color(0xFF6366F1), text: l10n.circleRule2, ), const SizedBox(height: 8), _RuleItem( icon: Icons.star_rounded, color: const Color(0xFF7C3AED), text: l10n.circleRule3, ), const SizedBox(height: 8), _RuleItem( icon: Icons.repeat_rounded, color: const Color(0xFF10B981), text: l10n.circleRule4, ), const SizedBox(height: 8), _RuleItem( icon: Icons.account_tree_rounded, color: const Color(0xFFF59E0B), text: l10n.circleRule5, ), const SizedBox(height: 8), _RuleItem( icon: Icons.redeem_rounded, color: const Color(0xFF10B981), text: l10n.circleRule6, ), ], ), ), ); } } // ── Circle Member Preview ───────────────────────────────────────────────────── class _CirclePreviewList extends ConsumerWidget { final Color cardColor; const _CirclePreviewList({required this.cardColor}); @override Widget build(BuildContext context, WidgetRef ref) { final l10n = AppLocalizations.of(context); final async = ref.watch(myCircleProvider); return async.when( loading: () => const SizedBox( height: 60, child: Center(child: CircularProgressIndicator())), error: (_, __) => const SizedBox.shrink(), data: (result) { if (result.items.isEmpty) { return _EmptyCard(cardColor: cardColor, message: l10n.noCircleMembersMessage); } final preview = result.items.take(3).toList(); return Card( color: cardColor, elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), clipBehavior: Clip.antiAlias, child: Column( children: preview.map((m) => _CircleMemberTile(member: m)).toList(), ), ); }, ); } } class _CircleMemberTile extends StatelessWidget { final CircleMember member; const _CircleMemberTile({required this.member}); @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); final statusColor = member.isActive ? const Color(0xFF10B981) : member.status == 'EXPIRED' ? Colors.grey : const Color(0xFFF59E0B); final statusLabel = switch (member.status) { 'PENDING' => l10n.pendingPaymentStatus, 'ACTIVE' => l10n.activatedStatus, 'REWARDED' => l10n.rewardedStatus, 'EXPIRED' => l10n.expiredStatus, _ => member.status, }; final levelLabel = member.level == 1 ? 'L1' : 'L2'; return ListTile( leading: CircleAvatar( backgroundColor: const Color(0xFF7C3AED).withAlpha(20), child: Text( levelLabel, style: const TextStyle( color: Color(0xFF7C3AED), fontWeight: FontWeight.bold, fontSize: 12), ), ), title: Text( member.referredUserId.length > 8 ? '${member.referredUserId.substring(0, 8)}...' : member.referredUserId, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500), ), subtitle: Text( '${l10n.joinedAtLabel} ${_formatDate(member.joinedAt)}', style: const TextStyle(fontSize: 12), ), trailing: Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: statusColor.withAlpha(20), borderRadius: BorderRadius.circular(20), ), child: Text( statusLabel, style: TextStyle( fontSize: 12, color: statusColor, fontWeight: FontWeight.w500), ), ), ); } String _formatDate(DateTime dt) => '${dt.year}-${dt.month.toString().padLeft(2, '0')}-${dt.day.toString().padLeft(2, '0')}'; } // ── Points Preview List ─────────────────────────────────────────────────────── class _PointsPreviewList extends ConsumerWidget { final Color cardColor; const _PointsPreviewList({required this.cardColor}); @override Widget build(BuildContext context, WidgetRef ref) { final l10n = AppLocalizations.of(context); final async = ref.watch(myPointsProvider); return async.when( loading: () => const SizedBox( height: 60, child: Center(child: CircularProgressIndicator())), error: (_, __) => const SizedBox.shrink(), data: (result) { if (result.transactions.isEmpty) { return _EmptyCard(cardColor: cardColor, message: l10n.noPointsHistoryMessage); } final preview = result.transactions.take(3).toList(); return Card( color: cardColor, elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), clipBehavior: Clip.antiAlias, child: Column( children: preview.map((t) => _PointsTile(tx: t)).toList(), ), ); }, ); } } class _PointsTile extends StatelessWidget { final PointTransaction tx; const _PointsTile({required this.tx}); @override Widget build(BuildContext context) { final color = tx.isEarned ? const Color(0xFF10B981) : const Color(0xFFEF4444); final sign = tx.isEarned ? '+' : ''; return ListTile( leading: CircleAvatar( backgroundColor: color.withAlpha(20), child: Icon( tx.isEarned ? Icons.add_circle_outline : Icons.remove_circle_outline, color: color, size: 20, ), ), title: Text(tx.typeLabel, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500)), subtitle: Text( _formatDate(tx.createdAt), style: const TextStyle(fontSize: 12, color: Colors.grey), ), trailing: Text( '$sign${tx.delta} pts', style: TextStyle( fontSize: 15, fontWeight: FontWeight.bold, color: color), ), ); } String _formatDate(DateTime dt) => '${dt.year}-${dt.month.toString().padLeft(2, '0')}-${dt.day.toString().padLeft(2, '0')}'; } // ── Full list pages (circle & points) ──────────────────────────────────────── class _CircleListPage extends ConsumerWidget { const _CircleListPage(); @override Widget build(BuildContext context, WidgetRef ref) { final l10n = AppLocalizations.of(context); final async = ref.watch(myCircleProvider); return Scaffold( appBar: AppBar(title: Text(l10n.referralTabPersonal)), body: async.when( loading: () => const Center(child: CircularProgressIndicator()), error: (e, _) => Center(child: Text('${l10n.loadFailed}: $e')), data: (result) => result.items.isEmpty ? Center(child: Text(l10n.noCircleMembersMessage)) : ListView.separated( itemCount: result.items.length, separatorBuilder: (_, __) => const Divider(height: 1), itemBuilder: (_, i) => _CircleMemberTile(member: result.items[i]), ), ), ); } } class _PointsHistoryPage extends ConsumerWidget { const _PointsHistoryPage(); @override Widget build(BuildContext context, WidgetRef ref) { final l10n = AppLocalizations.of(context); final async = ref.watch(myPointsProvider); return Scaffold( appBar: AppBar(title: Text(l10n.pointsHistorySection)), body: async.when( loading: () => const Center(child: CircularProgressIndicator()), error: (e, _) => Center(child: Text('${l10n.loadFailed}: $e')), data: (result) => result.transactions.isEmpty ? Center(child: Text(l10n.noPointsHistoryMessage)) : ListView.separated( itemCount: result.transactions.length, separatorBuilder: (_, __) => const Divider(height: 1), itemBuilder: (_, i) => _PointsTile(tx: result.transactions[i]), ), ), ); } } // ══════════════════════════════════════════════════════════════════════════════ // Shared / Tenant tab widgets (unchanged logic, extracted) // ══════════════════════════════════════════════════════════════════════════════ class _ReferralCodeCard extends StatelessWidget { final ReferralInfo info; final Color cardColor; const _ReferralCodeCard({required this.info, required this.cardColor}); @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); return Card( color: cardColor, elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), child: Padding( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( l10n.yourReferralCodeLabel, style: const TextStyle(color: Colors.grey, fontSize: 13), ), const SizedBox(height: 8), Row( children: [ Expanded( child: Text( info.referralCode, style: const TextStyle( fontSize: 28, fontWeight: FontWeight.bold, letterSpacing: 2, color: AppColors.primary, ), ), ), IconButton( icon: const Icon(Icons.copy, color: AppColors.primary), tooltip: l10n.copyReferralCodeTooltip, onPressed: () => _copy(context, info.referralCode), ), ], ), const SizedBox(height: 12), Row( children: [ Expanded( child: OutlinedButton.icon( icon: const Icon(Icons.link, size: 18), label: Text(l10n.copyInviteLinkButton), onPressed: () => _copy(context, info.shareUrl), style: OutlinedButton.styleFrom( foregroundColor: AppColors.primary, side: const BorderSide(color: AppColors.primary), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10)), ), ), ), const SizedBox(width: 8), Expanded( child: FilledButton.icon( icon: const Icon(Icons.share, size: 18), label: Text(l10n.shareButton), onPressed: () => _share(context, info), style: FilledButton.styleFrom( backgroundColor: AppColors.primary, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10)), ), ), ), ], ), ], ), ), ); } void _copy(BuildContext context, String text) { final l10n = AppLocalizations.of(context); Clipboard.setData(ClipboardData(text: text)); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(l10n.copiedToClipboard), duration: const Duration(seconds: 2)), ); } void _share(BuildContext context, ReferralInfo info) { final text = '邀请你使用 IT0 智能体管理平台,注册即可获得积分奖励!\n推荐码:${info.referralCode}\n链接:${info.shareUrl}'; _copy(context, text); } } class _StatsRow extends StatelessWidget { final ReferralInfo info; final Color cardColor; const _StatsRow({required this.info, required this.cardColor}); @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); return Row( children: [ _StatCard( cardColor: cardColor, label: l10n.referredLabel, value: '${info.directCount}', unit: l10n.peopleUnit, color: const Color(0xFF6366F1), ), const SizedBox(width: 10), _StatCard( cardColor: cardColor, label: l10n.activatedLabel, value: '${info.activeCount}', unit: l10n.peopleUnit, color: const Color(0xFF10B981), ), const SizedBox(width: 10), _StatCard( cardColor: cardColor, label: l10n.pendingCreditsLabel, value: info.pendingCreditFormatted, unit: '', color: const Color(0xFFF59E0B), ), ], ); } } class _StatCard extends StatelessWidget { final Color cardColor; final String label; final String value; final String unit; final Color color; const _StatCard({ required this.cardColor, required this.label, required this.value, required this.unit, required this.color, }); @override Widget build(BuildContext context) { return Expanded( child: Card( color: cardColor, elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: Padding( padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: const TextStyle(fontSize: 12, color: Colors.grey)), const SizedBox(height: 4), RichText( text: TextSpan( children: [ TextSpan( text: value, style: TextStyle( fontSize: 22, fontWeight: FontWeight.bold, color: color, ), ), if (unit.isNotEmpty) TextSpan( text: unit, style: TextStyle(fontSize: 13, color: color.withAlpha(180)), ), ], ), ), ], ), ), ), ); } } class _RewardRulesCard extends StatelessWidget { final Color cardColor; const _RewardRulesCard({required this.cardColor}); @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); return Card( color: cardColor, elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon(Icons.card_giftcard, color: Color(0xFFF59E0B), size: 20), const SizedBox(width: 6), Text( l10n.rewardRulesTitle, style: const TextStyle( fontWeight: FontWeight.w600, fontSize: 15), ), ], ), const SizedBox(height: 12), _RuleItem( icon: Icons.star_rounded, color: const Color(0xFF6366F1), text: l10n.proReferralReward, ), const SizedBox(height: 8), _RuleItem( icon: Icons.star_rounded, color: const Color(0xFF7C3AED), text: l10n.enterpriseReferralReward, ), const SizedBox(height: 8), _RuleItem( icon: Icons.repeat_rounded, color: const Color(0xFF10B981), text: l10n.renewalBonusReward, ), const SizedBox(height: 8), _RuleItem( icon: Icons.account_balance_wallet_outlined, color: const Color(0xFFF59E0B), text: l10n.creditDeductionReward, ), ], ), ), ); } } class _RuleItem extends StatelessWidget { final IconData icon; final Color color; final String text; const _RuleItem({required this.icon, required this.color, required this.text}); @override Widget build(BuildContext context) { return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(icon, color: color, size: 16), const SizedBox(width: 8), Expanded( child: Text(text, style: const TextStyle(fontSize: 13, height: 1.4)), ), ], ); } } class _SectionHeader extends StatelessWidget { final String title; final VoidCallback onTap; const _SectionHeader({required this.title, required this.onTap}); @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(title, style: const TextStyle( fontWeight: FontWeight.w600, fontSize: 16)), TextButton(onPressed: onTap, child: Text(l10n.viewAllButton)), ], ); } } class _EmptyCard extends StatelessWidget { final Color cardColor; final String message; const _EmptyCard({required this.cardColor, required this.message}); @override Widget build(BuildContext context) { return Card( color: cardColor, elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: Padding( padding: const EdgeInsets.all(20), child: Center( child: Text(message, style: const TextStyle(color: Colors.grey, fontSize: 13)), ), ), ); } } class _ReferralPreviewList extends ConsumerWidget { final Color cardColor; const _ReferralPreviewList({required this.cardColor}); @override Widget build(BuildContext context, WidgetRef ref) { final l10n = AppLocalizations.of(context); final async = ref.watch(referralListProvider); return async.when( loading: () => const SizedBox( height: 60, child: Center(child: CircularProgressIndicator())), error: (_, __) => const SizedBox.shrink(), data: (result) { if (result.items.isEmpty) { return _EmptyCard( cardColor: cardColor, message: l10n.noReferralsMessage); } final preview = result.items.take(3).toList(); return Card( color: cardColor, elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), clipBehavior: Clip.antiAlias, child: Column( children: preview.map((item) => _ReferralTile(item: item)).toList(), ), ); }, ); } } class _ReferralTile extends StatelessWidget { final ReferralItem item; const _ReferralTile({required this.item}); @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); final statusColor = item.isActive ? const Color(0xFF10B981) : item.status == 'EXPIRED' ? Colors.grey : const Color(0xFFF59E0B); final statusLabel = switch (item.status) { 'PENDING' => l10n.pendingPaymentStatus, 'ACTIVE' => l10n.activeStatus, 'REWARDED' => l10n.rewardedStatus, 'EXPIRED' => l10n.expiredStatus, _ => item.status, }; return ListTile( leading: CircleAvatar( backgroundColor: statusColor.withAlpha(30), child: Icon( item.isActive ? Icons.check_circle : Icons.pending, color: statusColor, size: 20, ), ), title: Text( item.referredTenantId.length > 8 ? '${item.referredTenantId.substring(0, 8)}...' : item.referredTenantId, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500), ), subtitle: Text( '${l10n.registeredAt} ${_formatDate(item.registeredAt)}', style: const TextStyle(fontSize: 12), ), trailing: Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: statusColor.withAlpha(20), borderRadius: BorderRadius.circular(20), ), child: Text( statusLabel, style: TextStyle( fontSize: 12, color: statusColor, fontWeight: FontWeight.w500), ), ), ); } String _formatDate(DateTime dt) => '${dt.year}-${dt.month.toString().padLeft(2, '0')}-${dt.day.toString().padLeft(2, '0')}'; } class _RewardPreviewList extends ConsumerWidget { final Color cardColor; const _RewardPreviewList({required this.cardColor}); @override Widget build(BuildContext context, WidgetRef ref) { final l10n = AppLocalizations.of(context); final async = ref.watch(pendingRewardsProvider); return async.when( loading: () => const SizedBox( height: 60, child: Center(child: CircularProgressIndicator())), error: (_, __) => const SizedBox.shrink(), data: (result) { if (result.items.isEmpty) { return _EmptyCard( cardColor: cardColor, message: l10n.noPendingRewardsMessage); } final preview = result.items.take(3).toList(); return Card( color: cardColor, elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), clipBehavior: Clip.antiAlias, child: Column( children: preview.map((item) => _RewardTile(item: item)).toList(), ), ); }, ); } } class _RewardTile extends StatelessWidget { final RewardItem item; const _RewardTile({required this.item}); @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); return ListTile( leading: CircleAvatar( backgroundColor: const Color(0xFFF59E0B).withAlpha(30), child: const Icon(Icons.attach_money, color: Color(0xFFF59E0B), size: 20), ), title: Text( item.amountFormatted, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Color(0xFF10B981)), ), subtitle: Text(item.triggerLabel, style: const TextStyle(fontSize: 12)), trailing: Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: const Color(0xFFF59E0B).withAlpha(20), borderRadius: BorderRadius.circular(20), ), child: Text( l10n.pendingDeductionStatus, style: const TextStyle( fontSize: 12, color: Color(0xFFF59E0B), fontWeight: FontWeight.w500), ), ), ); } } class _ReferralListPage extends ConsumerWidget { const _ReferralListPage(); @override Widget build(BuildContext context, WidgetRef ref) { final l10n = AppLocalizations.of(context); final async = ref.watch(referralListProvider); return Scaffold( appBar: AppBar(title: Text(l10n.referralRecordsSection)), body: async.when( loading: () => const Center(child: CircularProgressIndicator()), error: (e, _) => Center(child: Text('${l10n.loadFailed}: $e')), data: (result) => result.items.isEmpty ? Center(child: Text(l10n.noReferralsMessage)) : ListView.separated( itemCount: result.items.length, separatorBuilder: (_, __) => const Divider(height: 1), itemBuilder: (_, i) => _ReferralTile(item: result.items[i]), ), ), ); } } class _RewardListPage extends ConsumerWidget { const _RewardListPage(); @override Widget build(BuildContext context, WidgetRef ref) { final l10n = AppLocalizations.of(context); final async = ref.watch(allRewardsProvider); return Scaffold( appBar: AppBar(title: Text(l10n.rewardHistoryPageTitle)), body: async.when( loading: () => const Center(child: CircularProgressIndicator()), error: (e, _) => Center(child: Text('${l10n.loadFailed}: $e')), data: (result) => result.items.isEmpty ? Center(child: Text(l10n.noRewardsHistoryMessage)) : ListView.separated( itemCount: result.items.length, separatorBuilder: (_, __) => const Divider(height: 1), itemBuilder: (_, i) => _RewardTile(item: result.items[i]), ), ), ); } }