From 4a842e909889220f16fd14386e6bd46aa7111e58 Mon Sep 17 00:00:00 2001 From: pradeepkumar Date: Sat, 18 Apr 2026 11:44:25 +0530 Subject: [PATCH] feat: update billing UI for pending cancellations and add service area modal to agent profile --- .../screens/agent_home_screen.dart | 150 +++++++++++++++--- .../presentation/widgets/billing_tab.dart | 29 ++-- 2 files changed, 145 insertions(+), 34 deletions(-) diff --git a/lib/features/agents/presentation/screens/agent_home_screen.dart b/lib/features/agents/presentation/screens/agent_home_screen.dart index 841dfd4..60b9cf6 100644 --- a/lib/features/agents/presentation/screens/agent_home_screen.dart +++ b/lib/features/agents/presentation/screens/agent_home_screen.dart @@ -760,31 +760,67 @@ class _AgentHomeContentState extends ConsumerState<_AgentHomeContent> { ], const SizedBox(height: 8), // Location + Member Since (center-aligned, uniform sizing) - if (locationDisplay.isNotEmpty) ...[ - Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SvgPicture.asset( - 'assets/icons/location_filled_icon.svg', - width: 16, - height: 16, - ), - const SizedBox(width: 4), - Flexible( - child: Text( - locationDisplay, - style: const TextStyle( - fontFamily: 'SourceSerif4', - fontSize: 14, - fontWeight: FontWeight.w600, - color: AppColors.primaryDark, + // Show up to 4 locations with "+N more" matching web's ProfileCard. + if (state.locationParts.isNotEmpty || + locationDisplay.isNotEmpty) ...[ + Builder(builder: (_) { + final parts = state.locationParts.isNotEmpty + ? state.locationParts + : locationDisplay + .split(',') + .map((s) => s.trim()) + .where((s) => s.isNotEmpty) + .toList(); + const maxVisible = 4; + final visible = parts.take(maxVisible).join(', '); + final remaining = parts.length - maxVisible; + + return GestureDetector( + onTap: remaining > 0 + ? () => _showLocationModal(context, parts) + : null, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SvgPicture.asset( + 'assets/icons/location_filled_icon.svg', + width: 16, + height: 16, ), - textAlign: TextAlign.center, - ), + const SizedBox(width: 4), + Flexible( + child: Text.rich( + TextSpan( + children: [ + TextSpan( + text: visible, + style: const TextStyle( + fontFamily: 'SourceSerif4', + fontSize: 14, + fontWeight: FontWeight.w600, + color: AppColors.primaryDark, + ), + ), + if (remaining > 0) + TextSpan( + text: ' +$remaining more', + style: const TextStyle( + fontFamily: 'SourceSerif4', + fontSize: 14, + fontWeight: FontWeight.w500, + color: AppColors.accentOrange, + ), + ), + ], + ), + textAlign: TextAlign.center, + ), + ), + ], ), - ], - ), + ); + }), const SizedBox(height: 6), ], Row( @@ -835,6 +871,74 @@ class _AgentHomeContentState extends ConsumerState<_AgentHomeContent> { } // ── Experience Section ── + // Full service-areas list modal (matches web's ProfileCard location modal) + void _showLocationModal(BuildContext context, List locations) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (ctx) => Container( + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + padding: EdgeInsets.fromLTRB( + 24, 24, 24, 24 + MediaQuery.of(ctx).padding.bottom, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + 'Service Areas', + style: TextStyle( + fontFamily: 'Fractul', + fontSize: 18, + fontWeight: FontWeight.w700, + color: AppColors.primaryDark, + ), + ), + GestureDetector( + onTap: () => Navigator.of(ctx).pop(), + child: Icon( + Icons.close, + color: AppColors.primaryDark.withValues(alpha: 0.5), + ), + ), + ], + ), + const SizedBox(height: 16), + Wrap( + spacing: 8, + runSpacing: 8, + children: locations + .map((loc) => Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 6), + decoration: BoxDecoration( + color: const Color(0xFFE8E8E8), + borderRadius: BorderRadius.circular(10), + ), + child: Text( + loc, + style: const TextStyle( + fontFamily: 'SourceSerif4', + fontSize: 13, + color: AppColors.primaryDark, + ), + ), + )) + .toList(), + ), + ], + ), + ), + ); + } + Widget _buildExperienceSection(AgentDetailState state) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 16), diff --git a/lib/features/profile/presentation/widgets/billing_tab.dart b/lib/features/profile/presentation/widgets/billing_tab.dart index a336eef..ed1d526 100644 --- a/lib/features/profile/presentation/widgets/billing_tab.dart +++ b/lib/features/profile/presentation/widgets/billing_tab.dart @@ -19,6 +19,9 @@ class _BillingTabState extends ConsumerState bool _isLoadingSubscription = false; bool _openedExternalCheckout = false; + bool get cancelAtPeriodEnd => + _subscription?['cancelAtPeriodEnd'] == true; + @override void initState() { super.initState(); @@ -179,7 +182,9 @@ class _BillingTabState extends ConsumerState const Spacer(), if (_subscription?['currentPeriodEnd'] != null) Text( - 'Renews ${_formatDate(_subscription!['currentPeriodEnd'] as String)}', + cancelAtPeriodEnd + ? 'Cancels on ${_formatDate(_subscription!['currentPeriodEnd'] as String)}' + : 'Renews ${_formatDate(_subscription!['currentPeriodEnd'] as String)}', style: TextStyle( fontFamily: 'SourceSerif4', fontSize: 13, @@ -256,17 +261,19 @@ class _BillingTabState extends ConsumerState const SizedBox(height: 16), ..._buildPlanFeatures(), const SizedBox(height: 16), - Center( - child: TextButton( - onPressed: _cancelSubscription, - style: TextButton.styleFrom(foregroundColor: Colors.red), - child: const Text('Cancel Subscription', - style: TextStyle( - fontFamily: 'Fractul', - fontSize: 14, - fontWeight: FontWeight.w500)), + // Hide Cancel Subscription once cancellation is pending (matches web) + if (!cancelAtPeriodEnd) + Center( + child: TextButton( + onPressed: _cancelSubscription, + style: TextButton.styleFrom(foregroundColor: Colors.red), + child: const Text('Cancel Subscription', + style: TextStyle( + fontFamily: 'Fractul', + fontSize: 14, + fontWeight: FontWeight.w500)), + ), ), - ), ], ), );