import 'dart:io'; import 'package:flutter/material.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:image_picker/image_picker.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; import 'package:percent_indicator/circular_percent_indicator.dart'; import '../services/session_manager.dart'; class ProfileScreen extends StatefulWidget { const ProfileScreen({super.key}); @override _ProfileScreenState createState() => _ProfileScreenState(); } class _ProfileScreenState extends State { final _formKey = GlobalKey(); final _usernameController = TextEditingController(); final _emailController = TextEditingController(); final _phoneController = TextEditingController(); final _addressController = TextEditingController(); final _farmNameController = TextEditingController(); final SupabaseClient _supabase = Supabase.instance.client; final ImagePicker _picker = ImagePicker(); String? _avatarUrl; bool _isLoading = false; User? _user; // Statistics data int _totalFields = 0; int _activeSchedules = 0; int _completedHarvests = 0; double _averageYield = 0; String _mostPlantedCrop = '-'; @override void initState() { super.initState(); _initializeProfile(); } Future _initializeProfile() async { // Check session validity first final isAuthenticated = SessionManager.isAuthenticated; if (!isAuthenticated) { debugPrint('Profile: User not authenticated or session expired'); setState(() { _user = null; _isLoading = false; }); return; } _user = _supabase.auth.currentUser; if (_user != null) { await _loadProfile(); await _loadStatistics(); } else { debugPrint('Profile: No current user found'); setState(() { _isLoading = false; }); } } Future _loadProfile() async { if (_user == null) return; setState(() => _isLoading = true); try { final response = await _supabase .from('profiles') .select( 'user_id, username, email, phone, address, avatar_url, farm_name', ) .eq('user_id', _user!.id) .maybeSingle(); if (response == null) { await _createProfile(); // Pastikan ini membuat semua field yang dibutuhkan final newProfile = await _supabase .from('profiles') .select( 'user_id, username, email, phone, address, avatar_url, farm_name', ) .eq('user_id', _user!.id) .single(); _updateControllers(newProfile); } else { _updateControllers(response); } } catch (e) { debugPrint('Error loading profile: $e'); _showErrorSnackbar('Gagal memuat profil: ${e.toString()}'); } finally { if (mounted) setState(() => _isLoading = false); } } Future _loadStatistics() async { if (_user == null) return; try { // Reset values first setState(() { _totalFields = 0; _activeSchedules = 0; _completedHarvests = 0; _averageYield = 0.0; }); // Fetch fields count safely final fieldsResponse = await _supabase .from('fields') .select('id') .eq('user_id', _user!.id); _totalFields = fieldsResponse.length; // Fetch active schedules safely final now = DateTime.now().toIso8601String(); final schedulesResponse = await _supabase .from('crop_schedules') .select() .eq('user_id', _user!.id) .gt('end_date', now); _activeSchedules = schedulesResponse.length; // Fetch harvest results safely final harvestResponse = await _supabase .from('harvest_results') .select('productivity') .eq('user_id', _user!.id); if (harvestResponse.isNotEmpty) { _completedHarvests = harvestResponse.length; // Calculate average yield safely double totalYield = 0; int validRecords = 0; for (final harvest in harvestResponse) { final productivity = harvest['productivity'] as num?; if (productivity != null) { totalYield += productivity.toDouble(); validRecords++; } } _averageYield = validRecords > 0 ? totalYield / validRecords : 0.0; } if (mounted) setState(() {}); } catch (e) { debugPrint('Error loading statistics: $e'); if (mounted) { setState(() { _totalFields = 0; _activeSchedules = 0; _completedHarvests = 0; _averageYield = 0.0; }); } } } Future _createProfile() async { if (_user == null) return; try { final username = _user!.email?.split('@').first ?? 'user_${DateTime.now().millisecondsSinceEpoch}'; await _supabase.from('profiles').insert({ 'user_id': _user!.id, 'username': username, 'email': _user!.email ?? '', 'created_at': DateTime.now().toUtc().toIso8601String(), 'updated_at': DateTime.now().toUtc().toIso8601String(), }); } catch (e) { debugPrint('Error creating profile: $e'); _showErrorSnackbar('Error membuat profil: ${e.toString()}'); rethrow; } } void _updateControllers(Map data) { _usernameController.text = data['username'] ?? ''; _emailController.text = data['email'] ?? ''; _phoneController.text = data['phone'] ?? ''; _addressController.text = data['address'] ?? ''; _farmNameController.text = data['farm_name'] ?? ''; setState(() { _avatarUrl = data['avatar_url'] ?? ''; }); } Future _updateProfile() async { if (!_formKey.currentState!.validate() || _user == null) return; setState(() => _isLoading = true); try { final updates = { 'username': _usernameController.text.trim(), 'phone': _phoneController.text.trim(), 'address': _addressController.text.trim(), 'farm_name': _farmNameController.text.trim(), 'updated_at': DateTime.now().toUtc().toIso8601String(), }; await _supabase .from('profiles') .update(updates) .eq('user_id', _user!.id) .select(); _showSuccessSnackbar('Profil berhasil diperbarui'); } catch (e) { debugPrint('Error updating profile: $e'); _showErrorSnackbar('Error memperbarui profil: ${e.toString()}'); } finally { if (mounted) setState(() => _isLoading = false); } } Future _uploadAvatar() async { final picked = await _picker.pickImage(source: ImageSource.gallery); if (picked == null) return; try { final file = File(picked.path); final fileExt = picked.path.split('.').last; final filePath = 'avatars/${_user!.id}/avatar.$fileExt'; await _supabase.storage.from('avatars').upload( filePath, file, fileOptions: FileOptions( upsert: true, contentType: 'image/$fileExt', ), ); // Get the public URL instead of a signed URL final avatarUrl = _supabase.storage.from('avatars').getPublicUrl(filePath); await _supabase.from('profiles').update({ 'avatar_url': avatarUrl, }).eq('user_id', _user!.id); setState(() { _avatarUrl = avatarUrl; }); _showSuccessSnackbar('Avatar berhasil diunggah'); } catch (e) { debugPrint('Upload error: $e'); _showErrorSnackbar('Gagal mengunggah avatar'); } } void _showErrorSnackbar(String message) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(message), backgroundColor: Colors.red), ); } void _showSuccessSnackbar(String message) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(message), backgroundColor: Colors.green), ); } Future _signOut() async { await _supabase.auth.signOut(); if (mounted) { Navigator.of(context).pushReplacementNamed('/login'); } } @override Widget build(BuildContext context) { if (_user == null) { return _buildNoUserScreen(); } if (_isLoading) { return Scaffold( backgroundColor: const Color(0xFFF8F9FA), body: Center( child: CircularProgressIndicator( valueColor: AlwaysStoppedAnimation(const Color(0xFF056839)), ), ), ); } return Scaffold( backgroundColor: const Color(0xFFF8F9FA), appBar: _buildAppBar(), body: SingleChildScrollView( child: Column( children: [ _buildProfileHeader(), const SizedBox(height: 20), _buildFarmStatsSummary(), const SizedBox(height: 20), _buildProfileForm(), ], ), ), ); } PreferredSizeWidget _buildAppBar() { return AppBar( elevation: 0, backgroundColor: Colors.white, foregroundColor: Colors.black87, centerTitle: false, title: Text( 'Profil Saya', style: GoogleFonts.poppins( fontWeight: FontWeight.bold, color: Colors.black87, ), ), actions: [ IconButton( icon: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Colors.grey[100], shape: BoxShape.circle, ), child: Icon(Icons.logout, size: 18, color: Colors.red[700]), ), onPressed: _signOut, ), const SizedBox(width: 8), ], ); } Widget _buildProfileHeader() { return Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 20), decoration: BoxDecoration( color: Colors.white, boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), offset: const Offset(0, 2), blurRadius: 8, ), ], ), child: Column( children: [ _buildAvatarWithEditButton(), const SizedBox(height: 16), Text( _usernameController.text, style: GoogleFonts.poppins( fontSize: 24, fontWeight: FontWeight.bold, ), textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, ), Text( _emailController.text, style: GoogleFonts.poppins( fontSize: 14, color: Colors.grey[600], ), textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 16), _buildActionButtons(), ], ), ); } Widget _buildAvatarWithEditButton() { return Stack( children: [ Container( padding: const EdgeInsets.all(4), decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all(color: const Color(0xFF056839), width: 2), ), child: CircleAvatar( radius: 60, backgroundColor: Colors.grey[200], backgroundImage: _avatarUrl != null && _avatarUrl!.isNotEmpty ? NetworkImage(_avatarUrl!) : null, child: _avatarUrl == null || _avatarUrl!.isEmpty ? const Icon(Icons.person, size: 60, color: Colors.grey) : null, ), ), Positioned( bottom: 0, right: 0, child: GestureDetector( onTap: _uploadAvatar, child: Container( height: 40, width: 40, decoration: BoxDecoration( color: const Color(0xFF056839), shape: BoxShape.circle, boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: const Icon(Icons.camera_alt, color: Colors.white, size: 20), ), ), ), ], ); } Widget _buildActionButtons() { return SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ // No buttons needed as per user request ], ), ); } Widget _buildFarmStatsSummary() { final currency = NumberFormat.currency(locale: 'id_ID', symbol: 'Rp '); return Container( margin: const EdgeInsets.symmetric(horizontal: 20), padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), spreadRadius: 1, blurRadius: 10, offset: const Offset(0, 4), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Statistik Pertanian', style: GoogleFonts.poppins( fontSize: 18, fontWeight: FontWeight.bold, ), ), Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: const Color(0xFF056839).withOpacity(0.1), borderRadius: BorderRadius.circular(20), ), child: Text( 'Aktif', style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, color: const Color(0xFF056839), ), ), ), ], ), const SizedBox(height: 20), // Performance indicators _buildPerformanceIndicators(), const Divider(height: 32), // Financial summary _buildFinancialSummary(currency), ], ), ); } Widget _buildPerformanceIndicators() { // Calculate percentage for circular indicator based on average yield double yieldPercentage = 0.0; if (_averageYield > 0) { // Assuming optimal yield is 8 ton/ha, calculate percentage yieldPercentage = (_averageYield / 8.0).clamp(0.0, 1.0); } return Row( children: [ Expanded( child: CircularPercentIndicator( radius: 60.0, lineWidth: 10.0, percent: yieldPercentage, center: Column( mainAxisSize: MainAxisSize.min, children: [ Text( '${_averageYield.toStringAsFixed(1)}', style: GoogleFonts.poppins( fontSize: 18, fontWeight: FontWeight.bold, ), ), Text( 'ton/ha', style: GoogleFonts.poppins( fontSize: 12, color: Colors.grey[600], ), ), ], ), progressColor: const Color(0xFF056839), backgroundColor: const Color(0xFF056839).withOpacity(0.2), animation: true, animationDuration: 1200, ), ), const SizedBox(width: 16), Expanded( flex: 2, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildStatRow('Total Lahan', '$_totalFields Lahan'), const SizedBox(height: 8), _buildStatRow('Tanaman Aktif', '$_activeSchedules Jenis'), const SizedBox(height: 8), _buildStatRow('Total Panen', '$_completedHarvests Kali'), const SizedBox(height: 8), _buildStatRow('Tanaman Terbanyak', _mostPlantedCrop), ], ), ), ], ); } Widget _buildFinancialSummary(NumberFormat currency) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 4), child: Column( children: [ IntrinsicHeight( child: Row( children: [ Expanded( child: _buildMetricCard( 'Rata-rata Panen', '${(_averageYield * 10).toStringAsFixed(1)} kilogram/ha', Icons.trending_up, const Color(0xFF056839), 'Rata-rata hasil panen per hektar', ), ), const SizedBox(width: 12), Expanded( child: _buildMetricCard( 'Total Panen', '$_completedHarvests Kali', Icons.check_circle_outline, Colors.blue.shade700, 'Jumlah panen yang telah dilakukan', ), ), ], ), ), const SizedBox(height: 12), IntrinsicHeight( child: Row( children: [ Expanded( child: _buildMetricCard( 'Musim Tanam', '$_activeSchedules Aktif', Icons.calendar_today, Colors.orange.shade700, 'Jumlah tanaman yang sedang ditanam', ), ), const SizedBox(width: 12), Expanded( child: _buildMetricCard( 'Total Lahan', '$_totalFields Lahan', Icons.eco, Colors.green.shade700, 'Jumlah lahan yang dimiliki', ), ), ], ), ), ], ), ); } Widget _buildMetricCard(String title, String value, IconData icon, Color color, String tooltip) { return Container( constraints: const BoxConstraints(minHeight: 100), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(12), border: Border.all(color: color.withOpacity(0.2)), ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 18, color: color), const SizedBox(width: 8), Expanded( child: Text( title, style: GoogleFonts.poppins( fontSize: 13, color: color, fontWeight: FontWeight.w500, ), overflow: TextOverflow.ellipsis, ), ), ], ), const Spacer(), Text( value, style: GoogleFonts.poppins( fontSize: 15, fontWeight: FontWeight.bold, color: Colors.black87, ), overflow: TextOverflow.ellipsis, ), ], ), ); } Widget _buildStatRow(String label, String value) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( flex: 3, child: Text( label, style: GoogleFonts.poppins( fontSize: 14, color: Colors.grey[700], ), ), ), Expanded( flex: 2, child: Text( value, style: GoogleFonts.poppins( fontSize: 14, fontWeight: FontWeight.w600, ), textAlign: TextAlign.end, overflow: TextOverflow.ellipsis, ), ), ], ); } Widget _buildProfileForm() { return Container( margin: const EdgeInsets.symmetric(horizontal: 20), padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), spreadRadius: 1, blurRadius: 10, offset: const Offset(0, 4), ), ], ), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Informasi Pengguna', style: GoogleFonts.poppins( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black87, ), ), const SizedBox(height: 20), _buildInputField( controller: _usernameController, label: 'Nama Pengguna', icon: Icons.person_outlined, validator: (value) => value == null || value.isEmpty ? 'Nama pengguna wajib diisi' : null, ), const SizedBox(height: 16), _buildInputField( controller: _emailController, label: 'Email', icon: Icons.email_outlined, readOnly: true, ), // const SizedBox(height: 16), // _buildInputField( // controller: _farmNameController, // label: 'Nama Lahan', // icon: Icons.agriculture_outlined, // validator: (value) => // value == null || value.isEmpty // ? 'Nama lahan wajib diisi' // : null, // ), const SizedBox(height: 16), _buildInputField( controller: _phoneController, label: 'No. Telepon', icon: Icons.phone_outlined, keyboardType: TextInputType.phone, ), const SizedBox(height: 16), _buildInputField( controller: _addressController, label: 'Alamat', icon: Icons.location_on_outlined, maxLines: 3, ), const SizedBox(height: 30), _buildSaveButton(), ], ), ), ); } Widget _buildSaveButton() { return SizedBox( width: double.infinity, height: 55, child: ElevatedButton( onPressed: _updateProfile, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF056839), foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), elevation: 2, ), child: _isLoading ? const SizedBox( height: 20, width: 20, child: CircularProgressIndicator( strokeWidth: 3, valueColor: AlwaysStoppedAnimation(Colors.white), ), ) : Text( 'Simpan Perubahan', style: GoogleFonts.poppins( fontSize: 16, fontWeight: FontWeight.w600, ), ), ), ); } Widget _buildInputField({ required TextEditingController controller, required String label, required IconData icon, String? Function(String?)? validator, bool readOnly = false, TextInputType? keyboardType, int? maxLines, }) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.only(left: 4, bottom: 8), child: Text( label, style: GoogleFonts.poppins( fontSize: 16, color: Colors.grey[700], fontWeight: FontWeight.w500, ), ), ), Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey[300]!), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.03), blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: TextFormField( controller: controller, decoration: InputDecoration( prefixIcon: Icon(icon, color: const Color(0xFF056839), size: 22), border: InputBorder.none, contentPadding: const EdgeInsets.symmetric(vertical: 16, horizontal: 8), fillColor: readOnly ? Colors.grey[50] : Colors.white, filled: true, ), style: GoogleFonts.poppins(fontSize: 15), readOnly: readOnly, keyboardType: keyboardType, maxLines: maxLines ?? 1, validator: validator, ), ), ], ); } Widget _buildNoUserScreen() { return Scaffold( backgroundColor: const Color(0xFFF8F9FA), body: SafeArea( child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( padding: const EdgeInsets.all(30), decoration: BoxDecoration( color: Colors.grey[200], shape: BoxShape.circle, ), child: Icon( Icons.person_off_outlined, size: 70, color: Colors.grey[400], ), ), const SizedBox(height: 24), Text( 'Silakan masuk untuk melihat profil', style: GoogleFonts.poppins( fontSize: 18, fontWeight: FontWeight.w500, color: Colors.grey[700], ), ), const SizedBox(height: 12), Text( 'Anda perlu masuk ke akun untuk mengakses fitur ini', style: GoogleFonts.poppins( fontSize: 14, color: Colors.grey[600], ), textAlign: TextAlign.center, ), const SizedBox(height: 40), Container( width: 200, height: 55, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: const Color(0xFF056839).withOpacity(0.3), blurRadius: 12, offset: const Offset(0, 4), ), ], ), child: ElevatedButton( onPressed: () => Navigator.of(context).pushReplacementNamed('/login'), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF056839), foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 15), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), elevation: 0, ), child: Text( 'Masuk', style: GoogleFonts.poppins( fontSize: 16, fontWeight: FontWeight.w600, ), ), ), ), ], ), ), ), ); } @override void dispose() { _usernameController.dispose(); _emailController.dispose(); _phoneController.dispose(); _addressController.dispose(); _farmNameController.dispose(); super.dispose(); } }