import 'package:flutter/material.dart'; import 'package:uuid/uuid.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:tugas_akhir_supabase/core/theme/app_colors.dart'; import 'package:tugas_akhir_supabase/screens/calendar/field_model.dart'; // Pastikan path ini benar import 'package:tugas_akhir_supabase/screens/calendar/add_field_bottom_sheet.dart'; // final supabase = Supabase.instance.client; // Sebaiknya akses via Supabase.instance.client di dalam method class FieldManagementScreen extends StatefulWidget { const FieldManagementScreen({super.key}); @override State createState() => _FieldManagementScreenState(); } class _FieldManagementScreenState extends State { final _formKey = GlobalKey(); final _nameController = TextEditingController(); int _plotCount = 1; // Default plot count List _fields = []; Field? _editingField; bool _isLoadingFields = true; bool _isSaving = false; @override void initState() { super.initState(); _loadFields(); } @override void dispose() { // Hapus kode yang mungkin mengganggu keyboard _nameController.dispose(); super.dispose(); } Future _loadFields() async { if (!mounted) return; setState(() { _isLoadingFields = true; }); try { final userId = Supabase.instance.client.auth.currentUser?.id; if (userId == null) { throw Exception('User not authenticated'); } final result = await Supabase.instance.client .from('fields') .select() .eq('user_id', userId) .order('created_at', ascending: false); final fields = (result as List).map((f) => Field.fromMap(f)).toList(); if (mounted) { setState(() { _fields = fields; }); } } catch (e) { debugPrint('Error loading fields: $e'); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Gagal memuat data lahan. Silakan coba lagi.'), backgroundColor: Colors.red, ), ); } } finally { if (mounted) { setState(() { _isLoadingFields = false; }); } } } Future _saveField() async { if (!_formKey.currentState!.validate()) return; // Dismiss keyboard immediately when saving FocusScope.of(context).unfocus(); final userId = Supabase.instance.client.auth.currentUser?.id; if (userId == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('User tidak ditemukan, silakan login ulang.'), backgroundColor: Colors.red, ), ); return; } if (mounted) setState(() => _isSaving = true); try { if (_editingField == null) { // Create await Supabase.instance.client.from('fields').insert({ 'user_id': userId, 'name': _nameController.text, 'plot_count': _plotCount, }); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Lahan berhasil ditambahkan'), backgroundColor: Colors.green, ), ); } } else { // Update await Supabase.instance.client .from('fields') .update({'name': _nameController.text, 'plot_count': _plotCount}) .eq('id', _editingField!.id); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Lahan berhasil diperbarui'), backgroundColor: Colors.green, ), ); } } _resetForm(); await _loadFields(); } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Gagal menyimpan lahan. Silakan coba lagi.'), backgroundColor: Colors.red, ), ); } } finally { if (mounted) setState(() => _isSaving = false); } } Future _deleteField(Field field) async { // Dismiss keyboard when showing a dialog FocusScope.of(context).unfocus(); final confirm = await showDialog( context: context, builder: (_) => AlertDialog( title: const Text('Hapus Lahan'), content: Text( '''Yakin ingin menghapus lahan "${field.name}"? Semua jadwal tanam yang terkait dengan lahan ini akan tetap ada namun mungkin kehilangan referensi lahan. Anda mungkin perlu menangani ini secara manual atau membuat logika pembersihan data terkait.''', ), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('Batal'), ), ElevatedButton( onPressed: () => Navigator.pop(context, true), style: ElevatedButton.styleFrom(backgroundColor: Colors.red), child: const Text('Hapus'), ), ], ), ); if (confirm == true) { if (mounted) setState( () => _isSaving = true, ); // Bisa gunakan _isLoadingFields atau state lain try { await Supabase.instance.client .from('fields') .delete() .eq('id', field.id); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Lahan berhasil dihapus'), backgroundColor: Colors.green, ), ); } await _loadFields(); // Muat ulang daftar lahan } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Gagal menghapus lahan. Silakan coba lagi.'), backgroundColor: Colors.red, ), ); } } finally { if (mounted) setState(() => _isSaving = false); } } } void _resetForm() { // Dismiss keyboard when resetting the form FocusScope.of(context).unfocus(); _formKey.currentState?.reset(); // Ini akan mereset state validator _nameController.clear(); _plotCount = 1; // Reset ke nilai default _editingField = null; if (mounted) { setState( () {}, ); // Update UI untuk membersihkan form dan kembali ke mode "Tambah" } } void _startEdit(Field field) { // Dismiss keyboard when starting an edit FocusScope.of(context).unfocus(); _nameController.text = field.name; _plotCount = field.plotCount; _editingField = field; if (mounted) { setState( () {}, ); // Update UI untuk mengisi form dengan data lahan yang diedit } } @override Widget build(BuildContext context) { final isEditing = _editingField != null; return WillPopScope( onWillPop: () async { // Hapus unfocus yang menyebabkan masalah keyboard // FocusScope.of(context).unfocus(); return true; }, child: GestureDetector( // Hapus unfocus yang menyebabkan masalah keyboard // onTap: () => FocusScope.of(context).unfocus(), child: Scaffold( resizeToAvoidBottomInset: true, // Memastikan UI tidak tertutup keyboard appBar: AppBar( title: Text( 'Manajemen Lahan', style: TextStyle(fontWeight: FontWeight.w600), ), backgroundColor: AppColors.primary, foregroundColor: Colors.white, elevation: 0, actions: [ IconButton( icon: const Icon(Icons.add_circle_outline), tooltip: 'Tambah Lahan Baru', onPressed: () { // Use bottom sheet instead of dialog for adding fields showAddFieldBottomSheet( context: context, onFieldAdded: () { _loadFields(); }, ); }, ), IconButton( icon: const Icon(Icons.refresh), tooltip: 'Refresh Data', onPressed: () { _loadFields(); }, ), ], ), body: SingleChildScrollView( child: Column( children: [ // Form Section Container( padding: const EdgeInsets.fromLTRB(20, 16, 20, 24), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [AppColors.primary, AppColors.secondary], ), borderRadius: const BorderRadius.only( bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20), ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 8, offset: const Offset(0, 3), ), ], ), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize .min, // Agar Column tidak mengambil semua tinggi children: [ Text( isEditing ? 'Edit Detail Lahan' : 'Tambah Lahan Baru', style: const TextStyle( color: Colors.white, fontSize: 18, // Ukuran font disesuaikan fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), // Spasi antar elemen TextFormField( controller: _nameController, decoration: InputDecoration( labelText: 'Nama Lahan', labelStyle: TextStyle( color: Colors.white.withOpacity(0.9), ), hintText: 'Contoh: Lahan Cabai', hintStyle: TextStyle( color: Colors.white.withOpacity(0.7), ), filled: true, fillColor: Colors.white.withOpacity(0.2), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none, ), contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 14, ), prefixIcon: Icon( Icons.agriculture, color: Colors.white.withOpacity(0.9), size: 20, ), ), style: const TextStyle( color: Colors.white, fontSize: 15, ), validator: (val) => val == null || val.isEmpty ? 'Nama lahan wajib diisi' : null, ), const SizedBox(height: 16), Container( // Kontainer untuk baris jumlah plot padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 4, ), decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), borderRadius: BorderRadius.circular(12), ), child: Row( children: [ Icon( Icons.grid_view_rounded, color: Colors.white.withOpacity(0.9), size: 20, ), const SizedBox(width: 12), const Text( 'Jumlah Plot:', style: TextStyle( color: Colors.white, fontSize: 15, ), ), const Spacer(), // Untuk mendorong dropdown ke kanan DropdownButton( value: _plotCount, dropdownColor: AppColors.primary, // Warna dropdown icon: const Icon( Icons.arrow_drop_down, color: Colors.white, ), underline: const SizedBox(), // Hilangkan garis bawah default style: const TextStyle( color: Colors.white, fontSize: 15, // Ukuran font disesuaikan fontWeight: FontWeight.w500, ), items: List.generate(20, (i) => i + 1) .map( (n) => DropdownMenuItem( value: n, child: Text(n.toString()), ), ) .toList(), onChanged: _isSaving ? null : (val) => setState(() => _plotCount = val!), ), ], ), ), const SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ if (isEditing) TextButton.icon( onPressed: _isSaving ? null : _resetForm, icon: const Icon( Icons.cancel_outlined, size: 18, ), // Icon disesuaikan label: const Text('Batal Edit'), style: TextButton.styleFrom( foregroundColor: Colors.white, backgroundColor: Colors.black.withOpacity( 0.25, ), padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 10, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), ), if (isEditing) const SizedBox(width: 10), ElevatedButton.icon( onPressed: _isSaving ? null : _saveField, icon: _isSaving ? SizedBox( width: 18, height: 18, child: CircularProgressIndicator( strokeWidth: 2, color: AppColors.primary, ), ) : Icon( isEditing ? Icons.save_alt_outlined : Icons.add_circle_outline_rounded, size: 18, ), // Icon disesuaikan label: Text( isEditing ? 'Simpan Perubahan' : 'Tambah Lahan', style: TextStyle(fontSize: 14), ), style: ElevatedButton.styleFrom( backgroundColor: Colors.white, foregroundColor: AppColors.primary, padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 10, ), elevation: 2, shadowColor: Colors.black.withOpacity(0.3), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), ), ], ), ], ), ), ), // List Title Section Padding( padding: const EdgeInsets.fromLTRB( 20, 20, 20, 10, ), // Padding disesuaikan child: Row( children: [ Icon( Icons.list_alt_rounded, color: AppColors.primary, size: 22, // Ukuran icon disesuaikan ), const SizedBox(width: 8), Text( 'Daftar Lahan Tersimpan', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 17, // Ukuran font disesuaikan color: AppColors.primary, ), ), const SizedBox(width: 6), Text( '(${_fields.length})', style: TextStyle( color: Colors.grey.shade600, fontSize: 15, // Ukuran font disesuaikan ), ), ], ), ), // List Section _isLoadingFields ? Center( child: CircularProgressIndicator( color: AppColors.primary, ), ) : _fields.isEmpty ? Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.landscape_outlined, // Icon disesuaikan size: 60, // Ukuran icon disesuaikan color: Colors.grey.shade400, ), const SizedBox(height: 16), Text( 'Belum ada lahan tersimpan', style: TextStyle( color: Colors.grey.shade600, fontSize: 16, ), ), const SizedBox(height: 8), Text( 'Tambahkan lahan baru pada form di atas.', style: TextStyle( color: Colors.grey.shade500, fontSize: 14, ), textAlign: TextAlign.center, ), ], ), ) : ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: _fields.length, padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 8, ), itemBuilder: (context, index) { final field = _fields[index]; return Card( elevation: 1.5, // Elevasi disesuaikan margin: const EdgeInsets.only(bottom: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular( 10, ), // Border radius disesuaikan side: BorderSide( color: Colors .grey .shade300, // Warna border disesuaikan width: 0.8, ), ), child: ListTile( // Padding diatur oleh ListTile contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 10, ), leading: Container( // Icon Lahan width: 42, height: 42, decoration: BoxDecoration( color: Colors.green.shade50, borderRadius: BorderRadius.circular(8), ), child: Icon( Icons.eco_outlined, // Icon disesuaikan color: Colors .green .shade700, // Warna icon disesuaikan size: 22, ), ), title: Text( field.name, style: const TextStyle( fontWeight: FontWeight.w600, fontSize: 15.5, // Ukuran font disesuaikan ), ), subtitle: Padding( padding: const EdgeInsets.only( top: 5.0, ), // Padding disesuaikan child: Text( '${field.plotCount} Plot', style: TextStyle( color: Colors .grey .shade700, // Warna teks disesuaikan fontSize: 13, // Ukuran font disesuaikan ), ), ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( icon: Icon( Icons .edit_note_outlined, // Icon disesuaikan color: Colors .orange .shade700, // Warna icon disesuaikan size: 22, // Ukuran icon disesuaikan ), tooltip: 'Edit Lahan', style: IconButton.styleFrom( backgroundColor: Colors.orange.withOpacity( 0.1, ), // Background disesuaikan padding: const EdgeInsets.all(8), ), onPressed: _isSaving ? null : () => _startEdit(field), ), const SizedBox(width: 6), // Spasi antar tombol IconButton( icon: Icon( Icons .delete_outline_rounded, // Icon disesuaikan color: Colors .red .shade600, // Warna icon disesuaikan size: 22, // Ukuran icon disesuaikan ), tooltip: 'Hapus Lahan', style: IconButton.styleFrom( backgroundColor: Colors.red.withOpacity( 0.1, ), // Background disesuaikan padding: const EdgeInsets.all(8), ), onPressed: _isSaving ? null : () => _deleteField(field), ), ], ), ), ); }, ), ], ), ), floatingActionButton: FloatingActionButton( backgroundColor: AppColors.primary, foregroundColor: Colors.white, onPressed: () { showAddFieldBottomSheet( context: context, onFieldAdded: () { _loadFields(); }, ); }, tooltip: 'Tambah Lahan', child: const Icon(Icons.add), ), ), ), ); } }