import 'package:flutter/material.dart'; import '../services/odoo_service.dart'; import '../theme/app_theme.dart'; import '../widgets/carousel_widget.dart'; import '../widgets/promo_card_widget.dart'; import '../widgets/subscription_list_widget.dart'; /// Home tab — shows loyalty card, subscriptions, carousel, and promo highlights. /// Notification polling and AppBar are handled by MainShell. class LoyaltyDashboard extends StatefulWidget { final int partnerId; const LoyaltyDashboard({super.key, required this.partnerId}); @override State createState() => _LoyaltyDashboardState(); } class _LoyaltyDashboardState extends State { List _loyaltyCards = []; List _subscriptions = []; List _carouselSlides = []; List _promos = []; bool _isLoading = true; @override void initState() { super.initState(); _fetchAll(); } Future _fetchAll() async { setState(() => _isLoading = true); try { final results = await Future.wait([ OdooService().getLoyaltyCards(widget.partnerId), OdooService().getSubscriptionCards(widget.partnerId), OdooService().getCmsContent(), ]); final rawCards = results[0] as List; final rawSubs = results[1] as List; final cms = results[2] as Map; final List cards = []; final List subs = [...rawSubs]; for (var card in rawCards) { final progName = (card['program_id']?[1] as String? ?? '').toLowerCase(); final isSub = progName.contains('subscription') || card['subscription_start_date'] != null || card['subscription_end_date'] != null; if (isSub) { final code = card['code']; if (!subs.any((s) => s['code'] == code)) { subs.add(card); } } else { cards.add(card); } } if (mounted) { setState(() { _loyaltyCards = cards; _subscriptions = subs; _carouselSlides = (cms['carousel'] as List?) ?? []; _promos = (cms['promos'] as List?) ?? []; _isLoading = false; }); } } catch (e) { if (mounted) { setState(() => _isLoading = false); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Error loading data: $e')), ); } } } @override Widget build(BuildContext context) { if (_isLoading) { return const Center(child: CircularProgressIndicator()); } return RefreshIndicator( onRefresh: _fetchAll, child: ListView( children: [ // ── Loyalty Card ───────────────────────────────────────────── if (_loyaltyCards.isEmpty) Padding( padding: const EdgeInsets.fromLTRB(24, 32, 24, 0), child: Text( 'No active loyalty card yet.', style: Theme.of(context).textTheme.titleLarge, textAlign: TextAlign.center, ), ) else ..._loyaltyCards.map((card) => _LoyaltyCardTile(card: card)), // ── Subscriptions ───────────────────────────────────────────── if (_subscriptions.isNotEmpty) SubscriptionListWidget(subscriptions: _subscriptions), const SizedBox(height: 20), // ── Carousel ────────────────────────────────────────────────── if (_carouselSlides.isNotEmpty) ...[ CarouselWidget(slides: _carouselSlides), const SizedBox(height: 24), ], // ── Promo Highlights ───────────────────────────────────────── if (_promos.isNotEmpty) ...[ PromoCardRow(promos: _promos), const SizedBox(height: 24), ], ], ), ); } } class _LoyaltyCardTile extends StatelessWidget { final dynamic card; const _LoyaltyCardTile({required this.card}); @override Widget build(BuildContext context) { final programName = (card['program_id']?[1] as String? ?? '').toLowerCase(); String tier = 'MEMBER'; if (programName.contains('silver')) tier = 'SILVER MEMBER'; if (programName.contains('gold')) tier = 'GOLD MEMBER'; if (programName.contains('platinum')) tier = 'PLATINUM MEMBER'; return Container( margin: const EdgeInsets.fromLTRB(16, 16, 16, 8), padding: const EdgeInsets.all(24), decoration: BoxDecoration( color: AppTheme.primary, // Rich brick red background borderRadius: BorderRadius.circular(16), border: Border.all( color: const Color(0xFFF3DCA2), // Golden border width: 1.5, ), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.12), blurRadius: 16, offset: const Offset(0, 6), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Text( '${card['program_id']?[1] ?? 'Loyalty Program'}', style: Theme.of(context).textTheme.titleLarge?.copyWith( color: Colors.white, fontFamily: 'serif', ), softWrap: true, ), ), const SizedBox(width: 12), Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: const Color(0xFFB58428), // Golden honey badge background borderRadius: BorderRadius.circular(20), ), child: Text( tier, style: Theme.of(context).textTheme.labelLarge?.copyWith( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 9, letterSpacing: 0.8, ), ), ), ], ), const SizedBox(height: 24), Text( 'MEMBERSHIP CODE', style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Colors.white.withValues(alpha: 0.7), fontWeight: FontWeight.bold, fontSize: 10, letterSpacing: 1.0, ), ), const SizedBox(height: 4), Text( '${card['code'] ?? 'N/A'}', style: Theme.of(context).textTheme.titleMedium?.copyWith( color: Colors.white, fontFamily: 'monospace', fontSize: 16, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.end, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'AVAILABLE POINTS', style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Colors.white.withValues(alpha: 0.7), fontWeight: FontWeight.bold, fontSize: 10, letterSpacing: 1.0, ), ), const SizedBox(height: 4), Row( children: [ const Icon( Icons.restaurant_rounded, color: Color(0xFFF3DCA2), size: 16, ), const SizedBox(width: 6), Text( 'Dine & Save', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Colors.white.withValues(alpha: 0.9), fontStyle: FontStyle.italic, ), ), ], ), ], ), Text( '${card['points'] ?? 0}', style: Theme.of(context).textTheme.displayLarge?.copyWith( color: const Color(0xFFF3DCA2), // Bright golden amber accent fontWeight: FontWeight.bold, ), ), ], ), ], ), ); } }