import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import '../../services/auth_service.dart'; import '../../services/profile_service.dart'; import '../../services/api_service.dart'; import '../../services/dashboard_service.dart'; import '../../models/profile_model.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'dart:math'; import '../../services/perkembangan_service.dart'; import '../../services/jadwal_service.dart'; import 'anak_screen.dart'; import 'edit_profile_screen.dart'; import '../../services/stunting_service.dart'; class ProfileScreen extends StatefulWidget { @override _ProfileScreenState createState() => _ProfileScreenState(); } class _ProfileScreenState extends State with SingleTickerProviderStateMixin { late AnimationController _animationController; late Animation _fadeAnimation; late Animation _slideAnimation; final AuthService _authService = AuthService(); final ProfileService _profileService = ProfileService(); final ApiService _apiService = ApiService(); final DashboardService _dashboardService = DashboardService(); final JadwalService _jadwalService = JadwalService(); // User data String _userName = 'User'; String _userInitials = 'U'; String _childName = ''; String _childAge = ''; String _childGender = ''; String _email = ''; String _phone = ''; String _address = ''; String _nik = ''; double _childHeight = 0; double _childWeight = 0; String _childStatus = '-'; bool _isChildStunting = false; int _childCount = 0; int _scheduleCount = 0; bool _isLoading = true; // Dashboard data Map _dashboardData = {}; int? _selectedAnakId; @override void initState() { super.initState(); _animationController = AnimationController( vsync: this, duration: Duration(milliseconds: 1000), ); _fadeAnimation = Tween( begin: 0.0, end: 1.0, ).animate(CurvedAnimation( parent: _animationController, curve: Interval(0.1, 1.0, curve: Curves.easeOut), )); _slideAnimation = Tween( begin: 50.0, end: 0.0, ).animate(CurvedAnimation( parent: _animationController, curve: Interval(0.2, 0.8, curve: Curves.easeOut), )); // Clear any API cache first _apiService.clearChildCache(); // Load data initially _loadUserData(); _animationController.forward(); } Future _loadUserData() async { setState(() { _isLoading = true; }); try { // Get data from SharedPreferences first final prefs = await SharedPreferences.getInstance(); // Reset child count to 0 _childCount = 0; await prefs.setInt('child_count', 0); // Basic user info String? userName = prefs.getString('nama_ibu'); String? nik = prefs.getString('nik'); int? userId = prefs.getInt('user_id'); // Other profile info - Explicitly get from SharedPreferences first String? email = prefs.getString('email') ?? ''; String? phone = prefs.getString('no_telp') ?? ''; String? address = prefs.getString('alamat') ?? ''; print('Initial data from SharedPreferences:'); print('Email: $email'); print('Phone: $phone'); // Load dashboard data to get child info (same as dashboard screen) try { // Get last selected anak ID _selectedAnakId = await _dashboardService.getLastSelectedAnakId(); print('Selected Anak ID: $_selectedAnakId'); if (_selectedAnakId != null) { // Get dashboard summary _dashboardData = await _dashboardService.getDashboardSummary(anakId: _selectedAnakId); print('Dashboard data loaded: ${_dashboardData.toString()}'); if (_dashboardData['success'] == true && _dashboardData['data'] != null) { // Extract child info from dashboard data final childData = _dashboardData['data']['anak'] ?? {}; final statsData = _dashboardData['data']['statistik'] ?? {}; final growthData = _dashboardData['data']['pertumbuhan'] ?? {}; // Debug the data structure print('Child data: $childData'); print('Stats data: $statsData'); print('Growth data: $growthData'); // First try to get direct data from perkembangan service to ensure consistency try { final perkembanganService = PerkembanganService(); final perkembanganList = await perkembanganService.getPerkembanganByAnakId(_selectedAnakId!); if (perkembanganList.isNotEmpty) { // Sort by date descending to get the most recent perkembanganList.sort((a, b) { final dateA = DateTime.parse(a['tanggal']); final dateB = DateTime.parse(b['tanggal']); return dateB.compareTo(dateA); // Most recent first }); // Get the most recent record final latestRecord = perkembanganList.first; print('Using most recent perkembangan data: $latestRecord'); _childHeight = double.tryParse(latestRecord['tinggi_badan'].toString()) ?? 0; _childWeight = double.tryParse(latestRecord['berat_badan'].toString()) ?? 0; print('Perkembangan height: $_childHeight, weight: $_childWeight'); } } catch (e) { print('Error getting perkembangan data: $e, falling back to dashboard data'); } // If we couldn't get perkembangan data, fallback to dashboard data if (_childHeight <= 0 || _childWeight <= 0) { // Update child info String childName = childData['nama_anak'] ?? ''; String childAge = childData['usia'] ?? statsData['age'] ?? ''; String childGender = childData['jenis_kelamin'] ?? ''; // Extract height and weight - try all possible paths _childHeight = 0; _childWeight = 0; try { // Try directly from growth data (pertumbuhan) if (growthData is Map && growthData.isNotEmpty) { if (growthData.containsKey('tinggi_badan')) { var tinggi = growthData['tinggi_badan']; var berat = growthData['berat_badan']; print('Found direct growth data: tinggi=$tinggi, berat=$berat'); _childHeight = double.tryParse(tinggi.toString()) ?? 0; _childWeight = double.tryParse(berat.toString()) ?? 0; } } // If not found, try from stats data if (_childHeight <= 0 && statsData is Map && statsData.isNotEmpty) { if (statsData.containsKey('height') && statsData['height'] is Map) { var heightData = statsData['height']; var weightData = statsData['weight']; print('Found stats data: height=$heightData, weight=$weightData'); if (heightData.containsKey('value')) { _childHeight = double.tryParse(heightData['value'].toString()) ?? 0; } if (weightData != null && weightData.containsKey('value')) { _childWeight = double.tryParse(weightData['value'].toString()) ?? 0; } } } // Last resort - check if values are directly in anak data if (_childHeight <= 0 && childData is Map && childData.isNotEmpty) { if (childData.containsKey('tinggi_badan') || childData.containsKey('tinggi')) { var tinggi = childData['tinggi_badan'] ?? childData['tinggi']; var berat = childData['berat_badan'] ?? childData['berat']; print('Found child data: tinggi=$tinggi, berat=$berat'); _childHeight = double.tryParse(tinggi.toString()) ?? 0; _childWeight = double.tryParse(berat.toString()) ?? 0; } } } catch (e) { print('Error extracting height/weight: $e'); } print('Final extracted values: height=$_childHeight, weight=$_childWeight'); // Update local state with dashboard data _childName = childName; _childAge = childAge; _childGender = childGender; } else { // We already got data from perkembangan service, just update other fields String childName = childData['nama_anak'] ?? ''; String childAge = childData['usia'] ?? statsData['age'] ?? ''; String childGender = childData['jenis_kelamin'] ?? ''; _childName = childName; _childAge = childAge; _childGender = childGender; } // Try to get stunting data first try { final stuntingService = StuntingService(); final stuntingData = await stuntingService.getStuntingByAnakId(_selectedAnakId!); if (stuntingData.isNotEmpty) { // Sort by latest date stuntingData.sort((a, b) => b.tanggalPemeriksaan.compareTo(a.tanggalPemeriksaan)); // Get latest status final latestStuntingData = stuntingData.first; setState(() { _childStatus = latestStuntingData.status; _isChildStunting = _childStatus.toLowerCase().contains('stunting'); }); print('Status from stunting data: $_childStatus'); } } catch (e) { print('Error getting stunting data: $e'); // Fallback to stats data if stunting data not available if (statsData.isNotEmpty) { String? status; if (statsData['overall_status'] != null) { status = statsData['overall_status'].toString(); } else if (statsData['status'] != null) { status = statsData['status'].toString(); } else if (statsData['stunting_status'] != null) { status = statsData['stunting_status'].toString(); } if (status != null) { final normalizedStatus = status.toLowerCase().trim(); setState(() { if (normalizedStatus.contains('stunting')) { if (normalizedStatus.contains('resiko') || normalizedStatus.contains('risiko')) { _childStatus = 'Resiko Stunting'; } else if (!normalizedStatus.contains('tidak')) { _childStatus = 'Stunting'; } else if (normalizedStatus.contains('tidak')) { _childStatus = 'Tidak Stunting'; } } else if (normalizedStatus.contains('normal')) { _childStatus = 'Tidak Stunting'; } _isChildStunting = _childStatus.toLowerCase() == 'stunting'; }); print('Status from stats data: $_childStatus'); } } } } } } catch (e) { print('Error loading dashboard data: $e'); } // Try to get fresh user data from API if possible bool apiHasAnakData = false; try { if (nik != null && nik.isNotEmpty) { print('Fetching fresh user data from API'); final userInfo = await _authService.getCurrentUser(); print('API Response: ${userInfo.toString().substring(0, min(userInfo.toString().length, 500))}...'); if (userInfo['success'] == true && userInfo['data'] != null) { final userData = userInfo['data']; // Update user data with better null checking userName = userData['nama'] ?? userData['nama_ibu'] ?? userName; // For email - check multiple possible field names and sources if (userData['email'] != null && userData['email'].toString().isNotEmpty) { email = userData['email'].toString(); print('Email from API: $email'); await prefs.setString('email', email); } // For phone - check multiple possible field names if (userData['no_telp'] != null && userData['no_telp'].toString().isNotEmpty) { phone = userData['no_telp'].toString(); print('Phone from API (no_telp): $phone'); await prefs.setString('no_telp', phone); } else if (userData['telepon'] != null && userData['telepon'].toString().isNotEmpty) { phone = userData['telepon'].toString(); print('Phone from API (telepon): $phone'); await prefs.setString('no_telp', phone); } else if (userData['nomor_telepon'] != null && userData['nomor_telepon'].toString().isNotEmpty) { phone = userData['nomor_telepon'].toString(); print('Phone from API (nomor_telepon): $phone'); await prefs.setString('no_telp', phone); } address = userData['alamat'] ?? address; // Update preferences with fresh data if (userName != null) await prefs.setString('nama_ibu', userName); if (address != null) await prefs.setString('alamat', address); // Check for child data in API response if (userData['anak'] != null && userData['anak'] is List) { // Update child count dengan memeriksa apakah array benar-benar berisi data if (userData['anak'].isNotEmpty) { _childCount = userData['anak'].length; await prefs.setInt('child_count', _childCount); apiHasAnakData = true; print('Updated child count from API: $_childCount'); } else { // Array kosong, berarti tidak ada anak _childCount = 0; await prefs.setInt('child_count', 0); apiHasAnakData = true; // API memberikan data valid (array kosong) print('API reports no children (empty array)'); } } } } } catch (e) { print('Error fetching user data from API: $e'); // Continue with stored data } // If API has confirmed there are no children, don't try to get more data if (!apiHasAnakData && userId != null) { try { final List children = await _profileService.getChildren(userId: userId); if (children.isNotEmpty) { // Pastikan ini bukan dummy data bool isDummyData = children.length == 1 && (children[0]['nama']?.toString() == 'Data Dummy - API Error' || children[0]['nama']?.toString() == 'Error Data'); if (!isDummyData) { _childCount = children.length; await prefs.setInt('child_count', _childCount); print('Updated child count from ProfileService: $_childCount'); // Ambil jadwal dari semua anak _scheduleCount = 0; for (var child in children) { try { if (child['id'] != null) { final jadwalList = await _jadwalService.getRiwayatJadwalAnak(child['id']); // Filter jadwal yang belum terlaksana final jadwalBelumTerlaksana = jadwalList.where((jadwal) => jadwal.status?.toLowerCase() == 'belum' || jadwal.status?.toLowerCase() == 'jadwal' || jadwal.isImplemented == false ).toList(); _scheduleCount += jadwalBelumTerlaksana.length; print('Jadwal belum terlaksana untuk anak ${child['id']}: ${jadwalBelumTerlaksana.length} dari total ${jadwalList.length}'); } } catch (e) { print('Error mengambil jadwal untuk anak ${child['id']}: $e'); } } print('Total jadwal belum terlaksana untuk semua anak: $_scheduleCount'); } else { print('Ignoring dummy data from ProfileService'); _childCount = 0; await prefs.setInt('child_count', 0); } } else { // List kosong, berarti tidak ada anak _childCount = 0; await prefs.setInt('child_count', 0); print('ProfileService reports no children'); } } catch (e) { print('Error fetching children via ProfileService: $e'); } } else if (apiHasAnakData && _selectedAnakId != null) { // Ambil jadwal untuk anak yang dipilih try { final jadwalList = await _jadwalService.getUpcomingJadwalForChild(_selectedAnakId!); _scheduleCount = jadwalList.length; print('Jadwal untuk anak $_selectedAnakId: $_scheduleCount'); } catch (e) { print('Error mengambil jadwal untuk anak $_selectedAnakId: $e'); } } // Double-check data after API call to ensure we have the latest if (email == null || email.isEmpty) { email = prefs.getString('email') ?? ''; } if (phone == null || phone.isEmpty) { phone = prefs.getString('no_telp') ?? ''; } // Update state with all the data we've gathered setState(() { _isLoading = false; _userName = userName ?? 'User'; _nik = nik ?? ''; _email = email ?? ''; _phone = phone ?? ''; _address = address ?? ''; // Generate user initials if (_userName.isNotEmpty) { if (_userName.contains(' ')) { final nameParts = _userName.split(' '); if (nameParts.length >= 2) { _userInitials = '${nameParts[0][0]}${nameParts[1][0]}'; } else { _userInitials = _userName.substring(0, _userName.length > 1 ? 2 : 1); } } else { _userInitials = _userName.substring(0, _userName.length > 1 ? 2 : 1); } _userInitials = _userInitials.toUpperCase(); } }); } catch (e) { print('Error loading user data: $e'); setState(() { _isLoading = false; }); } } // Method to refresh data from API Future _refreshData() async { setState(() { _isLoading = true; }); try { // Ambil ID anak yang terakhir dipilih dari dashboard _selectedAnakId = await _dashboardService.getLastSelectedAnakId(); print('Refreshing data for anak ID: $_selectedAnakId'); if (_selectedAnakId != null) { // Pastikan cache dihapus untuk mendapatkan data terbaru _apiService.clearChildCache(); // Ambil data dashboard untuk anak yang dipilih _dashboardData = await _dashboardService.getDashboardSummary(anakId: _selectedAnakId); print('Dashboard data refreshed: ${_dashboardData.toString()}'); if (_dashboardData['success'] == true && _dashboardData['data'] != null) { final childData = _dashboardData['data']['anak'] ?? {}; final statsData = _dashboardData['data']['statistik'] ?? {}; final growthData = _dashboardData['data']['pertumbuhan'] ?? {}; print('Child data: $childData'); print('Stats data: $statsData'); print('Growth data: $growthData'); // Reset nilai awal data anak double childHeight = 0; double childWeight = 0; String childName = childData['nama_anak'] ?? ''; String childAge = childData['usia'] ?? statsData['age'] ?? ''; String childGender = childData['jenis_kelamin'] ?? ''; String childStatus = '-'; bool isChildStunting = false; // Ekstrak tinggi dan berat badan dari growth data if (growthData is Map && growthData.isNotEmpty) { if (growthData['tinggi_badan'] != null) { childHeight = double.tryParse(growthData['tinggi_badan'].toString()) ?? 0; print('Extracted height from growth data: $childHeight'); } if (growthData['berat_badan'] != null) { childWeight = double.tryParse(growthData['berat_badan'].toString()) ?? 0; print('Extracted weight from growth data: $childWeight'); } } else { print('Growth data empty or invalid'); } // Jika tidak berhasil mendapatkan tinggi dan berat dari growth data, coba dari stats data if (childHeight <= 0 || childWeight <= 0) { if (statsData is Map && statsData.isNotEmpty) { // Cek tinggi dari stats data if (statsData['height'] is Map && statsData['height']['value'] != null) { childHeight = double.tryParse(statsData['height']['value'].toString()) ?? 0; print('Extracted height from stats data: $childHeight'); } // Cek berat dari stats data if (statsData['weight'] is Map && statsData['weight']['value'] != null) { childWeight = double.tryParse(statsData['weight']['value'].toString()) ?? 0; print('Extracted weight from stats data: $childWeight'); } } } // Jika masih tidak berhasil, cek langsung dari child data if (childHeight <= 0 || childWeight <= 0) { if (childData is Map && childData.isNotEmpty) { if (childData['tinggi_badan'] != null) { childHeight = double.tryParse(childData['tinggi_badan'].toString()) ?? 0; print('Extracted height from child data: $childHeight'); } if (childData['berat_badan'] != null) { childWeight = double.tryParse(childData['berat_badan'].toString()) ?? 0; print('Extracted weight from child data: $childWeight'); } } } // Coba dapatkan data stunting try { final stuntingService = StuntingService(); final stuntingData = await stuntingService.getStuntingByAnakId(_selectedAnakId!); if (stuntingData.isNotEmpty) { // Urutkan berdasarkan tanggal terbaru stuntingData.sort((a, b) => b.tanggalPemeriksaan.compareTo(a.tanggalPemeriksaan)); final latestStuntingData = stuntingData.first; // Jika data stunting memiliki tinggi dan berat, gunakan data tersebut if (latestStuntingData.tinggiBadan > 0) { childHeight = latestStuntingData.tinggiBadan; print('Updated height from stunting data: $childHeight'); } if (latestStuntingData.beratBadan > 0) { childWeight = latestStuntingData.beratBadan; print('Updated weight from stunting data: $childWeight'); } // Ambil dan standardisasi status stunting final status = latestStuntingData.status.toLowerCase().trim(); if (status.contains('stunting')) { if (status.contains('resiko') || status.contains('risiko')) { childStatus = 'Resiko Stunting'; } else if (!status.contains('tidak')) { childStatus = 'Stunting'; } else if (status.contains('tidak')) { childStatus = 'Tidak Stunting'; } } else if (status.contains('normal')) { childStatus = 'Tidak Stunting'; } isChildStunting = childStatus == 'Stunting'; print('Status dari data stunting terbaru: $childStatus'); } else { // Jika tidak ada data stunting, coba dari statistik dashboard String? dashboardStatus; if (statsData.isNotEmpty) { if (statsData['overall_status'] != null) { dashboardStatus = statsData['overall_status'].toString(); } else if (statsData['status'] != null) { dashboardStatus = statsData['status'].toString(); } else if (statsData['stunting_status'] != null) { dashboardStatus = statsData['stunting_status'].toString(); } if (dashboardStatus != null) { final statusNormalized = dashboardStatus.toLowerCase().trim(); if (statusNormalized.contains('stunting')) { if (statusNormalized.contains('resiko') || statusNormalized.contains('risiko')) { childStatus = 'Resiko Stunting'; } else if (!statusNormalized.contains('tidak')) { childStatus = 'Stunting'; } else if (statusNormalized.contains('tidak')) { childStatus = 'Tidak Stunting'; } } else if (statusNormalized.contains('normal')) { childStatus = 'Tidak Stunting'; } isChildStunting = childStatus == 'Stunting'; print('Status dari statistik dashboard: $childStatus'); } } } } catch (e) { print('Error mendapatkan data stunting: $e'); // Tetap gunakan nilai default jika terjadi error } // Update semua data ke state sekaligus setState(() { _childName = childName; _childAge = childAge; _childGender = childGender; _childHeight = childHeight; _childWeight = childWeight; _childStatus = childStatus; _isChildStunting = isChildStunting; _childCount = 1; // Since we're viewing a specific child }); } } } catch (e) { print('Error in _refreshData: $e'); } finally { if (mounted) { setState(() { _isLoading = false; }); } } } @override void dispose() { _animationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final screenSize = MediaQuery.of(context).size; return Scaffold( backgroundColor: Colors.grey[100], body: _isLoading ? Center(child: CircularProgressIndicator()) : RefreshIndicator( onRefresh: _refreshData, child: CustomScrollView( physics: AlwaysScrollableScrollPhysics(), slivers: [ // App Bar SliverAppBar( expandedHeight: screenSize.height * 0.35, pinned: true, stretch: true, systemOverlayStyle: SystemUiOverlayStyle.light, backgroundColor: Colors.transparent, elevation: 0, leading: Padding( padding: EdgeInsets.all(8.0), child: Container( decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.black.withOpacity(0.2), ), child: IconButton( icon: Icon(Icons.arrow_back, color: Colors.white), onPressed: () => Navigator.pop(context), ), ), ), flexibleSpace: FlexibleSpaceBar( background: Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Colors.teal.shade400, Colors.teal.shade800, ], ), ), child: Stack( children: [ // Background patterns Positioned( top: -50, right: -20, child: Container( width: 200, height: 200, decoration: BoxDecoration( borderRadius: BorderRadius.circular(100), color: Colors.white.withOpacity(0.1), ), ), ), Positioned( bottom: -50, left: -50, child: Container( width: 200, height: 200, decoration: BoxDecoration( borderRadius: BorderRadius.circular(100), color: Colors.white.withOpacity(0.1), ), ), ), // Profile content Center( child: FadeTransition( opacity: _fadeAnimation, child: AnimatedBuilder( animation: _animationController, builder: (context, child) { return Transform.translate( offset: Offset(0, _slideAnimation.value), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Hero( tag: 'profile_pic', child: Container( padding: EdgeInsets.all(4), decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all( color: Colors.white, width: 3, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.2), blurRadius: 15, spreadRadius: 2, ), ], ), child: CircleAvatar( radius: screenSize.width * 0.15, backgroundColor: Colors.white.withOpacity(0.2), child: Text( _userInitials, style: TextStyle( fontSize: screenSize.width * 0.08, fontWeight: FontWeight.bold, color: Colors.white, ), ), ), ), ), SizedBox(height: 20), Text( _userName, style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: Colors.white, letterSpacing: 0.5, shadows: [ Shadow( color: Colors.black26, offset: Offset(0, 2), blurRadius: 4, ), ], ), ), SizedBox(height: 6), Container( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: Colors.white24, borderRadius: BorderRadius.circular(20), ), child: Text( _childCount > 0 ? _childName.isNotEmpty ? (_childCount > 1 ? 'Ibu dari $_childName dan ${_childCount-1} anak lainnya' : 'Ibu dari $_childName') : 'Memiliki $_childCount anak' : 'Belum memiliki data anak', style: TextStyle( color: Colors.white, fontSize: 14, ), ), ), ], ), ); }, ), ), ), ], ), ), collapseMode: CollapseMode.parallax, ), bottom: PreferredSize( preferredSize: Size.fromHeight(20), child: Container( height: 32, decoration: BoxDecoration( color: Colors.grey[100], borderRadius: BorderRadius.only( topLeft: Radius.circular(30), topRight: Radius.circular(30), ), ), ), ), ), // Content SliverToBoxAdapter( child: AnimatedBuilder( animation: _animationController, builder: (context, child) { return Opacity( opacity: _fadeAnimation.value, child: Transform.translate( offset: Offset(0, _slideAnimation.value), child: child, ), ); }, child: Padding( padding: EdgeInsets.fromLTRB(16, 0, 16, 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Quick stats Row( children: [ Expanded( flex: 1, child: _buildStatCard( context: context, title: 'Anak', value: '$_childCount', icon: Icons.child_care, color: Colors.blue, ), ), ], ), SizedBox(height: 24), // Personal info section Text( 'Informasi Pribadi', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.grey[800], ), ), SizedBox(height: 12), // Info cards _buildProfileCard( context: context, title: 'Email', content: _email.isNotEmpty ? _email : '-', icon: Icons.email_outlined, iconColor: Colors.red, ), SizedBox(height: 12), _buildProfileCard( context: context, title: 'No. Handphone', content: _phone.isNotEmpty ? _phone : '-', icon: Icons.phone_outlined, iconColor: Colors.green, ), SizedBox(height: 12), _buildProfileCard( context: context, title: 'Alamat', content: _address.isNotEmpty ? _address : '-', icon: Icons.location_on_outlined, iconColor: Colors.blue, ), SizedBox(height: 12), _buildProfileCard( context: context, title: 'NIK', content: _nik.isNotEmpty ? _maskNIK(_nik) : '-', icon: Icons.credit_card_outlined, iconColor: Colors.purple, ), SizedBox(height: 24), // Child info section - only show if there's child data if (_childName.isNotEmpty) ...[ Text( 'Informasi Anak', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.grey[800], ), ), SizedBox(height: 12), _buildChildCard(), SizedBox(height: 24), ], // Buttons section _buildActionButtons(context), SizedBox(height: 40), ], ), ), ), ), ], ), ), bottomNavigationBar: BottomAppBar( shape: CircularNotchedRectangle(), notchMargin: 8, elevation: 8, child: Container( height: 60, child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Expanded( child: IconButton( icon: Icon( Icons.home, color: Colors.grey[500], size: 28, ), onPressed: () { Navigator.pop(context); }, ), ), Expanded(child: SizedBox(width: 40)), Expanded( child: IconButton( icon: Icon( Icons.person, color: Colors.green[600], size: 28, ), onPressed: null, // Sedang di halaman profile ), ), ], ), ), ), floatingActionButton: FloatingActionButton( backgroundColor: Colors.green, elevation: 4, child: Icon(Icons.add_chart, size: 28), onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (context) => AnakScreen(), ), ).then((_) => _refreshData()); }, tooltip: 'Tambah Data Anak', ), floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, ); } // Helper method to mask NIK for privacy String _maskNIK(String nik) { if (nik.length <= 8) return nik; return nik.substring(0, 8) + 'XXXXXXXX'; } Widget _buildStatCard({ required BuildContext context, required String title, required String value, required IconData icon, required Color color, }) { // Verifikasi nilai untuk menghindari tampilan yang tidak akurat String displayValue = value; if (title == 'Anak' && value != '0') { try { final intValue = int.parse(value); if (intValue > 10) { displayValue = '1'; // Tampilkan nilai yang lebih masuk akal } } catch (e) { // Gunakan nilai asli jika parsing gagal } } return Container( padding: EdgeInsets.symmetric(vertical: 16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: Offset(0, 4), ), ], ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( padding: EdgeInsets.all(8), decoration: BoxDecoration( color: color.withOpacity(0.1), shape: BoxShape.circle, ), child: Icon( icon, color: color, size: 24, ), ), SizedBox(height: 8), Text( displayValue, style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.grey[800], ), ), SizedBox(height: 4), Text( title, style: TextStyle( fontSize: 12, color: Colors.grey[600], ), ), ], ), ); } Widget _buildProfileCard({ required BuildContext context, required String title, required String content, required IconData icon, required Color iconColor, }) { return Container( padding: EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: Offset(0, 4), ), ], ), child: Row( children: [ Container( padding: EdgeInsets.all(10), decoration: BoxDecoration( color: iconColor.withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Icon( icon, color: iconColor, size: 24, ), ), SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: TextStyle( fontSize: 14, color: Colors.grey[600], ), ), SizedBox(height: 4), Text( content, style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Colors.grey[800], ), ), ], ), ), ], ), ); } Widget _buildChildCard() { // Check if we have dashboard data if (_dashboardData.isEmpty || _dashboardData['success'] != true || _dashboardData['data'] == null || _childCount <= 0) { // No dashboard data or no children, show default/empty state return Container( padding: EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: Offset(0, 4), ), ], ), child: Column( children: [ Row( children: [ Container( padding: EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.orange.withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Icon( Icons.child_care, color: Colors.orange, size: 24, ), ), SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Belum ada data anak", style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Colors.grey[800], ), ), SizedBox(height: 4), Text( "Tambahkan data anak terlebih dahulu", style: TextStyle( fontSize: 14, color: Colors.grey[600], ), ), ], ), ), ], ), ], ), ); } // We have dashboard data, extract and format height and weight print('Building child card with height: $_childHeight, weight: $_childWeight'); String heightStr; if (_childHeight > 0) { // Format to 1 decimal place if needed heightStr = _childHeight == _childHeight.roundToDouble() ? _childHeight.round().toString() : _childHeight.toStringAsFixed(1); heightStr += ' cm'; } else { heightStr = "-"; } String weightStr; if (_childWeight > 0) { // Format to 1 decimal place if needed weightStr = _childWeight == _childWeight.roundToDouble() ? _childWeight.round().toString() : _childWeight.toStringAsFixed(1); weightStr += ' kg'; } else { weightStr = "-"; } // Gunakan status yang sudah disimpan dari _refreshData String status = _childStatus; Color statusColor = Colors.blue; IconData statusIcon = Icons.info_outline; // Tentukan warna dan icon sesuai dengan status if (status == 'Stunting') { statusColor = Colors.red; statusIcon = Icons.warning_rounded; } else if (status == 'Resiko Stunting') { statusColor = Colors.orange; statusIcon = Icons.warning_amber_rounded; } else if (status == 'Tidak Stunting') { statusColor = Colors.green; statusIcon = Icons.check_circle; } else if (status == '-') { statusColor = Colors.grey; statusIcon = Icons.info_outline; } print('Status untuk card anak: $status, Color: $statusColor'); return Container( padding: EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: Offset(0, 4), ), ], ), child: Column( children: [ Row( children: [ Container( padding: EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.blue.withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Icon( Icons.child_care, color: Colors.blue, size: 24, ), ), SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( _childName.isNotEmpty ? _childName : '-', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Colors.grey[800], ), ), SizedBox(height: 4), Text( _childAge.isNotEmpty && _childGender.isNotEmpty ? '$_childAge • $_childGender' : (_childAge.isNotEmpty ? _childAge : (_childGender.isNotEmpty ? _childGender : '-')), style: TextStyle( fontSize: 14, color: Colors.grey[600], ), ), ], ), ), ], ), SizedBox(height: 16), Divider(), SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _buildChildStat('Tinggi', heightStr, Icons.height), _buildChildStat('Berat', weightStr, Icons.monitor_weight), _buildChildStat( 'Status', status, statusIcon, customColor: statusColor ), ], ), ], ), ); } Widget _buildChildStat( String label, String value, IconData icon, {Color? customColor} ) { bool isEmpty = value == "-" || value.isEmpty; Color color = isEmpty ? Colors.grey : (customColor ?? Colors.blue); return Column( children: [ Container( padding: EdgeInsets.all(10), decoration: BoxDecoration( color: color.withOpacity(0.1), shape: BoxShape.circle, ), child: Icon( icon, color: color, size: 20, ), ), SizedBox(height: 6), Text( value == "-" ? "-" : value, style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: color, ), ), SizedBox(height: 4), Text( label, style: TextStyle( fontSize: 12, color: Colors.grey[600], ), ), ], ); } Widget _buildActionButtons(BuildContext context) { return Column( children: [ Container( width: double.infinity, child: ElevatedButton.icon( icon: Icon(Icons.edit), label: Text('Edit Profil'), style: ElevatedButton.styleFrom( backgroundColor: Colors.teal, foregroundColor: Colors.white, padding: EdgeInsets.symmetric(vertical: 14), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), onPressed: () => _showEditProfileDialog(context), ), ), SizedBox(height: 16), Container( width: double.infinity, child: OutlinedButton.icon( icon: Icon(Icons.logout), label: Text('Keluar'), style: OutlinedButton.styleFrom( foregroundColor: Colors.red, side: BorderSide(color: Colors.red), padding: EdgeInsets.symmetric(vertical: 14), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), onPressed: () => _showLogoutDialog(context), ), ), ], ); } void _showLogoutDialog(BuildContext context) { final navContext = Navigator.of(context); showDialog( context: context, builder: (BuildContext context) { return AlertDialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), title: Row( children: [ Icon( Icons.logout, color: Colors.red, ), SizedBox(width: 10), Text( "Konfirmasi", style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), ], ), content: Text( "Apakah Anda yakin ingin keluar dari aplikasi?", style: TextStyle(fontSize: 16), ), actions: [ TextButton( style: TextButton.styleFrom( foregroundColor: Colors.grey, ), onPressed: () { Navigator.pop(context); }, child: Text("Batal"), ), ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.red, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), onPressed: () async { Navigator.pop(context); // Implement logout logic using the profile service try { await _authService.logout(); } catch (e) { print('Logout error: $e'); // Continue with navigation even if API logout fails } // Gunakan navContext yang disiapkan di awal navContext.pushNamedAndRemoveUntil( '/login', (route) => false, // This clears the navigation stack ); }, child: Text("Keluar"), ), ], ); }, ); } void _showEditProfileDialog(BuildContext context) { final nameController = TextEditingController(text: _userName); final emailController = TextEditingController(text: _email); final phoneController = TextEditingController(text: _phone); final addressController = TextEditingController(text: _address); final nikController = TextEditingController(text: _nik); final formKey = GlobalKey(); bool isLoading = false; showDialog( context: context, builder: (BuildContext context) { return StatefulBuilder( builder: (context, setState) { return AlertDialog( title: Row( children: [ Icon(Icons.edit, color: Colors.teal), SizedBox(width: 10), Text('Edit Profil'), ], ), content: SingleChildScrollView( child: Form( key: formKey, child: Column( mainAxisSize: MainAxisSize.min, children: [ TextFormField( controller: nameController, decoration: InputDecoration( labelText: 'Nama Ibu', prefixIcon: Icon(Icons.person), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), ), validator: (value) { if (value == null || value.isEmpty) { return 'Nama tidak boleh kosong'; } return null; }, ), SizedBox(height: 16), TextFormField( controller: emailController, decoration: InputDecoration( labelText: 'Email', prefixIcon: Icon(Icons.email), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), ), keyboardType: TextInputType.emailAddress, validator: (value) { if (value == null || value.isEmpty) { return 'Email tidak boleh kosong'; } if (!value.contains('@') || !value.contains('.')) { return 'Email tidak valid'; } return null; }, ), SizedBox(height: 16), TextFormField( controller: phoneController, decoration: InputDecoration( labelText: 'No. Handphone', prefixIcon: Icon(Icons.phone), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), ), keyboardType: TextInputType.phone, validator: (value) { if (value == null || value.isEmpty) { return 'Nomor handphone tidak boleh kosong'; } if (value.length < 10) { return 'Nomor handphone minimal 10 digit'; } return null; }, ), SizedBox(height: 16), TextFormField( controller: addressController, decoration: InputDecoration( labelText: 'Alamat', prefixIcon: Icon(Icons.location_on), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), ), maxLines: 3, validator: (value) { if (value == null || value.isEmpty) { return 'Alamat tidak boleh kosong'; } return null; }, ), SizedBox(height: 16), TextFormField( controller: nikController, decoration: InputDecoration( labelText: 'NIK', prefixIcon: Icon(Icons.credit_card), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), ), keyboardType: TextInputType.number, maxLength: 16, validator: (value) { if (value == null || value.isEmpty) { return 'NIK tidak boleh kosong'; } if (value.length != 16) { return 'NIK harus 16 digit'; } return null; }, ), ], ), ), ), actions: [ TextButton( onPressed: isLoading ? null : () => Navigator.pop(context), child: Text('Batal'), style: TextButton.styleFrom( foregroundColor: Colors.grey, ), ), ElevatedButton( onPressed: isLoading ? null : () async { if (formKey.currentState!.validate()) { setState(() => isLoading = true); try { final profile = ProfileModel( id: 0, // ID akan diambil dari API name: nameController.text, email: emailController.text, phone: phoneController.text, address: addressController.text, nik: nikController.text, children: [], // Tidak perlu mengirim data anak ); await _profileService.updateProfile(profile); if (mounted) { Navigator.pop(context, true); // Return true to indicate success ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Profil berhasil diperbarui'), backgroundColor: Colors.green, ), ); _refreshData(); // Refresh data setelah update } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Gagal memperbarui profil: ${e.toString()}'), backgroundColor: Colors.red, ), ); } } finally { if (mounted) { setState(() => isLoading = false); } } } }, style: ElevatedButton.styleFrom( backgroundColor: Colors.teal, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), child: isLoading ? SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Colors.white), ), ) : Text('Simpan'), ), ], ); }, ); }, ); } }